新的起点 
这是我的第一篇博客,记载着我自学前端之后根据自己的一瞬想法而写成的‘东西’。虽然它很粗糙,但是它承载着我对前端的无限想象,是我前端之路的一个重要里程碑。 
时刻回望过去,继续憧憬未来。  
念念不忘,不求回响。 
 
前言 
作为一个前端小白,在b站学习完pink老师讲的动画原理之后,我脑中就有了写一个小游戏的想法。然后发现贪吃蛇好像还挺好实现的,然后就动手开始试着写了一下。
点击试玩⚡ 
实现过程 
用 HTML 和 CSS 构建场景和物件 
HTML 部分 
我的做法就是让整个页面由div标签构成,场地是个div,目标物是个div,蛇头和蛇身都是由一个个的div构成。我加入了障碍物的设定,就是每过一段时间就会在地图上生成一个障碍物。所有的蛇身会被放入类名为allBody的div中,所有的障碍物会被放入类名为allBarrier的div中。 
完整 HTML 代码如下:
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 <!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 > Snake</title >    <link  rel ="stylesheet"  href ="./snake.css" > </head > <body >    <div  class ="wall" >       <div  class ="remind" > </div >       <div  class ="ground" >         <div  class ="target" > </div >        <div  class ="head" > </div >        <div  class ="allBody" > </div >        <div  class ="allBarrier" > </div >      </div >    </div >    <script  src ="./snake.js" > </script > </body > </html > 
 
CSS 部分 
墙,就是除了整个页面除去内部场地的部分,所以给墙加上绝对定位,给提示和场地加上相对定位。而其他部分在场地内,加上绝对定位。 
完整 CSS 代码如下:
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 .wall  {   position : absolute;   left : 0 ;   top : 0 ;   width : 100 %;   height : 100 %;   background-color : greenyellow; } .remind  {   position : relative;   left : 0px;   top : 0px;   width : 50px;   height : 50px;   background-color : pink; } .ground  {   position : relative;   left : 50px;   top : 0 ;   width : 0 ;   height : 0 ;   background-color : white; } .head  {   position : absolute;   left : 0 ;   top : 0 ;   width : 50px;   height : 50px;   background-color : blue; } .body  {   position : absolute;   left : 0 ;   top : 0 ;   width : 50px;   height : 50px;   background-color : aquamarine; } .barrier  {   position : absolute;   left : 0 ;   top : 0 ;   width : 50px;   height : 50px;   background-color : gray; } .target  {   position : absolute;   left : 0 ;   top : 0 ;   width : 50px;   height : 50px;   background-color : red; }
 
用 JavaScript 控制各种功能的实现 
声明 
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 30 31 32 33 34 35 let  wall = document .querySelector ('.wall' );let  remind = document .querySelector ('.remind' );let  ground = document .querySelector ('.ground' );let  target = document .querySelector ('.target' );let  head = document .querySelector ('.head' );let  allBody = document .querySelector ('.allBody' );let  allBarrier = document .querySelector ('.allBarrier' );let  newBody = document .createElement ('div' );let  newBarrier = document .createElement ('div' );let  body = allBody.querySelectorAll ('.body' );let  barrier = allBarrier.querySelectorAll ('.barrier' );let  headX = randomX ();let  headY = randomY ();let  bodyX = head.offsetLeft ;let  bodyY = head.offsetTop ;let  targetX = randomX ();let  targetY = randomY ();let  barrierX = randomX ();let  barrierY = randomY ();let  time = 0 ;let  times = 0 ;let  score = 0 ;let  flag = true ;let  turnUp = true ;let  turnLeft = true ;let  turnDown = true ;let  turnRight = true ;let  single = true ;
 
随机生成函数得到随机值 
随机获取 a~b 之间的一个整数:Math.round(Math.random() * ( b - a ) + a); 
我们要生成是从场所内出现并且间隔为50的随机值
1 2 3 4 5 6 7 8 function  randomX ( ) {   return  Math .round (Math .random () * (wall.offsetWidth  / 50  - 4 ) + 1 ) * 50 ; }function  randomY ( ) {   return  Math .round (Math .random () * (wall.offsetHeight  / 50  - 4 ) + 1 ) * 50 ; }
 
蛇头的生成和控制 
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 30 31 32 function  setHead ( ) {   headX = randomX ();   headY = randomY ();   head.style .left  = headX + 'px' ;   head.style .top  = headY + 'px' ; }function  go (dir ) {   turn (dir);   clearInterval (head.timer );   judge (dir);   head.timer  = setInterval (function  ( ) {     judge (dir);   }, 100 ); }function  judge (dir ) {   body = allBody.querySelectorAll ('.body' );   barrier = allBarrier.querySelectorAll ('.barrier' );   followHead ();   switch  (dir) {     case  'up' : head.style .top  = head.offsetTop  - 50  + 'px' ; break ;     case  'left' : head.style .left  = head.offsetLeft  - 50  + 'px' ; break ;     case  'down' : head.style .top  = head.offsetTop  + 50  + 'px' ; break ;     case  'right' : head.style .left  = head.offsetLeft  + 50  + 'px' ; break ;   }   isGetTarget (dir);   isDead (dir); }
 
障碍物的生成 
在满足不会生成到蛇身、目标物和蛇头附近200px位置的情况下生成障碍物
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 function  setBarrier ( ) {   newBarrier = document .createElement ('div' );   allBarrier.appendChild (newBarrier);   newBarrier.classList .add ('barrier' );   barrierX = randomX ();   barrierY = randomY ();   for  (let  i = 0 ; i < body.length ; i++) {     body = allBody.querySelectorAll ('.body' );     for  (let  j = 0 ; j < barrier.length ; j++) {       barrier = allBarrier.querySelectorAll ('.barrier' );       if  (barrierX === body[i].offsetLeft  && barrierY === body[i].offsetTop  ||         barrierX === barrier[j].offsetLeft  && barrierY === barrier[j].offsetTop  ||         Math .abs ((barrierX - head.offsetLeft )) < 200  ||         Math .abs ((barrierX - head.offsetLeft )) < 200  ||         barrierX === target.offsetLeft  && barrierY === target.offsetTop ) {         barrierX = randomX ();         barrierY = randomY ();         i = 0 ;         j = 0 ;       }     }   }   newBarrier.style .left  = barrierX + 'px' ;   newBarrier.style .top  = barrierY + 'px' ; }
 
目标物的生成及被吃到后的重置 
在满足不会生成到蛇身、障碍物位置的情况下生成目标物
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  setTarget ( ) {   targetX = randomX ();   targetY = randomY ();   for  (let  i = 0 ; i < body.length ; i++) {     body = allBody.querySelectorAll ('.body' );     for  (let  j = 0 ; j < barrier.length ; j++) {       barrier = allBarrier.querySelectorAll ('.barrier' );       if  (targetX === body[i].offsetLeft  && targetY === body[i].offsetTop  ||         targetX === barrier[j].offsetLeft  && targetY === barrier[j].offsetTop ) {         targetX = randomX ();         targetY = randomY ();         i = 0 ;         j = 0 ;       }     }   }   target.style .left  = targetX + 'px' ;   target.style .top  = targetY + 'px' ; }function  isGetTarget (dir ) {   if  (targetX === head.offsetLeft  && targetY === head.offsetTop ) {     single = false ;      times++;     setTarget ();     setBody (dir);   } }
 
吃到目标物后蛇身的增长 
吃到目标物时根据此时的方向来决定新蛇身的位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function  setBody (dir ) {   newBody = document .createElement ('div' );   allBody.appendChild (newBody);   newBody.classList .add ('body' );   if  (dir === 'left' ) {     newBody.style .left  = head.offsetLeft  + 50  + 'px' ;     newBody.style .top  = head.offsetTop  + 'px' ;   }   if  (dir === 'right' ) {     newBody.style .left  = head.offsetLeft  - 50  + 'px' ;     newBody.style .top  = head.offsetTop  + 'px' ;   }   if  (dir === 'up' ) {     newBody.style .left  = head.offsetLeft  + 'px' ;     newBody.style .top  = head.offsetTop  + 50  + 'px' ;   }   if  (dir === 'down' ) {     newBody.style .left  = head.offsetLeft  + 'px' ;     newBody.style .top  = head.offsetTop  - 50  + 'px' ;   } }
 
控制蛇身跟着蛇头走 
核心代码,通过这种方式来控制蛇身的走动
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function  followHead ( ) {   body = allBody.querySelectorAll ('.body' );   if  (body.length  !== 0 ) {     for  (let  i = body.length  - 1 ; i >= 0 ; i--) {       if  (i === 0 ) {         body[i].style .left  = head.offsetLeft  + 'px' ;         body[i].style .top  = head.offsetTop  + 'px' ;       }       else  {         body[i].style .left  = body[i - 1 ].offsetLeft  + 'px' ;         body[i].style .top  = body[i - 1 ].offsetTop  + 'px' ;       }     }   } }
 
判断是否死亡 
三种死亡情况:撞墙死、撞身死和撞障碍物死
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function  isDead (dir ) {      switch  (dir) {     case  'up' : if  (head.offsetTop  < 0 ) gameOver (); break ;     case  'left' : if  (head.offsetLeft  < 0 ) gameOver (); break ;     case  'down' : if  (head.offsetTop  > ground.offsetHeight ) gameOver (); break ;     case  'right' : if  (head.offsetLeft  > ground.offsetWidth ) gameOver (); break ;   }      for  (let  i = 0 ; i < body.length ; i++) {     if  (body[i].offsetLeft  === head.offsetLeft  &&       body[i].offsetTop  === head.offsetTop )       gameOver ();   }      for  (let  j = 0 ; j < barrier.length ; j++) {     if  (barrier[j].offsetLeft  === head.offsetLeft  &&       barrier[j].offsetTop  === head.offsetTop )       gameOver ();   } }
 
死亡后的结算和重启 
这是我的计分公式:score = Math.round(times * 3.5 * (1 + 2 / (time + 1))) 
欢迎提出更合理的计分公式
1 2 3 4 5 6 7 8 9 10 function  gameOver ( ) {   clearInterval (head.timer );   score = Math .round (times * 3.5  * (1  + 2  / (time + 1 )));   if  (score > 100 ) score = 100 ;   alert ('本次你坚持了'  + time + '秒,吃到了'  + times + '次红方块,   系统评分为'  + score + '分,再来一次吧~' );   setHead ();   setTarget ();   window .location .reload (); }
 
如何控制有蛇身时,不能直接往反方向走 
排己思想,先将所有方向都设置为false,然后将选定方向的反方向设为false
1 2 3 4 5 6 7 8 9 10 11 12 function  turn (dir ) {   turnUp = true ;   turnLeft = true ;   turnDown = true ;   turnRight = true ;   switch  (dir) {     case  'up' : turnDown = false ; break ;     case  'left' : turnRight = false ; break ;     case  'down' : turnUp = false ; break ;     case  'right' : turnLeft = false ; break ;   } }
 
其他 
document.addEventListener(‘keydown’, function (e) {…}) 
和remind.addEventListener(‘click’, function () {alert(‘…’);})就不贴出来啦
完整的 JavaScript 的代码可以去 我的 Github  取。
总结 
对于一个初学者来说,能自己通过思考,结合之前学过的知识来完成一个小游戏,还是蛮有成就感的事情啊。如果有初学者也想自己动手写一下这个小游戏的话,可以参考我的思路来试着完成。我的代码肯定还有很多需要优化的地方,恳请大佬们提出意见和指导 QWQ