玩游戏学前端-贪吃蛇-使用DOM画蛇

使用DOM画蛇


上一篇中我们将整个游戏划分了模块,主要有main.js, snake.js, food.js三个模块,然后在main模块中使用了window.requestAnimationFrame()请求动画帧函数, 通过这个函数不断重复调用update()和draw()函数,这两个函数用于更新界面和重绘游戏中的蛇和食物, 到现在为止并没有实现这两个函数的具体功能,在这一篇中我们将使用JavaScript DOM技术把蛇画出来。

JavaScript DOM


DOM 全称文档对象模型,在前端是将WEB页面和JavaScript脚本语言连接起来,提供了一套API(应用程序编程接口)用于操作HTML元素、处理事件等,在贪吃蛇游戏中我们需要用到几个DOM API来创建蛇和食物:

怎样获取HTML元素?

1
2
document.getElementById();
document.querySelector();

怎样创建HTML元素?

1
document.createElement()

怎样修改元素的样式属性?

1
2
3
元素.style = ...
元素.className = ...
元素.classList.add(...)

怎样添加元素?

1
元素.appendChild(其它元素)

JavaScript中如何定义数组?

1
const snakeBody = [ 数组内容 ]

绘制蛇身体和头部


HTML代码就不重复了,先在main.js文件中获取网页中id为game-board的元素, 然后再绘制蛇或食物的时候需要传递这个HTML元素给drawSnake方法, 关键代码如下:

获取HTML元素的关键代码

1
2
3
let gameBoard = document.getElementById('game-board')
//或者
let gameBoard = document.querySelector('#game-board')

调用绘制蛇和食物的关键代码

1
2
drawSnake(gameBoard)
drawFood(gameBoard)

完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//引入 snake.js 和 food.js模块内容 
import {update as updateSnake, draw as drawSnake} from './snake.js'
import {update as updateFood, draw as drawFood} from './food.js'

/* 通过getElementById获取元素 */
let gameBoard = document.getElementById('game-board')

function main(currentTime) {
//再次请求动画帧
window.requestAnimationFrame(main)
//调用本模块中的update和draw函数
update()
draw()
}
//请求动画帧
window.requestAnimationFrame(main)

//因为有蛇和食物需要更新,所以定义了update函数,在函数内部调用另外两个模块的函数
function update() {
updateSnake()
updateFood()
}
//因为有蛇和食物需要绘制,所以定义了draw函数,在函数内部调用另外两个模块的函数
function draw() {
//画蛇 在game.js模块中drawSnake和drawFood 是一个别名
drawSnake(gameBoard)
//画食物
drawFood(gameBoard)
}

下面将会在snake.js模块中实现draw方法

在snake.js模块中需要定义蛇,我们使用一个数组来表示,数组中的每一个元素代表蛇的每一个部分的坐标位置,包括x和y, 因为game-board采用的是网格布局,所以每一项(也就是单元格) 都有固定的坐标, 这个坐标(x,y)不能为0,也不能超过在CSS中设定的网格数(网页中设定的是50) :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#game-board {
background-color: rgb(250, 245, 245);
width: 100vmin;
height: 100vmin;
display: grid;
grid-template-rows: repeat(50, 1fr);
grid-template-columns: repeat(50, 1fr);
}
/* 蛇头样式, 使用图片,来源 https://www.iconfont.cn/ */
.snake-head {
background-image: url('./images/head1.png');
background-size: cover;
}
/* 蛇身子样式 */
.snake-body {
background-color: hsl(200,100%,50%);
border-radius: 80%;
}

蛇用一个对象数组表示,数组中的每个对象表示蛇的一部分

1
2
3
const snakeBody = [
{x : 2, y : 2},
]

在draw函数中根据snakeBody数组的大小创建多个div元素,用于显示蛇,蛇的长度在后面的游戏过程中会要增加,所以这个snakeBody数组会动态改变大小, 创建div元素并设定它的样式代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
//创建一个div元素
const snakeElement = document.createElement('div')
//设置div元素的样式, 使用style
//行位置
snakeElement.style.gridRowStart = snakeBody[0].x
//列位置
snakeElement.style.gridColumnStart = snakeBody[0].y
//背景颜色
snakeElement.style.backgroundColor = 'hsl(200,100%,50%)'
//边框
snakeElement.style.border = '.25vmin solid black'
//将蛇添加到gameBoard元素中
gameBoard.appendChild(snakeElement)

通过循环画蛇,一开始仅有一个蛇头,可以自行在snakeBody中添加对象并查看效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
for(var i=0;i<=snakeBody.length;i++) {
//创建一个div元素
const snakeElement = document.createElement('div')
//设置div元素的样式, 使用style
//行位置
snakeElement.style.gridRowStart = snakeBody[i].x
//列位置
snakeElement.style.gridColumnStart = snakeBody[i].y
//背景颜色
snakeElement.style.backgroundColor = 'hsl(200,100%,50%)'
//边框
snakeElement.style.border = '.25vmin solid black'
//将蛇添加到gameBoard元素中
gameBoard.appendChild(snakeElement)
}

实现的代码删除和背景颜色和边框,使用classList.add方法添加样式,snake.js完整代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function update() {
console.log('更新蛇')
}
function draw(gameBoard) {
for(var i=0;i<=snakeBody.length;i++) {
//创建一个div元素
const snakeElement = document.createElement('div')
//设置div元素的样式, 使用style
//行位置
snakeElement.style.gridRowStart = snakeBody[i].x
//列位置
snakeElement.style.gridColumnStart = snakeBody[i].y

if(index == 0) {
//为蛇头 添加snake-head样式类
snakeElement.classList.add('snake-head')
//蛇头随着按下不同的方向键进行旋转,getHeadDirection函数后面再实现,此行先注释
//snakeElement.style.transform = 'rotate('+getHeadDirection()+")"
}
else {
//为蛇身体添加样式类
snakeElement.classList.add('snake-body')
}
//将蛇添加到gameBoard元素中
gameBoard.appendChild(snakeElement)
}
}
export {update, draw}

画食物的函数在这里就没有贴出代码,可以自己参考画蛇的方法完成食物的绘制