上下左右
控制 蛇 的移动canvas
: 相当于画布;行内元素,让方格在网页水平居中对齐先转块级( display: block
); 宽高设置用行内样式。
fillRect(x,y,width,height)
setInterval
: 定时器,蛇 移动的原理就是,利用每一张不同位置的蛇的位置图片,一直覆盖掉(并擦除掉上一次的图)上一张的图,通过定时器 1000/3 (1s执行3次这样的操作),让视觉效果看起来蛇平滑的移动。
addEventListener
: 事件监听(keydown)键盘按下监听,利用 e.keyCode 获取 上下左右
对应的键值(上: 38 下: 40 左:37 右:39)
pop()
: pop() 删除并返回数组的最后一个元素
unshift()
: 向数组的开头添加一个或多个元素,并返回新的长度
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>贪吃蛇绘制</title>
<!-- 设置一个背景色、 -->
<style>
canvas {
/* 在这里要居中的时候,要先转成块级元素 */
display: block;
margin: 0 auto;
background-color: #33cc99;
}
</style>
</head>
<body>
<!-- 准备画布 -->
<canvas width="500" height="500" id="huabu">
</canvas>
</body>
<script>
// 通过 js 代码来绘制网格
// 获取画布对象
let huabu = document.getElementById('huabu');
// 获取绘制工具箱
let tools = huabu.getContext('2d');
// 从画图工具箱取出要使用的工具;
// 随机生成x 和 y轴的坐标
let x = Math.floor(Math.random() * 20) * 25;
let y = Math.floor(Math.random() * 20) * 25;
let isEated = false;
// 每次移动的距离为一格(默认就是在这个位置出发)
let snake = [{
x: 3,
y: 0
}, {
x: 2,
y: 0
}, {
x: 1,
y: 0
}]
let directionX = 1;
let directionY = 0;
// 判断游戏是否结束
let isGameOver = false;
document.addEventListener('keydown', (e) => {
let k = event.keyCode;
// 上: 38 下: 40 左:37 右:39
if (k === 38) {
directionX = 0
directionY = -1
} else if (k === 40) {
directionX = 0
directionY = 1
} else if (k === 37) {
directionX = -1
directionY = 0
} else if (k === 39) {
directionX = 1
directionY = 0
}
})
// 利用定时器,让贪吃蛇动起来
setInterval(() => {
if (isGameOver) {
return
}
// 只需要将擦除 -> 重绘 的代码放里面即可(记得每次都先擦除画布再重绘一次)
tools.clearRect(0, 0, 500, 500);
// 注意:食物有默认的行为,刚开始是不动 的,被蛇吃后才会动
// -------------绘制食物开始------------
// 这段代码的意思是: 只有食物被吃掉了,才会生成新的食物
if (isEated) {
// 在这里是对 x 和 y 进行重新复制,不用加 var 声明
x = Math.floor(Math.random() * 20) * 25;
y = Math.floor(Math.random() * 20) * 25;
}
// 设置矩形的颜色
tools.fillStyle = '#fff000';
// 画一个矩形
// fillRect(x,y,width,height)
// 不能随机生成 x,y
// 随机生成一个位置 Math.random() 生成一个随机数 * 25就得到一个范围
// 问题: 如果根据 Math.random() 得到一个 [0,19] 并且 0 和 19 都能取到的值?
// 解决方案: Math.random() * 20 => [0,1) * 20 => [0,20)
// Math.floor() 向下取整
tools.fillRect(x, y, 25, 25);
// -------------绘制食物结束------------
// ----------绘制贪吃蛇开始---------
// 说明: 贪吃蛇随着 擦除 重绘,会动起来
// 蛇已经动起来,默认向右: x + 1,y 不变
// 水平向左: x -1y 不变
// 垂直向下:y +1 x 不变
// 垂直向上: y -1 x 不变
// 新添加的节,是上一次的蛇头位置再加1,就得到了
let oldHead = snake[0];
let newHead = {
x: oldHead.x + directionX,
y: oldHead.y + directionY
}
// 在这里做一个边界的判定
console.log(newHead.x * 30);
// 1.如果蛇头的 y 坐标比0小,说明游戏结束
if (newHead.y < 0 || newHead.x < 0 || newHead.x * 30 >= 600 || newHead.y * 30 >= 600) {
// 游戏就结束了
isGameOver = true;
} else {
snake.unshift(newHead); //向数组的开头添加一个或多个元素,并返回新的长度
// 蛇吃食物分析:
// 当蛇头的坐标 和 食物的坐标重合的时候,就表示食物被吃掉了
// 此时应该做两件事
// 1. 让 isEated 变成 true,表示食物被吃掉
// 2. 让 蛇 的身体增加一节
if (snake[0].x * 25 === x && snake[0].y * 25 === y) {
// 当蛇头的 x 和 Y 坐标都与 食物的 x和y坐标 重合,才证明食物被吃了
isEated = true;
// 如果蛇吃到了食物,就不执行 pop() 删除最后一节
} else {
// 在这里重新把 isEated 设置为 false
isEated = false; // 重新设置食物没被吃掉
// 表示蛇没吃到食物
snake.pop(); //
}
}
// ----------绘制贪吃蛇结束---------
// 默认移动方向为水平向右
// 绘制蛇头的颜色,每一届都是一个矩形
for (var i = 0; i < snake.length; i++) {
if (i === 0) {
// 只有蛇头是紫色的,0 的时候
tools.fillStyle = 'purple'
} else {
tools.fillStyle = 'blue'
}
tools.fillRect(snake[i].x * 25, snake[i].y * 25, 25, 25);
}
// -----------绘制网格开始----------
// 找位置
// 起点坐标
// 规律: (0,25 * N + 0.5)(500, 25 * N + 0.5)
for (let i = 1; i < 20; i++) {
tools.moveTo(0, 25 * i + 0.5);
// 终点坐标
tools.lineTo(500, 25 * i + 0.5);
tools.moveTo(25 * i + 0.5, 0);
// 终点坐标
tools.lineTo(25 * i + 0.5, 500);
}
// 设置绘制的画笔颜色
tools.strokeStyle = 'white';
// 绘制
tools.stroke();
// -----------绘制网格结束----------
}, 1000 / 3); //1000/3 为 1秒执行 3 次操作
</script>
</html>
4.同源码(无注释)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>贪吃蛇绘制二次代码编辑</title>
<style>
canvas {
display: block;
margin: 0 auto;
background-color: #33cc99;
}
</style>
</head>
<body>
<canvas width="500" height="500" id="huabu"></canvas>
</body>
<script>
let huabu = document.getElementById('huabu');
let tol = huabu.getContext('2d');
let x = Math.floor(Math.random() * 20) * 25;
let y = Math.floor(Math.random() * 20) * 25;
let isEated = false;
let snake = [{
x: 3,
y: 0
}, {
x: 2,
y: 0
}, {
x: 1,
y: 0
}];
let directionX = 1;
let directionY = 0;
let isGameOver = false;
document.addEventListener('keydown', (e) => {
let k = e.keyCode;
if (k === 38) {
directionX = 0;
directionY = -1
} else if (k === 40) {
directionX = 0;
directionY = 1
} else if (k === 37) {
directionX = -1;
directionY = 0
} else if (k === 39) {
directionX = 1;
directionY = 0
}
});
setInterval(() => {
if (isGameOver) {
return
}
tol.clearRect(0, 0, 500, 500);
if (isEated) {
x = Math.floor(Math.random() * 20) * 25;
y = Math.floor(Math.random() * 20) * 25;
}
tol.fillStyle = '#fff000';
tol.fillRect(x, y, 25, 25);
let oldHead = snake[0];
let newHead = {
x: oldHead.x + directionX,
y: oldHead.y + directionY
}
if (newHead.y < 0 || newHead.x < 0 || newHead.x * 30 >= 600 || newHead.y * 30 >= 600) {
isGameOver = true;
} else {
snake.unshift(newHead);
if (snake[0].x * 25 === x && snake[0].y * 25 === y) {
isEated = true;
} else {
isEated = false;
snake.pop();
}
}
for (var i = 0; i < snake.length; i++) {
if (i === 0) {
tol.fillStyle = 'purple';
} else {
tol.fillStyle = 'blue';
}
tol.fillRect(snake[i].x * 25, snake[i].y * 25, 25, 25);
}
for (let i = 1; i < 20; i++) {
tol.moveTo(0, 25 * i + 0.5);
tol.lineTo(500, 25 * i + 0.5);
tol.moveTo(25 * i + 0.5, 0);
tol.lineTo(25 * i + 0.5, 500);
}
tol.strokeStyle = 'white';
tol.stroke();
}, 300)
</script>
</html>
后续补全其他功能更,琢磨琢磨