主要使用html表格渲染,通过改变单元格class实现,目前还缺很多功能,如速度控制,网格大小控制等
<html>
<head>
<meta charset="utf-8">
</head>
<style>
.snake table {
margin: 20px 0;
color: #2bbc8a
}
.snake tbody {
width: 100%
}
.snake td {
border: 1px solid #0e6e4d;
background: white;
padding: 10px
}
.snake td.active.head {
background: #a94292
}
.snake td.active {
background: #2bbc8a
}
.snake td.food {
background: rebeccapurple
}
</style>
<div class="snake">
<table>
<caption>按空格键开始或暂停</caption>
<tbody>
</tbody>
</table>
</div>
<script>
let rows = 20 // 行数
let cols = 30 // 列数
let snakeList = [] // 蛇身坐标集
let food = null
let keyPress = false
let pause = false
function init() {
snakeList = [{x: 2, y: 15}, {x: 1, y: 15}, {x: 0, y: 15}] // 初始蛇身坐标
pause = true
keyPress = false
render()
}
// 创建网格
function createTable() {
let temp = document.createDocumentFragment()
for (let i = 0; i < rows; i++) {
let tr = document.createElement('tr')
for (let j = 0; j < cols; j++) {
let td = document.createElement('td')
td.setAttribute('row', i)
td.setAttribute('col', j)
td.setAttribute("data", 'x' + j + 'y' + i)
if (i == 0 && j == 0) {
td.setAttribute('class', 'active')
}
tr.appendChild(td)
}
temp.appendChild(tr)
}
let tbody = document.querySelector('.snake tbody')
tbody.appendChild(temp)
}
// 创建食物
function tryCreateFood() {
if (food == null) {
food = {
x: Math.floor(cols * Math.random()),
y: Math.floor(rows * Math.random())
}
let td = getTd(food.x, food.y)
td.setAttribute('class', 'food')
}
}
// 自动前进的函数,定时调用
function autoMove() {
let head = snakeList[0]
let second = snakeList[1]
let next = {
x: head.x,
y: head.y
}
// 通过头两个块判断方向
let dx = head.x - second.x
let dy = head.y - second.y
// x 方向
if (dx != 0) {
next.x += dx
}
// y 方向
if (dy != 0) {
next.y += dy
}
// 身体的每一块都前进, 相当于尾巴少一块,脑袋多一块
snakeList.unshift(next) // insert to first
snakeList.pop() // remove the last
}
function getTd(x, y) {
let selectors = 'td[data=x' + x + 'y' + y + ']'
return document.querySelector(selectors)
}
function render() {
let tdList = document.querySelectorAll('td')
// 清空样式
for (let i = 0; i < tdList.length; i++) {
let td = tdList[i]
td.removeAttribute('class')
}
// 设置样式
for (let i = 0; i < snakeList.length; i++) {
let body = snakeList[i]
let td = getTd(body.x, body.y)
if (td != null) {
td.setAttribute('class', 'active')
if(i == 0){
td.setAttribute('class', 'active head')
}
}
}
if (food != null) {
let foodTD = getTd(food.x, food.y)
foodTD.setAttribute('class', 'food')
}
}
function check() {
let head = snakeList[0]
// 判断撞墙
if (head.x < 0 || head.y > cols || head.y < 0 || head.y > rows) {
alert('撞墙了')
init()
return false
}
// 判断吃到食物
if (food != null && food.x == head.x && food.y == head.y) {
let last = snakeList[snakeList.length - 1]
if (last) {
snakeList.push({
x: last.x,
y: last.y
})
}
food = null
}
return true
}
function frame() {
if (pause) {
return
}
tryCreateFood()
if (keyPress == false) {
autoMove()
if (!check()) {
return
}
}
render()
keyPress = false
}
// 开始执行
createTable()
init()
// 监听按键
document.addEventListener('keydown', function (e) {
if (e.code == 'Space') {
// 开始或暂停
pause = !pause
e.preventDefault()
return
}
// 防抖
if (keyPress) {
return
}
e.preventDefault()
// 步进数, 根据按键增减
let dx = 0, dy = 0
switch (e.code) {
case 'ArrowUp':
dy--
break
case 'ArrowDown':
dy++
break
case 'ArrowLeft':
dx--
break
case 'ArrowRight':
dx++
break
default:
return
}
let head = snakeList[0]
let second = snakeList[1]
// 计算下一格
let next = {
x: head.x + dx,
y: head.y + dy
}
snakeList.unshift(next) // insert to first
snakeList.pop() // remove the last
check()
keyPress = true
})
setInterval(frame, 400)
</script>
</html>