这玩意光修bug都花了三小时 最终还是做出来了
效果(WASD操控)
代码
<!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> <link rel="stylesheet" href="./css/style.css"> <style> body { padding: 0; margin: 0; overflow: hidden; }
.map { position: relative; background-color: #313131; width: 100vw; height: 100vh; } .map .snake, .map .food { position: absolute; z-index: 1; display: block; width: 20px; height: 20px; background-color: red; border-radius: 50%; overflow: hidden; transition: all 1s; } #start { z-index: 10000; left: 50%; top: 50%; transform: translate(-50%, -50%); width: 140px; height: 40px; background-color: #00AEEC; border: none; border-radius: 20px; color: #eee; position: absolute; } #start:active { background-color: #00a1ec; } .snake:nth-child(2) { z-index: 111; transform: scale(1.5); background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAMAAAAM7l6QAAAC7lBMVEUAAAACAQAEAgADAgAFAwAAAAABAAAAAAAMCws4NDEAAAB3TSN4Sx8JBAEDAQACAQA0IhD0p0ztjzP1tV/rjTDPeim9cCZ1WUCIUBuUWCCKUBtqPRSNYC5eNxKBTBtYMxFBPz52Rxu5aiEoFwh6TiMSCgIxNjsGAgEfEQY/Ni8RERH7xHtiUULxq1n42qjwqlnwn0j2nD3Ufy3YijruvHTwz5LBcyjJgzunZSWdWx7Wk0jZuHV7SBl6RxjalEnSgzS5gDN+VSnGhkPAhEZ/VipeORZlOhSodTx7SBi3iUFTLw+OThFjOhN1TiaMbzlsPhR/Viw2Ig9xWyvUmDgxHAlFRkh9WR7/5JL+14H/////5pH/5I3/2Hz/oDr/5ZX/4or+3oL+03v85cH+4oj714f/4Ib/2oX+3H//13j/0Hf+zHD/v2X+xl/+vlz8tU75r0X/6JX/4ZD+3o7/0nPyw3L+zGn9x2f+yWb+w2X+vVL6sEn/ojz7mjeRVCH/56D/6Jv72Jf/1ZT+3Yz/3YP/2n3+1XX/zXT5x3P/yXD+0G79wmH8ula2gkf+pET3pkD/pz/83qz/8Kvx06X/66L826L/5Zf/34n/wIf91YT/sYL50n39zHr6w23+wGr/zGbUpGP/u2LYqWD/uFvhrln9wFf9uFH1s0v5q0L3p0L3oz/YlD39nDmjbDOtbyyoZyP/78385sT847rRwbr/66j+5qf84KH/7Jzz05z/6Jj/34/92ozbwoD9xHfVtnP2ynH5tm//xG39ymzyvWz+rWj+nmK/kV3/xVzYo1j+vVf/wlZ9a1H+rk/KlU//u068hkX9qkLgnz/3njvKjDvDgjSYYDGYWiFgHgD///H98uT84rT/4p762J7925n/1onfyon9tn39q3v603m/qnX+tm//0W7+rW7/xm35wGr+oGf+qmPSoV6Fe17Hl1v+s1r8m1X8olT3rE5uXErNkEeYcUfwq0TsokGwfEGERh6SURl3OhXJ/t5dAAAAV3RSTlMABA0IFhtHOEMsIIZYUDQqIP78+PXm19XCwLerpaOgnJqXiYB8Z2NdTUI8/Pz28vHv7Orp6OHg2szGwr27urSxpp+dmZiRj42MioeAeHZrZ2JaTjc3IxPcDjX+AAAC70lEQVQoz2XRZVRaYRzHcQEBZ697s2vd3d29y5QLioqBggJKCHZ3d3frZszWqbOdve7uznf7X8fZ4Ry/757zub/nxXMV5NJU1VNR0VPVVJjSBQOl0ztXpbUFvmRlr96lamIwTV6nac390heQaAUlJqewvi2fq2Usz/tmprx7inC5XO8GK2Zz38w1OnJzwt40K9rDxjoagjjXe8c3NlNTw3Rw//mQY8sTCte9KiYO3Bmh81yFIegxGZ7b/TWawWDc8Wxwj6HREGrOQDSjkhG57MhFTM+u/HXzyiWs254eHjRm9B/Z8dY2PPDJius3rtZ4xkZerrwX50GjMOAYix0rNigCa65/cN/L625VdXWNuzsNcebS4rwpKU1NqWlbsbXxitbHSUlJXlFRUTF1iLMZ3drc4sXrzs6PI7POA5/IbH2UkJDgVVvr4WSKgPr4Wjzr6HjzqajkFPB+VkCidz3iBJmaYlsbql+Qq9BRHI4ehidZxAqg0E0hQDNLax8bqi3PReAgEoe7LcEBZwYkM+PNIDrdEra+trwggUMumwNMUCDsyUxtpzDjLSFrc3PY+gW5gIp/FKLawCofXmUlU5hMHzAb33/ak8cWFxdKjgOfmc7qzvKhYFEBefaCYMcQ9mBx8dAsI4ICTn1TdvtPvq8Flp2dv70raN7g97L8iDka8NcUtftZWaUtVDs7DK9Nqii/vJtdchB7VLzRuv629xM5tjx7QGGwY4+od3hioCtithIeGKd8IOxzYIa0LEfgki5w6OoVDZdL36ZzJEeJcDfMyfPDpgv5oVJp2djo6Fj5b2koP52DLlCHMTZXVNscUcgO5GeElo6Pl4Zm8J/nctAtJEUYTzpRbX5JWJE4Wxjo72/v4iDKH5IsUIOrZRGUSUvXom4jReEFBRxOQQQ6eymJSJgk2f1k/YUbZ0hQNzdUMmPOQn117GY5xyubGOou3j5v3o7FuoZkZbycyT4gapCVSCQlsgYRD9Op4Qh4iCBvfwEgVTTKds/pnwAAAABJRU5ErkJggg==); background-size: contain; } .sad { background-image: url(data:image/png;base64,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) !important; } .map .food { left: 30px; transform: translate(50%, 50%); background-image: url(data:image/png;base64,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); background-size: contain; } &lt;/style&gt;
</head>
<body> <button id="start">开始游戏</button> <div class="map">
&lt;!-- &lt;span class="snake"&gt;&lt;/span&gt; --&gt; &lt;/div&gt; &lt;script&gt; //随机工具类 var Tools = { getRandom: function(min, max) { //min和max都能取到 return Math.floor(Math.random() * (Math.floor(max) - Math.ceil(min) + 1)) + min } }; &lt;/script&gt; &lt;script&gt; //独立作用域 (() =&gt; { //记录上次创建的食物 let elements = []; // 食物对象 function Food(options) { //防止传参为空 options = options || {}; this.x = options.x || 0; this.y = options.y || 0; this.width = options.width || 40; this.height = options.height || 40; this.color = options.color || 'transparent'; } //渲染食物 Food.prototype.render = function(map) { //删除之前创建的食物 remove(); //随机设置食物位置 this.x = Tools.getRandom(0, (map.offsetWidth / this.width - 1) * this.width); this.y = Tools.getRandom(0, (map.offsetHeight / this.height - 1) * this.height); //动态创建元素,我选择用span来显示食物 let span = document.createElement('span'); //设置样式s span.style.left = this.x + 'px'; span.style.top = this.y + 'px'; span.style.backgroundColor = this.color; span.classList.add('food'); map.appendChild(span); //保存食物 elements.push(span); }; //删除食物 function remove() { while (elements.length &gt; 0) { elements[elements.length - 1].parentNode.removeChild(elements[elements.length - 1]); elements.splice(elements.length - 1, 1); } } //把构造函数添加到window对象下,外部就可以访问了 window.Food = Food; })(); &lt;/script&gt; &lt;script&gt; //蛇对象 (() =&gt; { //记录上次创建的蛇 let elements = []; function Snake(options) { //防止传参为空 options = options || {}; //蛇节 this.width = options.width || 20; this.height = options.height || 20; //蛇的移动方向 this.direction = options.direction || 'right'; //蛇身 this.body = [{ x: 12, y: 2, color: 'transparent' }, { x: 11, y: 2, color: `rgba(${Tools.getRandom(0,255)},${Tools.getRandom(0,255)},${Tools.getRandom(0,255)},1)` }, ]; }; //渲染 Snake.prototype.render = function(map) { //删除之前创建的蛇 remove(); //渲染蛇节 for (let i = 0, len = this.body.length; i &lt; len; i++) { //蛇节 let obj = this.body[i]; //创建元素 let span = document.createElement('span'); //设置样式 span.style.width = this.width + 'px'; span.style.height = this.height + 'px'; //obj.x * this.width 表示x个蛇节宽度这么多的距离 span.style.left = (obj.x * this.width) + 'px'; span.style.top = (obj.y * this.height) + 'px'; span.style.backgroundColor = obj.color; span.classList.add('snake'); map.appendChild(span); elements.push(span); } }; //删除蛇方法 function remove() { while (elements.length &gt; 0) { elements[elements.length - 1].parentNode.removeChild(elements[elements.length - 1]); elements.splice(elements.length - 1, 1); } } //蛇移动 Snake.prototype.move = function(food, map) { //控制蛇的身体移动(当前蛇节到上一个蛇节的位置) for (let i = this.body.length - 1; i &gt; 0; i--) { this.body[i].x = this.body[i - 1].x; this.body[i].y = this.body[i - 1].y; } //控制蛇头的移动 //判断蛇移动的方向 let head = this.body[0]; switch (this.direction) { case 'right': head.x += 0.2; break; case 'left': head.x -= 0.2; break; case 'up': head.y -= 0.2; break; case 'down': head.y += 0.2; break; } //判断蛇头是否在食物的坐标范围内 let snakeX = (head.x * (this.width)) let snakeY = (head.y * (this.height)); let leftPaddingIsTrue = ((snakeX &gt;= food.x) &amp;&amp; (snakeX &lt;= (food.x + food.width))) &amp;&amp; ((snakeY &gt;= food.y) &amp;&amp; (snakeY &lt;= (food.y + food.height))) let rightPaddingIsTrue = ((snakeX + this.width / 2 &gt;= food.x) &amp;&amp; (snakeX + this.width / 2 &lt;= (food.x + food.width))) &amp;&amp; ((snakeY + this.height / 2 &gt;= food.y) &amp;&amp; (snakeY + this.height / 2 &lt;= (food.y + food.height))) if (leftPaddingIsTrue || rightPaddingIsTrue) { //蛇加一节 //复制蛇的最后一节 let lastNode = this.body[this.body.length - 1]; for (let i = 0; i &lt; 10; i++) { this.body.push({ x: lastNode.x, y: lastNode.y, color: `rgba(${Tools.getRandom(0,255)},${Tools.getRandom(0,255)},${Tools.getRandom(0,255)},.7)` }); } //随机在地图上增加食物 food.render(map); } } //把构造函数添加到window对象下,外部就可以访问了 window.Snake = Snake; })(); &lt;/script&gt; &lt;script&gt; //主游戏对象 (() =&gt; { var that; //创建食物和蛇对象的构造函数 function Game(map) { this.food = new Food(); this.snake = new Snake(); this.map = map; that = this; } //开始游戏方法 Game.prototype.start = function() { //渲染食物和蛇 this.food.render(this.map); this.snake.render(this.map); runSnake(); //开始游戏逻辑 //让蛇动起来 //通过键盘控制蛇移动的方向 //当蛇遇到食物 做相应的处理 //当蛇遇到边界游戏结束 }; //让蛇润起来 function runSnake() { var timer = setInterval(function() { //这里请注意this指向 that.snake.move(that.food, that.map); that.snake.render(that.map); //当蛇遇到边界游戏结束 //只需要判断蛇头的坐标即可 let headX = that.snake.body[0].x; let headY = that.snake.body[0].y; let maxX = Math.floor(that.map.offsetWidth / that.snake.width) - 0.5; let maxY = Math.floor(that.map.offsetHeight / that.snake.height) - 0.5; if (headX &gt;= maxX || headY &gt;= maxY) { //设置异步,避免alert卡帧 setTimeout(() =&gt; { // alert('你创到墙啦,游戏结束'); document.querySelector('.snake:nth-child(2)').classList.add('sad'); btn.style.display = 'block'; }, 100); clearInterval(timer); } if (headX &lt;= 0 || headY &lt;= 0) { setTimeout(() =&gt; { // alert('你创到墙啦,游戏结束'); document.querySelector('.snake:nth-child(2)').classList.add('sad'); btn.style.display = 'block'; }, 100); clearInterval(timer); } //判断是否吃到了自己 snakeBody = that.snake.body; for (let i = 1; i &lt; snakeBody.length; i++) { if ((snakeBody[0].x == snakeBody[i].x) &amp;&amp; (snakeBody[0].y == snakeBody[i].y)) { setTimeout(() =&gt; { // alert('你咬到自己啦,游戏结束'); document.querySelector('.snake:nth-child(2)').classList.add('sad'); }, 100); //clearInterval(timer); break; } } }, 10); } //蛇控制 function keyControl() { document.addEventListener('keyup', function(e) { switch (e.code) { case 'KeyD': //蛇不能180度掉头 if (that.snake.direction != 'left') { that.snake.direction = 'right'; } break; case 'KeyA': if (that.snake.direction != 'right') { that.snake.direction = 'left'; } break; case 'KeyS': if (that.snake.direction != 'up') { that.snake.direction = 'down'; } break; case 'KeyW': if (that.snake.direction != 'down') { that.snake.direction = 'up'; } break; } }, false); //事件冒泡 } keyControl(); //当蛇遇到食物 做相应的处理 window.Game = Game; })(); &lt;/script&gt; &lt;script&gt; var btn = document.querySelector('#start'); btn.addEventListener('click', function() { this.style.display = 'none'; var map = document.querySelector('.map'); var game = new Game(map); game.start(); }) &lt;/script&gt;
</body>
</html>