Note source: Hook Education – big front end employment Training camp

Content: Notes, thoughts and experiences in the process of learning

Snake game case

Analyze the objects that need to be encapsulated

In the whole game, we can see two objects, namely food and snake. At the same time, we can also encapsulate the logic of the game into objects, so that we can abstract out three objects

Note: This can be encapsulated using self-calling functions (execute functions immediately), exposing the object through the Window

Food object

Analyze properties and methods

Properties:

  • The background color of the food
  • Food width and speed
  • Food positioning throughout the game interface —-top and left values

Methods:

  • Food is rendered to the stage, so there needs to be a method of rendering to the stage, and random positioning is required
  • Delete the food method. When the snake encounters the food, it needs to delete the food. Of course, considering that there may be multiple food in the later stage, it needs to create a food set to accommodate all the food

Code implementation

<! Create a new index.html file -->
<! DOCTYPEhtml>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <title>Document</title>
  <! -- Introducing styles -->
  <link rel="stylesheet" href="./css/index.css">
</head>

<body>
  <! -- Stage elements -->
  <div class="box" id="box"></div>
  <! -- External JS files introduced (only three so far, more to come)
  <script src="./js/tools.js"></script>
  <script src="./js/food.js"></script>
  <script src="./js/index.js"></script>
</body>

</html>
Copy the code
/* Create an index. CSS file */

/* Style reset */
* {
  margin: 0;
  padding: 0;
}

/* Stage style */
.box {
  position: relative;
  width: 500px;
  height: 500px;
  background: #ccc;
}
Copy the code
// Create a new food.js file

// To avoid naming messy variables or functions globally, use the self-calling function (execute the function immediately) to define Food on window, followed by window. Food or simply call the constructor with Food
(function () {
  // Create a variable. Store the location property of the food, which is set to absolute location
  var ps = 'absolute';
  // Create a new constructor
  function Food(obj) {
    // To avoid incorrect formatting of data passed in by the user, use a judgment that if the user passed in data of object type, use it directly, otherwise use empty object
    obj = obj instanceof Object ? obj : {};
    // Set the values of the five properties and give them default values in case the user does not enter them
    / / width
    this.width = obj.width || 20;
    / / height
    this.height = obj.height || 20;
    //定位top
    this.top = obj.top || 0;
    / / position left
    this.left = obj.left || 0;
    / / the background color
    this.backgroundColor = obj.backgroundColor || 'skyblue';
    // Use an attribute to hold the element nodes that will be generated later, so that more than one food can be used later
    this.items = [];
  }
  // Redefine the Food prototype object
  Food.prototype = {
    // Point to Food manually
    constructor: Food,
    // Render method, create a new element and render it to the stage
    // Parameters: the stage to render to -- element, in this case box element
    reader: function (father) {
      // Sets the CSS style of the element
      // Random top positioning value: Using the random number method of the tool object Tools, to avoid the occurrence of dislocation first, we split the stage into small squares
      // Top = random number (0, stage height/food height -1) * food height
      this.top = tools.getRandomNumber(0, father.clientHeight / this.height - 1) * this.height;
      //left position value, same principle as above
      this.left = tools.getRandomNumber(0, father.clientWidth / this.width - 1) * this.width;
      // Create an element node
      var fooder = document.createElement('span');
      // Sets the element node attributes
      // Locate attributes and use variables to facilitate later modification
      fooder.style.position = ps;
      / wide/high
      fooder.style.width = this.width + 'px';
      fooder.style.height = this.height + 'px';
      // Locate the offset property
      fooder.style.top = this.top + 'px';
      fooder.style.left = this.left + 'px';
      / / the background color
      fooder.style.backgroundColor = this.backgroundColor;
      // Add elements to the stage
      father.appendChild(fooder);
      // Store elements
      this.items.push(fooder);
    },
    // Delete element method
    // Parameter 1: stage element
    // Argument 2: the subscript of the element to be deleted
    remove: function (father, index) {
      father.removeChild(this.items[index]);
      this.items.splice(index, 1); }}// Store the constructor inside window. Since window can be omitted, we can use Food directly
  window.Food = Food; }) ()Copy the code
// Create a new tools.js file

// a tool object to store methods that will be used
var tools = {
  // Generate a random number
  getRandomNumber: function (min, max) {
    min = Math.ceil(min);
    max = Math.floor(max);
    return Math.floor(Math.random() * (max - min + 1) + min);
  },
  // Generate random colors
  getRandomColor: function () {
    return 'rgb(' + this.getRandomNumber(0.255) + ', ' + this.getRandomNumber(0.255) + ', ' + this.getRandomNumber(0.255) + ') '; }}Copy the code

The snake object

Properties and methods

Properties:

  • The width and height of each snake joint
  • Special attributes for each section: positioning left and top values, background color (snake head and body are different)
  • The direction of movement of the snake

Methods:

  • Render to stage method (Reader)

Code implementation

// Add the snake constructor to the window, just like food
(function () {
  // Create a variable that represents the location of the snake node, set to absolute location
  var ps = 'absolute';

  // Snake constructor
  function Snick(opt) {
    // Determine if the passed argument is an object type, if yes, empty otherwise
    opt = opt instanceof Object ? opt : {};
    // Write the width and height properties and default values for the snake section
    this.width = opt.width || 20;
    this.height = opt.height || 20;
    // Put each section of the snake body into an array for later modification
    // Snake array: Each element inside is an object that stores the location properties and background color of the snake section
    this.body = [
        // The first one represents the head of the snake, and the rest represent each section of the snake's body. Note that the head and the body are prohibited in different colors
        {
          x: 3.y: 2.color: 'red'
        }, {
          x: 2.y: 2.color: 'blue'
        }, {
          x: 1.y: 2.color: 'blue'}].// The default movement direction of the snake is right, up, down, or left
      this.direction = 'right';
  }
  // Redefine the constructor prototype object
  Snick.prototype = {
    // Point to the constructor manually
    constructor: Snick,
    // Render method
    // Parameters: stage elements
    reader: function (father) {
      // We need to render each snake section to the stage, so we need to traverse the entire snake array
      // Create two new variables to save reading this.body.length every time
      for (let i = 0, len = this.body.length; i < len; i++) {
        // Create a new element node
        var me = document.createElement('span');
        // Sets the inline style of the element node
        // Location mode
        me.style.position = ps;
        / wide/high
        me.style.width = this.width + 'px';
        me.style.height = this.height + 'px';
        // Locate the offset property
        me.style.top = this.body[i].y * this.height + 'px';
        me.style.left = this.body[i].x * this.width + 'px';
        // Background color
        me.style.backgroundColor = this.body[i].color;
        // Insert each section into the stagefather.appendChild(me); }}}// Assign the entire constructor to the window
  window.Snick = Snick; }) ()Copy the code

The game object

Properties and methods

Properties:

  • Food Object Instance
  • Snake Object Instance
  • The stage object

Methods:

  • Initialization (Render food and snake initialization)

Code implementation

// Create a new game.jss

// Write to the self-calling function (immediately executed inside the function), again placing the constructor on the window object
(function () {
  // Gameobject constructor
  // Parameters: stage elements
  function Game(map) {
    // Three attributes
    // Food instance
    this.food = new Food();
    / / instance
    this.snick = new Snick();
    // Stage elements
    this.map = map;
  }
  // Bloodshot gameobject prototype object
  Game.prototype = {
    // Execute the constructor
    constructor: Game,
    // Initialize the snake and food to the stage
    inIt: function () {
      this.food.reader(this.map);
      this.snick.reader(this.map); }}// Put the game constructor above the window
  window.Game = Game; }) ()// Test the code
// Get the stage
var map = document.getElementById('box');
// Create a game instance
var game = new Game(map);
// Perform game initialization
game.inIt();
Copy the code

Game logic implementation

Snake movement and game over

We need to add a motion method to the snake. We can do this using the previous snake instance’s Reader method (we can use a timer to execute the snake’s Reader method at regular intervals), but note that we need to remove the rendered snake before calling the method, otherwise you will find two snakes

Snake deletion method

Ideas:

  1. In order to delete we have to find the element to delete
  2. In our snake rendering method, every snake section added to the stage is stored in an array property (this.items)
  3. Add the remove method (remove) to the snake prototype to call the array traversal of this property to remove it
  4. And then we call the snake render method (Reader)
/ / snick. Js file

// Add the snick array to the snick constructor
this.items = [];

// Add the delete snake method to Snick
// Delete the snake method
// Parameters: stage elements
remove: function (father) {
  // Iterate through the snake section array, removing each item from the stage element
  for (let i = 0, l = this.items.length; i < l; i++) {
    // Delete a node
    father.removeChild(this.items[i]);
  }
  // Reset the snake node array
  this.items = [];
}
Copy the code

Snake position update method

Ideas:

  1. Create a new upDate method for the snake instance.
  2. (Every time the snake moves, the snake body (except the head) will reach the position of the previous section, so we update the position of the previous section and assign the position to the next section, and the head will adjust itself according to the direction of movement).
/ / snick. Js file

// Add to the snick prototype object
// Snake node position update method
    upDate: function () {
      // Loop through the array of snakes, setting the location of each snake to the location of the previous one
      for (let 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;
      }
      // Modify the position of the snake head according to the direction of movement
      switch (this.direction) {
        case 'right':
          this.body[0].x++;
          break;
        case 'left':
          this.body[0].x--;
          break;
        case 'top':
          this.body[0].y--;
          break;
        case 'buttom':
          this.body[0].y++;
          break; }}Copy the code

Make the snake move and stop the game beyond the edge

movement

We’ve already defined the snake delete method, position update method, and render method inside the snake, so all we need to do to move the snake is repeat delete, update, and render methods

Game over

We judge whether the head of the snake is beyond the edge of the game stage every time the snake moves. We can judge the x and Y values of the head of the snake. When x or Y is less than 0 or greater than the maximum score, the game can be stopped

Finally realize

We can write this whole process inside a function, so that we don’t need to write it inside the constructor, and write a timer inside the function that continuously executes the snake’s deletion, position update, render method, and each time decide whether to go beyond the stage edge.

Note that this points to a problem, because writing this inside an external function might invalidate this, so we need to manually place this

//game.js

// Write to the self-calling function (immediately executed inside the function), again placing the constructor on the window object
(function () {
  // Define a variable that points to the constructor's this
  var that;
  // Gameobject constructor
  // Parameters: stage elements
  function Game(map) {
    // Three attributes
    // Food instance
    this.food = new Food();
    / / instance
    this.snick = new Snick();
    // Stage elements
    this.map = map;
    // Assign this to that in the constructor
    that = this;
  }
  // Rewrite the gameobject prototype object
  Game.prototype = {
    // Execute the constructor
    constructor: Game,
    // Initialize the snake and food to the stage
    inIt: function () {
      this.food.reader(this.map);
      this.snick.reader(this.map);
      // Call the snake movement function to execute the snake movement and the game end operationsnickRun(); }}// The snake movement function is a private function that can only be called internally. Note that this is used instead of this
  function snickRun() {
    // Create a timer that repeats the snake movement and determines the end of the game
    var timer = setInterval(function () {
      // Execute the snake delete method
      that.snick.remove(that.map);
      // Execute the snake position update method
      that.snick.upDate();
      // Execute the snake render method
      that.snick.reader(that.map);
      // Calculate the maximum number of copies, when the snake head x or Y exceeds the maximum number of copies, the game ends
      var maxX = that.map.offsetWidth / that.snick.width;
      var maxY = that.map.offsetHeight / that.snick.height;
      // Determine the position of the snake head. Once it is found that the snake head exceeds the boundary, delete the timer and prompt the game to end
      if (that.snick.body[0].x < 0 || that.snick.body[0].x >= maxX || that.snick.body[0].y < 0 || that.snick.body[0].y >= maxY) {
        // Delete the timer
        clearInterval(timer);
        // The game is over
        alert('Game over'); }},150)}// Put the game constructor above the window
  window.Game = Game; }) ()// Test the code
// Get the stage
var map = document.getElementById('box');
// Create a game instance
var game = new Game(map);
// Perform game initialization
game.inIt();

Copy the code

Control the direction of the snake

We rely on the keyboard to control the direction of the snake’s movement, so we need to monitor the events of the keyboard press, identify the direction key press, and change the direction of the snake’s movement

// Listen for keyboard press events to change the direction of snake movement
  function chageDirection() {
    // Add the monitor keyboard press event to the entire page to determine the direction of the snake movement. Note that here to determine the original direction, do not set the reverse direction
    document.onkeydown = function (e) {
      // Get the value of the pressed key
      var code = e.keyCode;
      // Get the current direction
      var direction = that.snick.direction;
      // Change direction
      switch (code) {
        case 37:
          that.snick.direction = direction === 'right' ? direction : 'left';
          console.log(that.snick.direction);
          break;
        case 38:
          that.snick.direction = direction === 'down' ? direction : 'up';
          console.log(that.snick.direction);
          break;
        case 39:
          that.snick.direction = direction === 'left' ? direction : 'right';
          console.log(that.snick.direction);
          break;
        case 40:
          that.snick.direction = direction === 'up' ? direction : 'down';
          console.log(that.snick.direction); }}}Copy the code

The food disappears and the snake grows longer

Train of thought

1. Determine whether the positioning of the snake head is equal to the positioning of the food. If you want to wait, it means that they overlap and prove that you have eaten the food

2, after eating the food to generate new food, new food to perform the rendering method of food

3. To grow the snake after eating food, push a data in the snake’s body array. This data is the same as the data of the last item of the current snake

SnickRun () snickRun ()

// Determine whether the snake head overlaps with the food, and determine the location
if (that.snick.body[0].x * that.snick.width === that.food.left && that.snick.body[0].y * that.snick.height === that.food.top) {
  // If it overlaps
  // Delete the food
  that.food.remove(that.map, 0);
  // Create a new food
  that.food.reader(that.map);
  // Create a variable to get the last section of the snake
  var last = that.snick.body[that.snick.body.length - 1];
  // Add a section at the end of the snake
  that.snick.body.push({
    x: last.x,
    y: last.y,
    color: 'blue'
  });
}
Copy the code

Upgrade the number of food \ hit yourself game over

The snake grows longer when the food disappears

  1. We need to render multiple foods at initialization

  2. Each time we move, we need to determine whether the head of the snake is on top of each food item, and we need to walk through the food array

    //game.js
    inIt: function () {
        // Initializes adding three foods
        this.food.reader(this.map);
        this.food.reader(this.map);
        this.food.reader(this.map);
        this.snick.reader(this.map);
        // Call the snake movement function to execute the snake movement and the game end operation
        snickRun();
        // Executes the change direction function to listen for global keypress eventschageDirection(); }}function snickRun() {
        // Create a timer that repeats the snake movement and determines the end of the game
        var timer = setInterval(function () {
          // Execute the snake delete method
          that.snick.remove(that.map);
          // Execute the snake position update method
          that.snick.upDate();
          // Execute the snake render method
          that.snick.reader(that.map);
          // Calculate the maximum number of copies, when the snake head x or Y exceeds the maximum number of copies, the game ends
          var maxX = that.map.offsetWidth / that.snick.width;
          var maxY = that.map.offsetHeight / that.snick.height;
    
          // Multiple foods, traversing the food array to determine if it collided with the snake head
          for (let i = 0, l = that.food.items.length; i < l; i++) {
            // Get snakehead location and food location
            var headX = that.snick.body[0].x * that.snick.width,
              headY = that.snick.body[0].y * that.snick.height,
              foodX = parseFloat(that.food.items[i].style.left),
              foodY = parseFloat(that.food.items[i].style.top);
            // Determine the overlap between the snake head and the food
            if (headX === foodX && headY === foodY) {
              // Overlap delete food
              that.food.remove(that.map, i);
              // Create a new food
              that.food.reader(that.map);
              // Create a variable to get the last section of the snake
              var last = that.snick.body[that.snick.body.length - 1];
              // Add a section at the end of the snake
              that.snick.body.push({
                x: last.x,
                y: last.y,
                color: 'blue'}); }}// Determine the position of the snake head. Once it is found that the snake head exceeds the boundary, delete the timer and prompt the game to end
          if (that.snick.body[0].x < 0 || that.snick.body[0].x >= maxX || that.snick.body[0].y < 0 || that.snick.body[0].y >= maxY) {
            // Delete the timer
            clearInterval(timer);
            // The game is over
            alert('Game over');
          }
    
          // Add snake head touching body to end game
          // Run through all snake segments except the snake's head, and determine whether they collide
          for (let i = 1, l = that.snick.body.length; i < l; i++) {
            // Check whether there is a collision
            if (that.snick.body[0].x === that.snick.body[i].x && that.snick.body[0].y === that.snick.body[i].y) {
              // Delete the timer
              clearInterval(timer);
              // The game is over
              alert('Game over'); }}},150)}Copy the code

Final code implementation

Note: there is no use of code compression and merge code operations, there is no parameter to the self-calling function, you can perform later

index.html

<! DOCTYPEhtml>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
  <title>Document</title>
  <! -- Introducing styles -->
  <link rel="stylesheet" href="./css/index.css">
</head>

<body>
  <! -- Stage elements -->
  <div class="box" id="box"></div>
  <! Import external js files -->
  <script src="./js/tools.js"></script>
  <script src="./js/food.js"></script>
  <script src="./js/snick.js"></script>
  <script src="./js/game.js"></script>
  <script src="./js/index.js"></script>
</body>

</html>
Copy the code

Index.css

/* Style reset */
* {
  margin: 0;
  padding: 0;
}

/* Stage style */
.box {
  position: relative;
  width: 500px;
  height: 500px;
  background: #ccc;
}
Copy the code

Tools.js

// a tool object to store methods that will be used
var tools = {
  // Generate a random number
  getRandomNumber: function (min, max) {
    min = Math.ceil(min);
    max = Math.floor(max);
    return Math.floor(Math.random() * (max - min + 1) + min);
  },
  // Generate random colors
  getRandomColor: function () {
    return 'rgb(' + this.getRandomNumber(0.255) + ', ' + this.getRandomNumber(0.255) + ', ' + this.getRandomNumber(0.255) + ') '; }}Copy the code

food.js

// To avoid naming messy variables or functions globally, use the self-calling function (execute the function immediately) to define Food on window, followed by window. Food or simply call the constructor with Food
(function () {
  // Create a variable. Store the location property of the food, which is set to absolute location
  var ps = 'absolute';
  // Create a new constructor
  function Food(obj) {
    // To avoid incorrect formatting of data passed in by the user, use a judgment that if the user passed in data of object type, use it directly, otherwise use empty object
    obj = obj instanceof Object ? obj : {};
    // Set the values of the five properties and give them default values in case the user does not enter them
    / / width
    this.width = obj.width || 20;
    / / height
    this.height = obj.height || 20;
    //定位top
    this.top = obj.top || 0;
    / / position left
    this.left = obj.left || 0;
    / / the background color
    this.backgroundColor = obj.backgroundColor || 'skyblue';
    // Use an attribute to hold the element nodes that will be generated later, so that more than one food can be used later
    this.items = [];
  }
  // Redefine the Food prototype object
  Food.prototype = {
    // Point to Food manually
    constructor: Food,
    // Render method, create a new element and render it to the stage
    // Parameters: the stage to render to -- element, in this case box element
    reader: function (father) {
      // Sets the CSS style of the element
      // Random top positioning value: Using the random number method of the tool object Tools, to avoid the occurrence of dislocation first, we split the stage into small squares
      // Top = random number (0, stage height/food height -1) * food height
      this.top = tools.getRandomNumber(0, father.clientHeight / this.height - 1) * this.height;
      //left position value, same principle as above
      this.left = tools.getRandomNumber(0, father.clientWidth / this.width - 1) * this.width;
      // Create an element node
      var fooder = document.createElement('span');
      // Sets the element node attributes
      // Locate attributes and use variables to facilitate later modification
      fooder.style.position = ps;
      / wide/high
      fooder.style.width = this.width + 'px';
      fooder.style.height = this.height + 'px';
      // Locate the offset property
      fooder.style.top = this.top + 'px';
      fooder.style.left = this.left + 'px';
      / / the background color
      fooder.style.backgroundColor = this.backgroundColor;
      // Add elements to the stage
      father.appendChild(fooder);
      // Store elements
      this.items.push(fooder);
    },
    // Delete element method
    // Parameter 1: stage element
    // Argument 2: the subscript of the element to be deleted
    remove: function (father, index) {
      father.removeChild(this.items[index]);
      this.items.splice(index, 1); }}// Store the constructor inside window. Since window can be omitted, we can use Food directly
  window.Food = Food; }) ()Copy the code

Snick.js

// Add the snake constructor to the window, just like food
(function () {
  // Create a variable that represents the location of the snake node, set to absolute location
  var ps = 'absolute';

  // Snake constructor
  function Snick(opt) {
    // Determine if the passed argument is an object type, if yes, empty otherwise
    opt = opt instanceof Object ? opt : {};
    // Write the width and height properties and default values for the snake section
    this.width = opt.width || 20;
    this.height = opt.height || 20;
    // Put each section of the snake body into an array for later modification
    // Snake array: Each element inside is an object that stores the location properties and background color of the snake section
    this.body = [
        // The first one represents the head of the snake, and the rest represent each section of the snake's body. Note that the head and the body are prohibited in different colors
        {
          x: 3.y: 2.color: 'red'
        }, {
          x: 2.y: 2.color: 'blue'
        }, {
          x: 1.y: 2.color: 'blue'}].// The default movement direction of the snake is right, up, down, or left
      this.direction = 'right';
    // Create an array of snake nodes
    this.items = [];
  }
  // Redefine the constructor prototype object
  Snick.prototype = {
    // Point to the constructor manually
    constructor: Snick,
    // Render method
    // Parameters: stage elements
    reader: function (father) {
      // We need to render each snake section to the stage, so we need to traverse the entire snake array
      // Create two new variables to save reading this.body.length every time
      for (let i = 0, len = this.body.length; i < len; i++) {
        // Create a new element node
        var me = document.createElement('span');
        // Sets the inline style of the element node
        // Location mode
        me.style.position = ps;
        / wide/high
        me.style.width = this.width + 'px';
        me.style.height = this.height + 'px';
        // Locate the offset property
        me.style.top = this.body[i].y * this.height + 'px';
        me.style.left = this.body[i].x * this.width + 'px';
        // Background color
        me.style.backgroundColor = this.body[i].color;
        // Insert each section into the stage
        father.appendChild(me);
        this.items.push(me); }},// Delete the snake method
    // Parameters: stage elements
    remove: function (father) {
      // Iterate through the snake section array, removing each item from the stage element
      for (let i = 0, l = this.items.length; i < l; i++) {
        // Delete a node
        father.removeChild(this.items[i]);
      }
      // Reset the snake node array
      this.items = [];
    },
    // Snake node position update method
    upDate: function () {
      // Loop through the array of snakes, setting the location of each snake to the location of the previous one
      for (let 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;
      }
      // Modify the position of the snake head according to the direction of movement
      switch (this.direction) {
        case 'right':
          this.body[0].x += 1;
          break;
        case 'left':
          this.body[0].x -= 1;
          break;
        case 'up':
          this.body[0].y -= 1;
          break;
        case 'down':
          this.body[0].y += 1;
          break; }}}// Assign the entire constructor to the window
  window.Snick = Snick; }) ()Copy the code

Game.js

// Write to the self-calling function (immediately executed inside the function), again placing the constructor on the window object
(function () {
  // Define a variable that points to the constructor's this
  var that;
  // Gameobject constructor
  // Parameters: stage elements
  function Game(map) {
    // Three attributes
    // Food instance
    this.food = new Food();
    / / instance
    this.snick = new Snick();
    // Stage elements
    this.map = map;
    // Assign this to that in the constructor
    that = this;
  }
  // Rewrite the gameobject prototype object
  Game.prototype = {
    // Execute the constructor
    constructor: Game,
    // Initialize the snake and food to the stage
    inIt: function () {
      // Initializes adding three foods
      this.food.reader(this.map);
      this.food.reader(this.map);
      this.food.reader(this.map);
      this.snick.reader(this.map);
      // Call the snake movement function to execute the snake movement and the game end operation
      snickRun();
      // Executes the change direction function to listen for global keypress eventschageDirection(); }}// The snake movement function
  function snickRun() {
    // Create a timer that repeats the snake movement and determines the end of the game
    var timer = setInterval(function () {
      // Execute the snake delete method
      that.snick.remove(that.map);
      // Execute the snake position update method
      that.snick.upDate();
      // Execute the snake render method
      that.snick.reader(that.map);
      // Calculate the maximum number of copies, when the snake head x or Y exceeds the maximum number of copies, the game ends
      var maxX = that.map.offsetWidth / that.snick.width;
      var maxY = that.map.offsetHeight / that.snick.height;

      // Multiple foods, traversing the food array to determine if it collided with the snake head
      for (let i = 0, l = that.food.items.length; i < l; i++) {
        // Get snakehead location and food location
        var headX = that.snick.body[0].x * that.snick.width,
          headY = that.snick.body[0].y * that.snick.height,
          foodX = parseFloat(that.food.items[i].style.left),
          foodY = parseFloat(that.food.items[i].style.top);
        // Determine the overlap between the snake head and the food
        if (headX === foodX && headY === foodY) {
          // Overlap delete food
          that.food.remove(that.map, i);
          // Create a new food
          that.food.reader(that.map);
          // Create a variable to get the last section of the snake
          var last = that.snick.body[that.snick.body.length - 1];
          // Add a section at the end of the snake
          that.snick.body.push({
            x: last.x,
            y: last.y,
            color: 'blue'}); }}// Determine the position of the snake head. Once it is found that the snake head exceeds the boundary, delete the timer and prompt the game to end
      if (that.snick.body[0].x < 0 || that.snick.body[0].x >= maxX || that.snick.body[0].y < 0 || that.snick.body[0].y >= maxY) {
        // Delete the timer
        clearInterval(timer);
        // The game is over
        alert('Game over');
      }

      // Add snake head touching body to end game
      // Run through all snake segments except the snake's head, and determine whether they collide
      for (let i = 1, l = that.snick.body.length; i < l; i++) {
        // Check whether there is a collision
        if (that.snick.body[0].x === that.snick.body[i].x && that.snick.body[0].y === that.snick.body[i].y) {
          // Delete the timer
          clearInterval(timer);
          // The game is over
          alert('Game over'); }}},150)}// Listen for keyboard press events to change the direction of snake movement
  function chageDirection() {
    // Add the monitor keyboard press event to the entire page to determine the direction of the snake movement. Note that here to determine the original direction, do not set the reverse direction
    document.onkeydown = function (e) {
      // Get the value of the pressed key
      var code = e.keyCode;
      // Get the current direction
      var direction = that.snick.direction;
      // Change direction
      switch (code) {
        case 37:
          that.snick.direction = direction === 'right' ? direction : 'left';
          console.log(that.snick.direction);
          break;
        case 38:
          that.snick.direction = direction === 'down' ? direction : 'up';
          console.log(that.snick.direction);
          break;
        case 39:
          that.snick.direction = direction === 'left' ? direction : 'right';
          console.log(that.snick.direction);
          break;
        case 40:
          that.snick.direction = direction === 'up' ? direction : 'down';
          console.log(that.snick.direction); }}}// Put the game constructor above the window
  window.Game = Game; }) ()Copy the code

index.js

(function () {
  // Get the stage
  var map = document.getElementById('box');
  // Create a game instance
  var game = new Game(map);
  // Perform game initializationgame.inIt(); }) ()Copy the code

Performance optimization

Reduce the number of SENDING HTTP requests

Put all the code into one file and pay attention to the order of reference

Compressed JS code

You can use the online – tool.oschina.net/jscompress/ compression tools

Problems with self-calling functions

This can cause problems if two self-calling functions are next to each other without a semicolon in between

Solution: Add a semicolon after or before a self-calling function. The best way is to add a semicolon before a self-calling function

Self-calling function arguments

  • In the previous code, self-executing functions can add parameters and arguments to window
  • Undefined may change in older browsers, and wrapping undefined in self-calling functions prevents it from being tampered with

Such as:

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