SOURCE

console 命令行工具 X clear

                    
>
console
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="UTF-8" />
		<meta name="viewport" content="width=device-width, initial-scale=1.0" />
		<title>贪吃蛇</title>
	</head>
	<body>
    <script type="text/javascript">
    
    // ES5的语法中 js没有块级作用域的概念 
    // 用自执行函数充当块级作用域

			/* 地图类*/
			(function() {
			  // 地图构造函数 需要三个参数 宽高背景色
			  function Map(width,height,bgColor){
          this.width = width;
          this.height = height;
          this.bgColor = bgColor;
        }
        
        // 将公共方法创建到原型对象上
        Map.prototype.init = function(){
          // 创建一个div
          let divMap = document.createElement('div')

          // 设置宽高背景色
          divMap.style.width = this.width + 'px';
          divMap.style.height = this.height + 'px';
          divMap.style.backgroundColor = this.bgColor
          divMap.style.left = '50%'
          divMap.style.transform = 'translate(-50%,15%)'

          // 设置divMap 相对定位(作为食物的包含块)
          divMap.style.position = 'relative'
          divMap.id = 'divMap'

          // 添加到body中
          document.body.append(divMap)
        }

        // 将构造函数Map挂载到window上 暴露出去
        window.Map = Map
      })();
      
      // 食物类
      (function() {
        // 食物数组
        let elements = [];
        // 食物构造函数
        function Food(width,height,bgColor){
          this.width = width;
          this.height = height;
          this.bgColor = bgColor;
          this.x = 0;
          this.y = 0;
        }

        // 食物原型
        Food.prototype.init = function(){
          // 先删除
          remove();
          // 创建食物
          let divFood = document.createElement('div')
          // 获取divMap
          let divMap = document.getElementById('divMap')

          // 设置食物div的css属性
          divFood.style.width = this.width + 'px'
          divFood.style.height = this.height + 'px'
          divFood.style.backgroundColor = this.bgColor
          divFood.style.position = 'absolute'

          /*
            x和y存储的是格数, 用地图的宽度800除以食物的宽度20等于40, 生成一个0-40之间的随机数
            再向下取整 就得到了食物X轴和Y轴的格数 再乘以自身的宽高就得到了食物的left和top值
          */
          this.x = Math.floor(Math.random() * (divMap.offsetWidth / this.width))
          this.y = Math.floor(Math.random() * (divMap.offsetHeight / this.height))

          divFood.style.left = this.x * this.width + 'px'
          divFood.style.top = this.y * this.height + 'px'
          // 添加到divMap中
          divMap.appendChild(divFood)
          // 将divFood存到食物数组中
          elements.push(divFood)
        }

        function remove() {
          let divMap = document.getElementById('divMap')
          for (let i = 0; i < elements.length; i++) {
            // 删除divMap中的食物
            divMap.removeChild(elements[i])
            // 清空食物数组
            elements.splice(i, 1)
          }
        }

        // 将构造函数Food挂载到window上
        window.Food = Food
      })();

      // 蛇类
      (function() {
        // 蛇数组
        let elements = []
        // 蛇构造函数 dir表示方向
        function Snake(width,height,dir) {
          this.width = width
          this.height = height
          this.dir = dir

          // this.snBody表示整条蛇, 以数组的形式存储三个对象 分别代蛇头 蛇身 蛇尾
          this.snBody = [{
            x:3,
            y:2,
            bgColor: 'red'
          },{
            x:2,
            y:2,
            bgColor: '#64f'
          },{
            x:1,
            y:2,
            bgColor: '#64f'
          }]
        }

        // 初始化蛇
        Snake.prototype.init = function(){
          // 干掉死去的蛇
          remove();
          // 分别创建蛇头 蛇中 蛇尾3个div 分别设置div的css属性
          let divMap = document.getElementById('divMap')
          for(let i = 0; i < this.snBody.length; i++) {
            let divSnake = document.createElement('div')
            divSnake.style.width = this.width + 'px'
            divSnake.style.height = this.height + 'px'
            divSnake.style.background = this.snBody[i].bgColor;
            divSnake.style.position = 'absolute'
            divSnake.style.left = this.snBody[i].x * this.width + 'px'
            divSnake.style.top = this.snBody[i].y * this.height + 'px'

            // 添加到dom上
            divMap.appendChild(divSnake)
            elements.push(divSnake)
          }
        }

        // 蛇移动
        Snake.prototype.move = function(){
          // 把蛇中的xy给蛇尾 把蛇头的xy给蛇中
          for(let i = this.snBody.length - 1; i > 0; i--){
            this.snBody[i].x = this.snBody[i - 1].x
            this.snBody[i].y = this.snBody[i - 1].y
          }

          // 根据dir的值来决定蛇头的方向
          if(this.dir === 'right') {
            // 蛇头x自加1
            this.snBody[0].x += 1
          }

          if(this.dir === 'left') {
            // 蛇头x自减1
            this.snBody[0].x -= 1
          }
          
          if(this.dir === 'top') {
            // 蛇头x自减1
            this.snBody[0].y -= 1
          }

          if(this.dir === 'bottom') {
            // 蛇头x自加1
            this.snBody[0].y += 1
          }
        }

        // 删除方法
        function remove() {
          // 获取地图div
          let divMap = document.getElementById('divMap')
          // 反向遍历 从蛇尾往蛇头删除
          for(let i = elements.length - 1; i >= 0; i--) {
            // 删除地图div中的dom
            divMap.removeChild(elements[i])
            // 删除蛇数组中的元素
            elements.splice(i, 1)
          }
        }

        window.Snake = Snake
      })();
    
      // 游戏类 定义游戏规则
      (function(){
        // 用来存储game实例对象
        let gameThis = null
        let timer = 0

        // 游戏构造函数
        function Game() {
          // 实例化地图对象
          this.mapG = new Map(800, 600, '#ccc')

          // 实例化食物对象
          this.food = new Food(20, 20, 'green') 

          // 实例化蛇对象
          this.snake = new Snake(20, 20, 'right')
        }

        Game.prototype.init = function(){
          // 用对象调原型对象中的init方法
          this.mapG.init()

          this.food.init()

          this.snake.init()
        }

        // 游戏规则
        Game.prototype.ruler = function(){
          // 将game实例对象保存到局部变量中
          gameThis = this

          // 所有的游戏规则都要在定时器内
          timer = setInterval(function(){
            // 调用蛇移动函数
            gameThis.snake.move()
            // 调用蛇初始化函数
            gameThis.snake.init()

            // 判断蛇是否超出边界(四个方向)
            if(gameThis.snake.snBody[0].x < 0 || (gameThis.snake.snBody[0].x >= gameThis.mapG.width / gameThis.snake.width) || gameThis.snake.snBody[0].y < 0 || (gameThis.snake.snBody[0].y >= gameThis.mapG.height / gameThis.snake.height)) {
              console.log('Game Over')

              // 关闭定时器
              clearInterval(timer)

              // 弹出对话框
              let divOver = document.createElement('div')
              divOver.style.width = '300px'
              divOver.style.height = '200px'
              divOver.style.background = 'rgba(255,0,0,0.7)'
              divOver.style.lineHeight = '200px'
              divOver.style.textAlign = 'center'
              divOver.style.position = 'absolute'
              divOver.style.left = '50%'
              divOver.style.marginLeft = '-150px'
              divOver.style.top = '50%'
              divOver.style.marginTop = '-100px'
              divOver.style.zIndex = '10'

              document.body.appendChild(divOver)
              let btnAgain = document.createElement('button')
              btnAgain.innerText = 'try angin'
              btnAgain.style.cursor = 'pointer'

              btnAgain.onclick = function() {
                // 刷新
                window.location.reload()
              }

              divOver.appendChild(btnAgain)
            }
            
            // 吃食物长尾巴 删除食物 重新生成新食物
            if(gameThis.snake.snBody[0].x === gameThis.food.x && gameThis.snake.snBody[0].y === gameThis.food.y) {
              console.log('吃到了')
              gameThis.food.init()

              // 创建新增蛇尾的对象 把最后一节蛇尾的x和y 赋值给新增的
              let t = {
                x: gameThis.snake.snBody[gameThis.snake.snBody.length - 1].x,
                y: gameThis.snake.snBody[gameThis.snake.snBody.length - 1].y,
                bgColor: '#64f'
              }

              // 将元素添加到数组中
              gameThis.snake.snBody.push(t)
            }

            // 键盘按下事件
            document.onkeydown = function(e){
              if(e.keyCode === 37) {
                // 向左
                gameThis.snake.dir = 'left'
              }

              if(e.keyCode === 38) {
                // 向上
                gameThis.snake.dir = 'top'
              } 

              if(e.keyCode === 39) {
                // 向右
                gameThis.snake.dir = 'right'
              }

              if(e.keyCode === 40) {
                // 向下
                gameThis.snake.dir = 'bottom'
              }
            }

          },100)
        }

        window.Game = Game
      })();

      // 游戏实例对象
      let game = new Game()
  
      // 游戏初始化
      game.init()

      // 游戏规则方法
      game.ruler()
    </script>
	</body>
</html>