框架
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>钟摆游戏</title>
<style>
* {
box-sizing: border-box;
margin: 0;
}
/* game盒子 采用 相对布局 ,便于后续添加 绝对定位的item*/
.game {
position: relative;
height: 500px;
width: 500px;
}
/* 分数 绝对布局,在 正中间 */
.score {
position: absolute;
left: 250px;
top: 250px;
transform: translate(-50%, -50%);
}
</style>
</head>
<body>
<div class="score">分数:<label id="val">0</label></div>
<div class="game"></div>
</body>
</html>
整个游戏我们都放在500 * 500 的区域中进行。 所以我们使用了 box-sizing属性,具体用法可见 盒子模型中box-sizing: border-box;的作用_Eric加油学!的博客-CSDN博客
得分圈和钟摆
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>钟摆游戏</title>
<style>
* {
box-sizing: border-box;
margin: 0;
}
/* game盒子 采用 相对布局 ,便于后续添加 绝对定位的item*/
.game {
position: relative;
height: 500px;
width: 500px;
}
.item {
display: flex;
justify-content: center;
align-items: center;
background-color: cadetblue;
border-radius: 100%;
color: #fff;
width: 85px;
height: 85px;
border: 1px solid rgb(55, 101, 103);
position: absolute;
left: 50%;
top: 50%;
margin: -40px 0 0 -40px;
}
.item-name {
position: relative;
z-index: 1;
}
/* 摆锤动画 */
.tool {
animation: run 2s linear infinite;
}
@keyframes run {
0% {
transform:rotate(0deg);
}
100% {
transform:rotate(360deg);
}
}
.paused {
animation-play-state: paused;
}
.tool,.tool-circle {
position: absolute;
width: 60px;
height: 60px;
left: 50%;
top: 50%;
margin: -30px 0 0 -30px;
background-color: rgb(117, 68, 68);
border-radius: 100%;
}
.tool:after {
content: '';
position: absolute;
height: 100px;
width: 10px;
background-color: rgb(117, 68, 68);
left: 50%;
margin-left: -5px;
bottom: 30px;
}
.tool-circle {
top: -78px;
display: flex;
justify-content: center;
align-items: center;
}
/* 分数 绝对布局,在 正中间 */
.score {
position: absolute;
left: 250px;
top: 250px;
transform: translate(-50%, -50%);
}
</style>
</head>
<body>
<div class="score">分数:<label id="val">0</label></div>
<div class="game"></div>
</body>
<script>
const game = document.querySelector('.game')
const score = document.querySelector('#val')
// 定义一圈设置9个得分点
const max = 9
let index = 0
const data = Array.from(new Array(max)).map((v, i) => {
// 均分角度
const deg = i * 360 / max;
return `<div class="item" style="transform: rotate(${deg}deg) translate(160px);z-index:${i === index ? '10' : ''}">
<span class="item-name"></span>
${i === index ? `<div class="tool">
<div class="tool-circle"><span id="curr"></span></div>
</div>`:''}
</div>`
})
game.innerHTML = data.join('')
</script>
</html>
首先获取到相应的元素,并设置一共9个得分圈,从正右边开始(0号),index从0起。
const data = Array.from(new Array(max)).map((v, i) => {
// 均分角度
const deg = i * 360 / max;
return `<div class="item" style="transform: rotate(${deg}deg) translate(160px);z-index:${i === index ? '10' : ''}">
<span class="item-name"></span>
${i === index ? `<div class="tool">
<div class="tool-circle"><span id="curr"></span></div>
</div>`:''}
</div>`
})
game.innerHTML = data.join('')
这段代码,就是利用JS添加这9个item得分圈。 并计算旋转角度,设置平移距离160px;
然后如果当前索引上有钟摆,我们添加钟摆,并为其设置动画。
tool 就是 得分圈上的钟摆座, tool-circle就是正在旋转的钟摆,我们利用 .tool:after 为其添加中间的杆 tool-name和curr分别是得分圈和钟摆的中心点
这里其实是用拼接innerHTML属性的字符串,各个字符串追加game里面,最后调用数组的join方法生成目标字符串 。 这种方法其实效率更高,但是可读性并不是很强。
也有最简单的方法,就是将元素先写好,然后再设置属性
<div class="game">
<div class="item">1</div>
<div class="item">2</div>
<div class="item">3</div>
<div class="item">4</div>
<div class="item">5</div>
<div class="item">6</div>
<div class="item">7</div>
<div class="item">8</div>
<div class="item">9</div>
</div>
const game = document.querySelector('.game')
const item = game.getElementsByClassName("item")
const score = document.querySelector('#val')
// 定义一圈设置9个得分点
const max = 9
let index = 0
const data = Array.from(new Array(max)).map((v, i) => {
// 均分角度
const deg = i * 360 / max;
item[i].style.webkitTransform = "rotate(" + deg + "deg)" + " translate(160px)";
})
这里用JS设置transform属性,常用的
setAttribute
不起作用,可以使用:element.style.webkitTransform = "40deg"
这种方法,看上去很清楚明了,但是效率最慢
动态得分规则
当我们点击屏幕的时候,暂停动画,计算 当前摆锤的中心点位置 和 目标得分圈的中心点位置,由二者差值得出 是否成功得分。如果得分,将摆锤底座 添加至 下一个得分圈,重新开始动画,否则游戏结束。
document.onclick = () => {
const tool = game.querySelector('.tool')
// 动画暂停
tool.classList.add('paused')
// 计算下一个点
const nextIndex = index === max - 1 ? 0 : index + 1
const list = game.children
// 当前摆锤的位置,获得其中心点位置
const currRect = document.getElementById('curr').getBoundingClientRect()
// 找到下一个得分圈的中心点
const moveEl = list[nextIndex].querySelector('.item-name')
// 下一个得分圈中心点位置信息
const moveRect = moveEl.getBoundingClientRect()
// 计算两者之间差值 (得分圈和钟摆大小差值为25)
if(Math.abs(currRect.left - moveRect.left) < 15 && Math.abs(currRect.top - moveRect.top) < 15){
// 摆锤 停的位置 是正确的
// 将摆锤底座 添加到 下一个得分圈上
list[nextIndex].appendChild(tool)
// 移除 暂停状态
tool.classList.remove('paused')
// 清空当前状态的index,并将下一个得分圈的index设为10
list[index].style.zIndex = ''
list[nextIndex].style.zIndex = '10'
if (index === max - 1) {
index = 0
} else {
index++
}
// 得分+1
score.innerHTML = parseInt(score.innerHTML) + 1
} else if (score.innerHTML.indexOf(',') === - 1) {
score.innerHTML += ',游戏结束'
}
}
调整游戏难度
如果想要调整游戏的难度,可以 设置 钟摆旋转动画的快慢,或者判定得分的差值大小。
完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>钟摆游戏</title>
<style>
* {
box-sizing: border-box;
margin: 0;
}
/* game盒子 采用 相对布局 ,便于后续添加 绝对定位的item*/
.game {
position: relative;
height: 500px;
width: 500px;
}
.item {
display: flex;
justify-content: center;
align-items: center;
background-color: cadetblue;
border-radius: 100%;
color: #fff;
width: 85px;
height: 85px;
border: 1px solid rgb(55, 101, 103);
position: absolute;
left: 50%;
top: 50%;
margin: -40px 0 0 -40px;
}
.item-name {
position: relative;
z-index: 1;
}
/* 摆锤动画 */
.tool {
animation: run 3s linear infinite;
}
@keyframes run {
0% {
transform:rotate(0deg);
}
100% {
transform:rotate(360deg);
}
}
.paused {
animation-play-state: paused;
}
.tool,.tool-circle {
position: absolute;
width: 60px;
height: 60px;
left: 50%;
top: 50%;
margin: -30px 0 0 -30px;
background-color: rgb(117, 68, 68);
border-radius: 100%;
}
.tool:after {
content: '';
position: absolute;
height: 100px;
width: 10px;
background-color: rgb(117, 68, 68);
left: 50%;
margin-left: -5px;
bottom: 30px;
}
.tool-circle {
top: -78px;
display: flex;
justify-content: center;
align-items: center;
}
/* 分数 绝对布局,在 正中间 */
.score {
position: absolute;
left: 250px;
top: 250px;
transform: translate(-50%, -50%);
}
</style>
</head>
<body>
<div class="score">分数:<label id="val">0</label></div>
<div class="game"></div>
</body>
<script>
const game = document.querySelector('.game')
const score = document.querySelector('#val')
// 定义一圈设置9个得分点
const max = 9
let index = 0
const data = Array.from(new Array(max)).map((v, i) => {
// 均分角度
const deg = i * 360 / max;
return `<div class="item" style="transform: rotate(${deg}deg) translate(160px);z-index:${i === index ? '10' : ''}">
<span class="item-name"></span>
${i === index ? `<div class="tool">
<div class="tool-circle"><span id="curr"></span></div>
</div>`:''}
</div>`
})
game.innerHTML = data.join('')
document.onclick = () => {
const tool = game.querySelector('.tool')
// 动画暂停
tool.classList.add('paused')
// 计算下一个点
const nextIndex = index === max - 1 ? 0 : index + 1
const list = game.children
// 当前摆锤的位置,获得其中心点位置
const currRect = document.getElementById('curr').getBoundingClientRect()
// 找到下一个得分圈的中心点
const moveEl = list[nextIndex].querySelector('.item-name')
// 下一个得分圈中心点位置信息
const moveRect = moveEl.getBoundingClientRect()
// 计算两者之间差值 (得分圈和钟摆大小差值)
if(Math.abs(currRect.left - moveRect.left) < 15 && Math.abs(currRect.top - moveRect.top) < 15){
// 摆锤 停的位置 是正确的
// 将摆锤底座 添加到 下一个得分圈上
list[nextIndex].appendChild(tool)
// 移除 暂停状态
tool.classList.remove('paused')
// 清空当前状态的index,并将下一个得分圈的index设为10
list[index].style.zIndex = ''
list[nextIndex].style.zIndex = '10'
if (index === max - 1) {
index = 0
} else {
index++
}
// 得分+1
score.innerHTML = parseInt(score.innerHTML) + 1
} else if (score.innerHTML.indexOf(',') === - 1) {
score.innerHTML += ',游戏结束'
}
}
</script>
</html>
参考:js实现钟摆小游戏_哔哩哔哩_bilibili