screenshots

The target

The purpose of the game is to experience the use of JavaScript advanced syntax, not the ability to abstract objects. Using object-oriented approach to analyze problems requires a long process of accumulation.

The development tools

VS Code

Project file setup

Now we officially start the snake game production.

Set up the project file directory

Build the page

Put a container for the game scene div#map and style it. The code in index. HTML is as follows:

<! DOCTYPEhtml>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <title>Document</title>
  <link rel="stylesheet" href="css/index.css">
</head>
<body>
  <div class="map" id="map"></div>
</body>
</html>
Copy the code

Create a new index. CSS file in the CSS folder and edit it with the following code:

* {
  margin: 0;
  padding: 0;
}
.map {
  position: relative;
  width: 800px;
  height: 600px;
  background-color: lightgray;
}
Copy the code

Use Alt + B shortcut keys in index. HTML to open it in the browser and generate the game background as follows:

Analyze the objects the game needs to create

Objects to see:

  • Food
  • Serpent (snake)

Invisible objects:

  • Game logic, etc

Creating a food object

Add tool method objects. Add tools.js to the js folder and write the following code:

(function () {
  // Create a tool object with multiple tool methods added inside
  var Tools = {
    // Get a random integer inside a range
    getRandom: function (min, max) {
      min = Math.ceil(min);
      max = Math.floor(max);
      return Math.floor(Math.random() * (max - min + 1)) + min; // Contain maximum value, contain minimum value
    },
    // Get the random color method
    getColor: function () {
      // RGB (r,g,b) three color values can be randomly selected between 0 and 255 numbers
      // Get three color values
      var r = this.getRandom(0.255);
      var g = this.getRandom(0.255);
      var b = this.getRandom(0.255);
      // Return a color value
      return "rgb(" + r + "," + g + "," + b + ")"; }};window.Tools = Tools; }) ();Copy the code

Add the foods.js file to the js folder and open the edit and write the following code: Create a food object

function Food(option) {
  // Avoid passing parameters of the wrong data type or without passing parameters
  option = option instanceof Object ? option : {};
  // The data passed in May be objects like arrays, so you need to make a further judgment
  this.width = option.width || 20;
  this.height = option.height || 20;
  this.x = option.x || 0;
  this.y = option.y || 0;
  this.color = option.color || "green";
  // Add an attribute to store all future div elements rendered from this object
  this.elements = [];
}
Copy the code

Render on the page

  // Render an element onto the page that needs to be added to the method of the prototype object
  Food.prototype.render = function (map) {
  Create a new div element
  var ele = document.createElement("div");
  // Add the corresponding style
  ele.style.width = this.width + "px";
  ele.style.height = this.height + "px";
  ele.style.left = this.x + "px";
  ele.style.top = this.y + "px";
  ele.style.backgroundColor = this.color;
  // Add the new element to the specified parent
  map.appendChild(ele);
  // Add the new element to the array to facilitate the later call to delete
  this.elements.push(ele);
  };
Copy the code

To test, again write the following code in foods.js.

// Get the map element
var map = document.getElementById("map");
/ / test
var food = new Food();
food.render(map);
Copy the code

Then import all the js files in index.html as follows:

<! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, </title> <link rel="stylesheet" href=" CSS /index.css"> </head> <body> <div class="map"  id="map"></div> <script src="js/tools.js"></script> <script src="js/foods.js"></script> </body> </html>Copy the code

The Alt+B shortcut opens with the default browser and a food object is rendered.

Implement random change of food object position

Now the food is fixed in position. If you want it to move randomly, you define the element as absolute position. Var ps = “absolute”; . Horizontal random analysis of food object position: Use a random function whose left value is at (0,n-1) * w, where n is the number of food items that can fit in a row and w is the width of the food object. Similarly, the relevant codes are as follows:

  this.x = Tools.getRandom(0, map.clientWidth / this.width - 1) * this.width;
  this.y = Tools.getRandom(0, map.clientHeight / this.height - 1) * this.height;
Copy the code

Thus, the contents of foods.js are modified as follows:

Var ps = "absolute"; Function Food(option) {function Food(option) {// Do not pass the parameter data type, or do not pass the parameter option = option instanceof Object? option : {}; / / the incoming data is likely to be similar to an array object, so need to be further determine this. Width = option. The width | | 20; this.height = option.height || 20; this.x = option.x || 0; this.y = option.y || 0; this.color = option.color || "green"; This.elements = []; // Add an attribute to store all future div elements rendered by this object. } // Render an element onto the page, Food.prototype.render = function (map) {var ele = document.createElement("div"); This. x = tools. getRandom(0, map.clientWidth/this.width - 1) * this.width; this.y = Tools.getRandom(0, map.clientHeight / this.height - 1) * this.height; Width = this.width + "px"; // Add the corresponding style ele.style.width = this.width + "px"; ele.style.height = this.height + "px"; ele.style.left = this.x + "px"; ele.style.top = this.y + "px"; ele.style.backgroundColor = this.color; ele.style.position = ps; Map. appendChild(ele); // add the new element to the specified parent. // Add the new element to the array for later calls to delete this.elements. Push (ele); }; Var map = document.getelementById ("map"); Var food = new food (); food.render(map);Copy the code

In this way, the function of random location of food is realized.

Food Deletion Method

When eaten by a snake, food disappears and is regenerated in another place. Add the following code to foods.js:

  // Remove a food div element
  Food.prototype.remove = function (map, i) {
    // There are several ways to get the subscript of the food to be deleted
    // Remove the element from the HTML structure
    map.removeChild(this.elements[i]);
    // Remove the element from the array
    this.elements.splice(i, 1);
  };
  // Test, delete food after 2 seconds
  setTimeout(function () {
    food.remove(map, 0)},2000)
Copy the code



Regenerating food is just another callfood.render(map);Can.

Self – calling functions close scope

Now the Food object and tools object have been basically created, but they and their methods are all global variables, which is easy to cause variable pollution and difficult to manage. So we need to wrap them again with self-calling function methods, that is (function (){Food})(); And (the function () {view}) (); Change their scope from global to local. Both global variables are set to point to the encapsulated Food object and tools object. So the contents of foods.js are as follows:

// We need to narrow down the role of the definition constructor
// Anonymous function, self-calling function, IIFE, closed scope
(function () {
  // Global variables
  var ps = "absolute";
  // Create the food constructor
  function Food(option) {
    // Avoid passing parameters of the wrong data type or without passing parameters
    option = option instanceof Object ? option : {};
    // The data passed in May be objects like arrays, so you need to make a further judgment
    this.width = option.width || 20;
    this.height = option.height || 20;
    this.x = option.x || 0;
    this.y = option.y || 0;
    this.color = option.color || "green";
    // Add an attribute to store all future div elements rendered from this object
    this.elements = [];
  }
  // Render an element onto the page that needs to be added to the method of the prototype object
  Food.prototype.render = function (map) {
    Create a new div element
    var ele = document.createElement("div");
    // Get a random x and y value each time before setting the style
    this.x = Tools.getRandom(0, map.clientWidth / this.width - 1) * this.width;
    this.y = Tools.getRandom(0, map.clientHeight / this.height - 1) * this.height;
    // Add the corresponding style
    ele.style.width = this.width + "px";
    ele.style.height = this.height + "px";
    ele.style.left = this.x + "px";
    ele.style.top = this.y + "px";
    ele.style.backgroundColor = this.color;
    ele.style.position = ps;
    // Add the new element to the specified parent
    map.appendChild(ele);
    // Add the new element to the array to facilitate the later call to delete
    this.elements.push(ele);
  };
  // Remove a food div element
  Food.prototype.remove = function (map, i) {
    // There are several ways to get the subscript of the food to be deleted
    // Remove the element from the HTML structure
    map.removeChild(this.elements[i]);
    // Remove the element from the array
    this.elements.splice(i, 1);
  };
  // Expose the Food function with the window object for external use
  window.Food = Food; }) ();// We need to find a way to call the Food function outside
Copy the code

Tools.js contains the following contents:

(function () {
  // Create a tool object with multiple tool methods added inside
  var Tools = {
    // Get a random integer inside a range
    getRandom: function (min, max) {
      min = Math.ceil(min);
      max = Math.floor(max);
      return Math.floor(Math.random() * (max - min + 1)) + min; // Contain maximum value, contain minimum value
    },
    // Get the random color method
    getColor: function () {
      // RGB (r,g,b) three color values can be randomly selected between 0 and 255 numbers
      // Get three color values
      var r = this.getRandom(0.255);
      var g = this.getRandom(0.255);
      var b = this.getRandom(0.255);
      // Return a color value
      return "rgb(" + r + "," + g + "," + b + ")"; }};window.Tools = Tools; }) ();Copy the code

Creating a snake object



Analysis needs: A snake is born with three segments, two bodies and one head, fixed at birth (upper left corner of map). Has the movement direction, the timing movement step length and other attributes. Rendering methods, movement methods, food eating methods, etc. After eating food, the snake grows in length.

Start creating:

Create Snake’s constructor and set the properties

Width The default width of the snake node is 20

Height The default height of the snake festival is 20

The body array, the head and body of the snake, starts with the head

Direction Direction of the snake by default, right can be left, top, or bottom

Through the prototype setup method

Render Creates a random snake object and prints it to the map

Encapsulation is done by self-calling the function: Expose the Snake object through the window

Create a new snake. Js file in the js folder with the following contents.

// Use self-calling functions to close scopes
(function () {
  // Global variables
  var ps = "absolute";
  // Create the snake constructor
  function Snake(option) {
    // Avoid passing parameters of the wrong data type or without passing parameters
    option = option instanceof Object ? option : {};
    // Add attributes to the object
    // Set the width and height properties of the snakestick
    this.width = option.width || 20;
    this.height = option.height || 20;
    // Set the snake data
    this.body = [
      {x: 3.y: 2.color: "red"},
      {x: 2.y: 2.color: "blue"},
      {x: 1.y: 2.color: "blue"}];// Set the direction of the snake. You can also set it to left, top, or bottom
    this.direction = "right";
    // Add an array of elements to store all rendered divs
    this.elements = [];
  }
  // Add a method to render elements to the page
  Snake.prototype.render = function (map) {
    // Generate the corresponding number of div elements
    // go through the number group
    for (var i = 0,len = this.body.length ; i < len ; i++) {
      // Generate a new div element based on the data of each item in the array
      var piece = this.body[i];
      // Create a new element
      var ele = document.createElement("div");
      // Add styles
      ele.style.width = this.width + "px";
      ele.style.height = this.height + "px";
      ele.style.left = piece.x * this.width + "px";
      ele.style.top = piece.y * this.height + "px";
      ele.style.position = ps;
      ele.style.backgroundColor = piece.color;
      // Render inside the specified parent
      map.appendChild(ele);
      // The new element to be added is stored in the array
      this.elements.push(ele); }};// Expose the constructor through window
  window.Snake = Snake; }) ();/ / test
var map = document.getElementById("map");
var snake = new Snake();
snake.render(map);
Copy the code

Then add a reference to index.html:<script src="js/snake.js"></script>Run to get the rendered snake object.

Creating a Game object

Start the Game (draw all Game objects, render food objects and snake objects) by calling the function itself, encapsulate the Game, Comment out the original test code. Game.js is as follows:

// The self-calling function closes the scope
(function () {
  // Define a global variable that stores this
  var that;
  // Create a game constructor
  function Game(map) {
    // Set three properties, store food, snake, map
    this.food = new Food();
    this.snake = new Snake();
    this.map = map;
    that = this;
  }
  // Add a method to start the game that initializes the snake and food
  Game.prototype.start = function () {
    // 1. Add snakes and food to the map
    this.food.render(this.map);
    this.food.render(this.map);
    this.food.render(this.map);
    this.snake.render(this.map);
  }
  // Expose the constructor through window
  window.Game = Game; }) ();/ / test
var map = document.getElementById("map");
game = new Game();
game.start();
Copy the code

Alt+B shortcut in index.html using the default browser.

Add movement to the snake

Analysis: When the snake moves one square, each segment of the snake moves to the position of the previous one. So we can deal with the movements of the snake from back to front, and then we can deal with the movements of the snake’s head. Add the following movement method to the self-calling function of snake. Js:

  // Add the snake movement method
  Snake.prototype.move = function () {
    // Each segment of the snake becomes the same as the previous one
    // The loop needs to start with the last item in order to avoid premature changes to the previous data
    for (var i = this.body.length - 1 ; i > 0 ; i--) {
      this.body[i].x = this.body[i-1].x;
      this.body[i].y = this.body[i-1].y;
    }
    // Store the snakehead data
    var head = this.body[0];
    // The head of the snake changes position according to the direction
    switch (this.direction) {
      case "right":
        head.x += 1;
        break;
      case "left":
        head.x -= 1;
        break;
      case "top":
        head.y -= 1;
        break;
      case "bottom":
        head.y += 1;
    }
Copy the code

When used, call snake. Move () and render snake. Render () to achieve the movement. However, we rendered the snake object twice, which is equivalent to a second snake covering the top of the first snake. The last render didn’t disappear, the snake looked like it had an extra knot of tail, so we had to delete the last render. Add the following deletion method to snake. Js:

// Remove all div elements from the snake that was last rendered
  Snake.prototype.remove = function (map) {
    // Iterate over the number group and delete all elements
    // Delete the element from the HTML structure
    for (var i = this.elements.length - 1 ; i >= 0  ; i--) {
      map.removeChild(this.elements[i]);
    }
    // The array also needs to be emptied
    this.elements = [];
  }
Copy the code

Override the start method in game.js:

// Add a method to start the game that initializes the snake and food
  Game.prototype.start = function () {
    // 1. Add snakes and food to the map
    this.food.render(this.map);
    this.food.render(this.map);
    this.food.render(this.map);
    this.snake.render(this.map);
    this.snake.move();
    this.snake.remove(this.map);
    this.snake.render(this.map);
  }
Copy the code

Save and run Alt+B in index.html to see that the snake has moved one space to the right.It is equivalent to snake calling the move(), remove(), and render() methods once to make the snake object move one space.

Game logic writing

Analysis: After rendering the snake and food to the map, need: 1. Make the snake move automatically runSnake(); In addition, during exercise, eating food will increase the snake body, hitting the wall will end the game. 2. Control the movement direction of the snake through the up, down, and left arrow bindKey(); Game.js is modified as follows:

// The self-calling function closes the scope
(function () {
  // Define a global variable that stores this
  var that;
  // Create a game constructor
  function Game(map) {
    // Set three properties, store food, snake, map
    this.food = new Food();
    this.snake = new Snake();
    this.map = map;
    that = this;
  }
  // Add a method to start the game that initializes the snake and food
  Game.prototype.start = function () {
    // 1. Add snakes and food to the map
    this.food.render(this.map);
    this.food.render(this.map);
    this.food.render(this.map);
    this.snake.render(this.map);
    // 2. Let the game logic begin
    // 2.1 Make the snake move automatically
    runSnake();
    // 2.2 Control the movement direction of the snake through the up, down and left arrows
    bindKey(); 

  }
  // Encapsulates a private function that controls the direction in which the up, down, left, and right keys change
  function bindKey() {
    // Bind keyboard press events to the document
    document.onkeydown = function (e) {
      // console.log(e.keyCode);
      // Keyboard encoding
      // 37 -- left
      // 38 -- top
      // 39 -- right
      // 40 -- bottom
      switch (e.keyCode) {
        case 37:
          that.snake.direction = "left";
          break;
        case 38:
          that.snake.direction = "top";
          break;
        case 39:
          that.snake.direction = "right";
          break;
        case 40:
          that.snake.direction = "bottom";
          break; }}; }// Encapsulates a private function that can only be called inside the module
  function runSnake() {
    // Start a timer to make the snake move continuously
    var timer = setInterval(function () {
      // This inside the timer function refers to window
      // Make the snake move
      that.snake.move();
      // Delete the previous snake
      that.snake.remove(that.map);
      // Render snake in new position
      that.snake.render(that.map);
      // Record the largest position
      var maxX = that.map.offsetWidth / that.snake.width;
      var maxY = that.map.offsetHeight / that.snake.height;
      // Find the current snakehead location
      var headX = that.snake.body[0].x;
      var headY = that.snake.body[0].y;
      // Every time the snake moves to a new location, it decides whether it has eaten
      // 2.3 Judge whether the snake head colliders with the food, eat the food, and add a section to it
      // Record the coordinates of the food
      // var foodX = that.food.x;
      // var foodY = that.food.y;
      // Get the coordinate position of the snake head, px value
      var hX = headX * that.snake.width;
      var hY = headY * that.snake.height;
      / / determine
      // Compare each element in the array of foods, who was eaten, remove themselves, render a new element
      for (var i = 0 ; i < that.food.elements.length ; i++) {
        if (that.food.elements[i].offsetLeft === hX && that.food.elements[i].offsetTop === hY) {
          // Eat the food
          // Let the food be deleted, then render a new food
          that.food.remove(that.map,i);
          that.food.render(that.map);
          // Add a new snake festival
          var last = that.snake.body[that.snake.body.length - 1];
          that.snake.body.push({
            x: last.x,
            y: last.y,
            color: last.color }); }}// Every time you move, check if you are out of the map and the game is over
      // 2.4 Determine if it is out of the map and end the game
      // Make a judgment
      if (headX < 0 || headX >= maxX || headY < 0 || headY >= maxY) {
        // Stop the timer
        clearInterval(timer);
        // Pop up a reminder
        alert("Game over"); }},150);
  }
  // Expose the constructor through window
  window.Game = Game; }) ();/ / test
var map = document.getElementById("map");
game = new Game(map);
game.start();
Copy the code

At this point, the game function is basically completed. Here are some optimizations for non-functional implementations.

Code optimization

All use self-calling functions

We need to wrap the test part of game.js into a separate JS file and use it as a self-calling function. So we create a new main.js file in the js folder with the following contents:

// Use self-calling functions to close scopes
(function () {
  var map = document.getElementById("map");
  var game = newGame(map); game.start(); }) ();Copy the code

Add a reference to index.html: Thus, each JS file is a self-calling function, each with its own function and scope.

Arguments to a self-calling function

In the self-calling function, we use the window variable to expose the constructor through the window. When parsing, the interpreter will jump out of scope every time to look for the global variable Window, which is inefficient and cannot be compressed like other variables when compressing the code, so we need to pass the window parameter to the self-calling function. Since undefined can be overridden to give a new value in IE8, we also pass undefined, in the scope of the self-calling anonymous function, to make sure undefined is really undefined. Thus, all self-calling functions are of the following form:

(function (window.undefined) ({})window.undefined);
Copy the code

Js code compression

Create a new index.js folder in the js folder, which contains the contents of tools.js, foods.js, snake. Js, game.js, and main.js in order:

// All module code should be introduced in a certain order
// ======================Tools============================; (function (window.undefined) {
  // Create a tool object with multiple tool methods added inside
  var Tools = {
    // Get a random integer inside a range
    getRandom: function (min, max) {
      min = Math.ceil(min);
      max = Math.floor(max);
      return Math.floor(Math.random() * (max - min + 1)) + min; // Contain maximum value, contain minimum value
    },
    // Get the random color method
    getColor: function () {
      // RGB (r,g,b) three color values can be randomly selected between 0 and 255 numbers
      // Get three color values
      var r = this.getRandom(0.255);
      var g = this.getRandom(0.255);
      var b = this.getRandom(0.255);
      // Return a color value
      return "rgb(" + r + "," + g + "," + b + ")"; }};window.Tools = Tools; }) (window.undefined)
// ==================Food===========================; (function (window.undefined) {
  // Global variables
  var ps = "absolute";
  // Create the food constructor
  function Food(option) {
    // Avoid passing parameters of the wrong data type or without passing parameters
    option = option instanceof Object ? option : {};
    // The data passed in May be objects like arrays, so you need to make a further judgment
    this.width = option.width || 20;
    this.height = option.height || 20;
    this.x = option.x || 0;
    this.y = option.y || 0;
    this.color = option.color || "green";
    // Add an attribute to store all future div elements rendered from this object
    this.elements = [];
  }
  // Render an element onto the page that needs to be added to the method of the prototype object
  Food.prototype.render = function (map) {
    Create a new div element
    var ele = document.createElement("div");
    // Get a random x and y value each time before setting the style
    this.x = Tools.getRandom(0, map.clientWidth / this.width - 1) * this.width;
    this.y = Tools.getRandom(0, map.clientHeight / this.height - 1) * this.height;
    // Add the corresponding style
    ele.style.width = this.width + "px";
    ele.style.height = this.height + "px";
    ele.style.left = this.x + "px";
    ele.style.top = this.y + "px";
    ele.style.backgroundColor = this.color;
    ele.style.position = ps;
    // Add the new element to the specified parent
    map.appendChild(ele);
    // Add the new element to the array to facilitate the later call to delete
    this.elements.push(ele);
  };
  // Remove a food div element
  Food.prototype.remove = function (map, i) {
    // There are several ways to get the subscript of the food to be deleted
    // Remove the element from the HTML structure
    map.removeChild(this.elements[i]);
    // Remove the element from the array
    this.elements.splice(i, 1);
  };
  // Expose the Food function with the window object for external use
  window.Food = Food; }) (window.undefined)
// ===================Snake=============================; (function (window.undefined) {
  // Global variables
  var ps = "absolute";
  // Create the snake constructor
  function Snake(option) {
    // Avoid passing parameters of the wrong data type or without passing parameters
    option = option instanceof Object ? option : {};
    // Add attributes to the object
    // Set the width and height properties of the snakestick
    this.width = option.width || 20;
    this.height = option.height || 20;
    // Set the snake data
    this.body = [
      {x: 3.y: 2.color: "red"},
      {x: 2.y: 2.color: "blue"},
      {x: 1.y: 2.color: "blue"}];// Set the direction of the snake. You can also set it to left, top, or bottom
    this.direction = "right";
    // Add an array of elements to store all rendered divs
    this.elements = [];
  }
  // Add a method to render elements to the page
  Snake.prototype.render = function (map) {
    // Generate the corresponding number of div elements
    // go through the number group
    for (var i = 0,len = this.body.length ; i < len ; i++) {
      // Generate a new div element based on the data of each item in the array
      var piece = this.body[i];
      // Create a new element
      var ele = document.createElement("div");
      // Add styles
      ele.style.width = this.width + "px";
      ele.style.height = this.height + "px";
      ele.style.left = piece.x * this.width + "px";
      ele.style.top = piece.y * this.height + "px";
      ele.style.position = ps;
      ele.style.backgroundColor = piece.color;
      // Render inside the specified parent
      map.appendChild(ele);
      // The new element to be added is stored in the array
      this.elements.push(ele); }};// Add the snake movement method
  Snake.prototype.move = function () {
    // Each segment of the snake becomes the same as the previous one
    // The loop needs to start with the last item in order to avoid premature changes to the previous data
    for (var i = this.body.length - 1 ; i > 0 ; i--) {
      this.body[i].x = this.body[i-1].x;
      this.body[i].y = this.body[i-1].y;
    }
    // Store the snakehead data
    var head = this.body[0];
    // The head of the snake changes position according to the direction
    switch (this.direction) {
      case "right":
        head.x += 1;
        break;
      case "left":
        head.x -= 1;
        break;
      case "top":
        head.y -= 1;
        break;
      case "bottom":
        head.y += 1; }};// Remove all div elements from the snake that was last rendered
  Snake.prototype.remove = function (map) {
    // Iterate over the number group and delete all elements
    // Delete the element from the HTML structure
    for (var i = this.elements.length - 1 ; i >= 0  ; i--) {
      map.removeChild(this.elements[i]);
    }
    // The array also needs to be emptied
    this.elements = [];
  }
  // Expose the constructor through window
  window.Snake = Snake; }) (window.undefined)
// ========================Game===================; (function (window.undefined) {
  // Define a global variable that stores this
  var that;
  // Create a game constructor
  function Game(map) {
    // Set three properties, store food, snake, map
    this.food = new Food();
    this.snake = new Snake();
    this.map = map;
    that = this;
  }
  // Add a method to start the game that initializes the snake and food
  Game.prototype.start = function () {
    // 1. Add snakes and food to the map
    this.food.render(this.map);
    this.food.render(this.map);
    this.food.render(this.map);
    this.snake.render(this.map);
    // 2. Let the game logic begin
    // 2.1 Make the snake move automatically
    runSnake();
    // 2.2 Control the movement direction of the snake through the up, down and left arrows
    bindKey(); 

  }
  // Encapsulates a private function that controls the direction in which the up, down, left, and right keys change
  function bindKey() {
    // Bind keyboard press events to the document
    document.onkeydown = function (e) {
      // console.log(e.keyCode);
      // Keyboard encoding
      // 37 -- left
      // 38 -- top
      // 39 -- right
      // 40 -- bottom
      switch (e.keyCode) {
        case 37:
          that.snake.direction = "left";
          break;
        case 38:
          that.snake.direction = "top";
          break;
        case 39:
          that.snake.direction = "right";
          break;
        case 40:
          that.snake.direction = "bottom";
          break; }}; }// Encapsulates a private function that can only be called inside the module
  function runSnake() {
    // Start a timer to make the snake move continuously
    var timer = setInterval(function () {
      // This inside the timer function refers to window
      // Make the snake move
      that.snake.move();
      // Delete the previous snake
      that.snake.remove(that.map);
      // Render snake in new position
      that.snake.render(that.map);
      // Record the largest position
      var maxX = that.map.offsetWidth / that.snake.width;
      var maxY = that.map.offsetHeight / that.snake.height;
      // Find the current snakehead location
      var headX = that.snake.body[0].x;
      var headY = that.snake.body[0].y;
      // Every time the snake moves to a new location, it decides whether it has eaten
      // 2.3 Judge whether the snake head colliders with the food, eat the food, and add a section to it
      // Record the coordinates of the food
      // var foodX = that.food.x;
      // var foodY = that.food.y;
      // Get the coordinate position of the snake head, px value
      var hX = headX * that.snake.width;
      var hY = headY * that.snake.height;
      / / determine
      // Compare each element in the array of foods, who was eaten, remove themselves, render a new element
      for (var i = 0 ; i < that.food.elements.length ; i++) {
        if (that.food.elements[i].offsetLeft === hX && that.food.elements[i].offsetTop === hY) {
          // Eat the food
          // Let the food be deleted, then render a new food
          that.food.remove(that.map,i);
          that.food.render(that.map);
          // Add a new snake festival
          var last = that.snake.body[that.snake.body.length - 1];
          that.snake.body.push({
            x: last.x,
            y: last.y,
            color: last.color }); }}// Every time you move, check if you are out of the map and the game is over
      // 2.4 Determine if it is out of the map and end the game
      // Make a judgment
      if (headX < 0 || headX >= maxX || headY < 0 || headY >= maxY) {
        // Stop the timer
        clearInterval(timer);
        // Pop up a reminder
        alert("Game over"); }},150);
  }
  // Expose the constructor through window
  window.Game = Game; }) (window.undefined)
// ========================== Main =========================; (function (window.undefined) {
  var map = document.getElementById("map");
  var game = newGame(map); game.start(); }) (window.undefined)
Copy the code

Search for “code compression” in a search engine, and you’ll find an online compression tool that removes comments, whitespace, and identifier obfuscation. The transmission speed will be improved after compression. We compress the contents of index.js. Create a new index.min.js file in the js folder, and the content is the compressed code:

(function(){var a={getRandom:function(c,b){c=Math.ceil(c); b=Math.floor(b); Return Math. Floor (Math. The random () * (b - c + 1)) + c}, getColor: function () {var e = this. GetRandom (0255); Var d = this. GetRandom (0255); Var c = this. GetRandom (0255); return"rgb("+e+","+d+","+c+")"}}; window.Tools=a})(); (function(){var b="absolute"; function a(c){c=c instanceof Object? c:{}; this.width=c.width||20; this.height=c.height||20; this.x=c.x||0; this.y=c.y||0; this.color=c.color||"green"; this.elements=[]}a.prototype.render=function(d){var c=document.createElement("div"); this.x=Tools.getRandom(0,d.clientWidth/this.width-1)*this.width; this.y=Tools.getRandom(0,d.clientHeight/this.height-1)*this.height; c.style.width=this.width+"px"; c.style.height=this.height+"px"; c.style.left=this.x+"px"; c.style.top=this.y+"px"; c.style.backgroundColor=this.color; c.style.position=b; d.appendChild(c); this.elements.push(c)}; a.prototype.remove=function(d,c){d.removeChild(this.elements[c]); this.elements.splice(c,1)}; window.Food=a})(); (function(){var b="absolute"; function a(c){c=c instanceof Object? c:{}; this.width=c.width||20; this.height=c.height||20; this.body=[{x:3,y:2,color:"red"},{x:2,y:2,color:"blue"},{x:1,y:2,color:"blue"}]; this.direction="right"; this.elements=[]}a.prototype.render=function(g){for(var d=0,c=this.body.length; d<c; d++){var e=this.body[d]; var f=document.createElement("div"); f.style.width=this.width+"px"; f.style.height=this.height+"px"; f.style.left=e.x*this.width+"px"; f.style.top=e.y*this.height+"px"; f.style.position=b; f.style.backgroundColor=e.color; g.appendChild(f); this.elements.push(f)}}; a.prototype.move=function(){for(var d=this.body.length-1; d>0; d--){this.body[d].x=this.body[d-1].x; this.body[d].y=this.body[d-1].y}var c=this.body[0]; switch(this.direction){case"right":c.x+=1; break; case"left":c.x-=1; break; case"top":c.y-=1; break; case"bottom":c.y+=1}}; a.prototype.remove=function(d){for(var c=this.elements.length-1; c>=0; c--){d.removeChild(this.elements[c])}this.elements=[]}; window.Snake=a})(); (function(){var c; function d(e){this.food=new Food(); this.snake=new Snake(); this.map=e; c=this}d.prototype.start=function(){this.food.render(this.map); this.food.render(this.map); this.food.render(this.map); this.snake.render(this.map); b(); a()}; function a(){document.onkeydown=function(f){switch(f.keyCode){case 37:c.snake.direction="left"; break; case 38:c.snake.direction="top"; break; case 39:c.snake.direction="right"; break; case 40:c.snake.direction="bottom"; break}}}function b(){var e=setInterval(function(){c.snake.move(); c.snake.remove(c.map); c.snake.render(c.map); var l=c.map.offsetWidth/c.snake.width; var j=c.map.offsetHeight/c.snake.height; var f=c.snake.body[0].x; var n=c.snake.body[0].y; var m=f*c.snake.width; var k=n*c.snake.height; for(var g=0; g<c.food.elements.length; g++){if(c.food.elements[g].offsetLeft===m&&c.food.elements[g].offsetTop===k){c.food.remove(c.map,g); c.food.render(c.map); var h=c.snake.body[c.snake.body.length-1]; c.snake.body.push({x:h.x,y:h.y,color:h.color})}}if(f<0||f>=l||n<0||n>=j){clearInterval(e); alert("Game over")}},150)}window.Game=d})(); (function(){var b=document.getElementById("map"); var a=new Game(b); a.start()})();Copy the code

Later, there will be a package tool compression, instead of manual compression. We are now in order to understand this compression process, to develop this awareness. Index.html simply introduces a js file named index.min.js, which contains the following contents:

<! DOCTYPEhtml>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <title>Document</title>
  <link rel="stylesheet" href="css/index.css">
</head>
<body>
  <div class="map" id="map"></div>
  <! -- Import multiple js files -->
  <! -- <script src="js/tools.js"></script> <script src="js/food.js"></script> <script src="js/snake.js"></script> <script src="js/game.js"></script> <script src="js/main.js"></script> -->

  <! To optimize performance, you need to reduce the number of HTTP requests the browser sends.
  <! -- <script src="js/index.js"></script> -->
  <script src="js/index.min.js"></script>
</body>
</html>
Copy the code

Run Alt+B to get the same gameplay effect.

Ahh, I am good at cooking!!