一、实现效果
二、实现过程
先看一眼HTML结构
<div class="top-bar"><span>得分:</span><span id="score">0</span></div>
<div class="container">
<div class="gem-container">
<div class="gem" id="gem0">
<img src="./img/blueGem.png" alt="蓝宝石" />
</div>
<div class="gem" id="gem1">
<img src="./img/purpleGem.png" alt="紫宝石" />
</div>
<div class="gem" id="gem2">
<img src="./img/blueGem.png" alt="蓝宝石" />
</div>
<div class="gem" id="gem3">
<img src="./img/purpleGem.png" alt="紫宝石" />
</div>
<div class="gem" id="gem4">
<img src="./img/blueGem.png" alt="蓝宝石" />
</div>
</div>
<div class="car" id="car">
<img src="./img/car.png" alt="矿车" />
</div>
</div>
接下来是过程:
1.小车移动
先获取小车当前的left,使用split取到数字
全局监听键盘左右键,keyCode分别是37和39,同时判断在边框时不再生效移动
因为上面使用spilit()的原因,取到的小车left是字符串,使用eval()将其视为数字计算
计算后更改小车的left
document.onkeydown = function (e) {
left = car.style.left.split("px")[0];
if (e.keyCode == 37 && car.style.left != "0px") { // 监听方向键左且不在边框最左侧
left = eval(left) - 20;
car.style.left = left + "px";
}
if (e.keyCode == 39 && car.style.left != "400px") { // 监听方向键右且不在边框最右侧
left = eval(left) + 20;
car.style.left = left + "px";
}
};
2.随机出现宝石
HTML共定义5个宝石,命名分别是gem0~4,因此使用Math方法随机获取0~4整数作为索引
以此获取到宝石后,做判断:该索引的宝石是否存在,若存在则再次随机(以此避免重复使同一个宝石出现)
然后对出现的宝石触发下落
最后对上述过程做一个循环器,每隔4秒执行
// 随机指定宝石出现
function randomGem() {
let index = Math.floor(Math.random() * 5);
let gemId = "gem" + index;
let gemEl = document.getElementById(gemId);
return gemEl;
}
// 宝石出现循环器
setInterval(function () {
let gemEl = randomGem(); // 获取随机的宝石
for (let i = 0; gemEl.style.display == "block"; i++) { // 遍历宝石以获取未出现的宝石
gemEl = randomGem();
}
gemEl.style.display = "block";
gemDown(gemEl);
}, 4000);
3.宝石下落
这个比较简单,获取上文函数中的gemEl(宝石元素),初始化它的top为0px(否则会为空值导致报错)
设定循环器:获取宝石的top,然后对其增加并赋给宝石,这里要实现流程的下落,尽可能将循环器定义的循环速度高一些,我这里采用100毫秒
// 宝石下落
function gemDown(gemEl) {
gemEl.style.top = "0px"; // 初始化gem的top
const gemDown = setInterval(function () { // 下落循环器
let gemTop = gemEl.style.top.split("px")[0];
gemTop = eval(gemTop) + 10;
gemEl.style.top = gemTop + "px";
}, 100);
}
4.判断是否接取宝石
难点主要集中在这里了
不着急一步步来:
(1)先算出宝石到达小车时的top是多少,作为判定线
if (gemEl.style.top == "720px") {
}
(2) 到达判定线时,判断是否接住宝石
概念大概如下图,主要计算最左侧接取和最右侧接取,宝石接取的范围即是: 最左侧的值~最右侧的值
接下来计算:
分别获取小车和宝石的left
当宝石在最左侧:宝石left + 30 (自身宽度) - 10 (避免造成擦边接取以减少接取范围)
当宝石在最右侧:宝石left + 10 (原理同上),但此时的小车最右侧的left应该等于当前left+自身宽度,即:小车left + 100 (自身宽度)
作为代码就是:eval(gemLeft) + 20 >= carLeft && gemLeft <= eval(carLeft) + 90
// 判定是否接住宝石
function isCatch(gemEl) {
let gemLeft = gemEl.style.left.split("px")[0];
let carLeft = car.style.left.split("px")[0];
if (
eval(gemLeft) + 20 >= carLeft && // 最左侧判定
gemLeft <= eval(carLeft) + 90 // 最右侧判定
)
return true;
else return false;
}
结合第(1)步就是
// 宝石到达判定线
if (gemEl.style.top == "720px") {
// 判断是否接住宝石
if (isCatch(gemEl)) {
}
}
(3)实现当移动小车使用小车左右侧碰撞宝石仍可以接取宝石
实现这个有个前提就是:一直在做判断是否接取,那就定义一个循环器呗
连起来就是,宝石到达判定线开始,每0.1秒做判断是否接取了宝石
// 宝石到达判定线
if (gemEl.style.top == "720px") {
const chkCatch = setInterval(function () { // 在宝石掉出边框前循环判断是否接住(小车左右侧接触宝石也可以接取)
// 判断是否接住宝石
if (isCatch(gemEl)) {
clearInterval(gemDown);
clearInterval(chkCatch); // 清理下落和判定接取的循环器
gemEl.style.display = "none";
score.innerText = ++scoreNum; // 接住加分
}
}, 100);
}
(4)判断宝石是否掉到边框外
很简单不赘述,留意要放在循环器中
// 直到掉出边框未接住
if (gemEl.style.top == "810px") {
clearInterval(gemDown);
clearInterval(chkCatch);
gemEl.style.display = "none";
score.innerText = --scoreNum; // 未接住扣分
}
三、源码
最后源码奉上,我已经把img替换,这样直接就能跑起来了
<!DOCTYPE html>
<html lang="zh-CN">
<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>
.top-bar {
width: 500px;
margin: 0 auto;
}
.container {
width: 500px;
height: 800px;
margin: 0 auto;
border: 2px black solid;
background-color: burlywood;
}
.gem-container {
position: relative;
}
.gem {
width: 30px;
height: 30px;
float: left;
line-height: 30px;
text-align: center;
font-size: 900;
color: red;
position: absolute;
top: 0;
left: 0;
display: inline;
background-color: yellow;
border-radius: 15px;
}
.car {
position: relative;
top: 750px;
left: 0px;
width: 100px;
height: 50px;
background-color: gray;
}
</style>
</head>
<body>
<div class="top-bar"><span>得分:</span><span id="score">0</span></div>
<div class="container">
<div class="gem-container">
<div class="gem" id="gem0">$</div>
<div class="gem" id="gem1">$</div>
<div class="gem" id="gem2">$</div>
<div class="gem" id="gem3">$</div>
<div class="gem" id="gem4">$</div>
</div>
<div class="car" id="car"></div>
</div>
<script>
const gem = document.getElementsByClassName("gem"); // 宝石
const car = document.getElementById("car"); // 小车
const score = document.getElementById("score"); //得分
let scoreNum = 0; // 得分数字
let left = 0; // 小车的left数字
car.style.left = "0px"; // 初始化小车的left
// 初始隐藏宝石,并给宝石left赋值
let gemLeft = 0;
for (let i of gem) {
i.style.display = "none";
gemLeft += 80;
i.style.left = gemLeft + "px";
}
// 监听键盘按键以控制小车移动
document.onkeydown = function (e) {
left = car.style.left.split("px")[0];
if (e.keyCode == 37 && car.style.left != "0px") { // 监听方向键左且不在边框最左侧
left = eval(left) - 20;
car.style.left = left + "px";
}
if (e.keyCode == 39 && car.style.left != "400px") { // 监听方向键右且不在边框最右侧
left = eval(left) + 20;
car.style.left = left + "px";
}
};
// 随机指定宝石出现
function randomGem() {
let index = Math.floor(Math.random() * 5);
let gemId = "gem" + index;
let gemEl = document.getElementById(gemId);
return gemEl;
}
// 宝石出现循环器
setInterval(function () {
let gemEl = randomGem(); // 获取随机的宝石
for (let i = 0; gemEl.style.display == "block"; i++) { // 遍历宝石以获取未出现的宝石
gemEl = randomGem();
}
gemEl.style.display = "block";
gemDown(gemEl);
}, 4000);
// 宝石下落
function gemDown(gemEl) {
gemEl.style.top = "0px"; // 初始化gem的top
const gemDown = setInterval(function () { // 下落循环器
let gemTop = gemEl.style.top.split("px")[0];
gemTop = eval(gemTop) + 10;
gemEl.style.top = gemTop + "px";
// 宝石到达判定线
if (gemEl.style.top == "720px") {
const chkCatch = setInterval(function () { // 在宝石掉出边框前循环判断是否接住(小车左右侧接触宝石也可以接取)
// 判断是否接住宝石
if (isCatch(gemEl)) {
clearInterval(gemDown);
clearInterval(chkCatch); // 清理下落和判定接取的循环器
gemEl.style.display = "none";
score.innerText = ++scoreNum; // 接住加分
}
// 直到掉出边框未接住
if (gemEl.style.top == "810px") {
clearInterval(gemDown);
clearInterval(chkCatch);
gemEl.style.display = "none";
score.innerText = --scoreNum; // 未接住扣分
}
}, 100);
}
}, 100);
}
// 判定是否接住宝石
function isCatch(gemEl) {
let gemLeft = gemEl.style.left.split("px")[0];
let carLeft = car.style.left.split("px")[0];
if (
eval(gemLeft) + 20 >= carLeft && // 最左侧判定
gemLeft <= eval(carLeft) + 90 // 最右侧判定
)
return true;
else return false;
}
</script>
</body>
</html>