The technology used

  1. es6
  2. canvas

canvas

The Canvas API provides a way to draw graphics using JavaScript and HTML elements. It can be used for animation, game graphics, data visualization, image editing, and real-time video processing.

I met a canvas

<! DOCTYPEhtml>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="Width = device - width, initial - scale = 1.0, the maximum - scale = 1, minimum - scale = 1, the user - scalable = no"
    />
    <title>First canvas. HTML</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
      canvas {
        background-color: aqua;
      }
    </style>
  </head>
  <body>
    <canvas></canvas>
  </body>
</html>

Copy the code

It can be seen that Canvas is an inline element with a default width and height of 300*150.

Draw a straight line lineTo

The following code draws a diagonal line

      // 1 gets the DOM element
      const canvas = document.querySelector('canvas');
      // 2 Get the canvas object
      const ctx = canvas.getContext('2d');
      // 3 Set the starting point
      ctx.moveTo(0.1);
      // 4 Set the destination
      ctx.lineTo(300.150);
      / / 5 stroke
      ctx.stroke();
Copy the code

Resize canvas

Note that if you want to change the width and height of the Canvas tag, you cannot change it directly in CSS, otherwise you will stretch the canvas. You can only change width and height on the attributes of the tag

The wrong sample

  canvas {
    width: 600px;
    height: 300px;
  }
Copy the code


The correct sample

<canvas width="600" height="300"></canvas>
Copy the code

Modify the strokeStyle color

When we don’t want the default black line, we can use strokeStyle to change the color

  ctx.strokeStyle = 'pink';
  ctx.stroke();
Copy the code

Draw arc

  // Arc (center x, center y, radius length, start radian, end radian)
  ctx.arc(300.150.100.0.Math.PI * 2);

  ctx.stroke();
Copy the code

It can also be changed to a filled arc fill

ctx.arc(300.150.100.0.Math.PI * 2);
ctx.fill();
Copy the code

If you want to change the color of the fill, use fillStyle

ctx.fillStyle = 'yellow';
ctx.fill();
Copy the code

show time

In the following code example, the annotated representation is new code

Set up static structures

<! DOCTYPEhtml>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="Width = device - width, initial - scale = 1.0, the maximum - scale = 1, minimum - scale = 1, the user - scalable = no"
    />
    <title>gobang</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
      body {
        text-align: center;
        height: 100vh;
      }
      canvas {
        /* You can write your own background color instead of the following four lines */
        background-image: url(./images/20211227092309212.jpg);
        background-repeat: no-repeat;
        background-size: cover;
        background-position: center;
      }
    </style>
  </head>
  <body>
    <canvas></canvas>
  </body>
</html>
Copy the code

Create a new class and declare related methods

In the following code example, the annotated representation is new code

There are four main functions

  1. Initialize related properties such as checkerboard width and height
  2. Declares a method for drawing a chess board
  3. Declares the method of clicking on the board to play chess
  4. Declare how to judge victory in chess
      class Gobang {
        /** constructor */
        constructor() {}
        /** Initialize the canvas */
        init() {}
        /** draw the board */
        drawCheckerBroad() {}
        /** Canvas bind click event to implement chess function */
        checkerBroadEvent() {}
        /** determine whether the victory ** /
        victory(){}}Copy the code

Initialize the canvas

In the following code example, the annotated representation is new code

The attributes that need to be initialized are

The requirement is that you want a board with 18 by 18 squares, so you need 19 lines, each of which is 20 by 20

  1. this.canvasUsed to bind click events later
  2. this.checkerBoradWidthUsed to declare the size of a grid
  3. this.lineNumsUsed to declare how many lines to draw on a checkerboard
  4. this.blaorwhUsed to declare whether the current chess player is yellow or white
  5. this.chessMartrixsCheckerboard position matrix, used to record the positions of chess pieces and determine victory
    init() {
      // The width of the grid
      this.checkerBoradWidth = 20;
      this.lineNums = 19;
      // White or yellow is yellow
      this.blaorwh = 'yellow';
      // Store the positions of the chess pieces in the two-dimensional array 19*19
      this.chessMartrixs = [];
      for (let i = 0; i <= this.lineNums; i++) {
        this.chessMartrixs.push(
          new Array(this.lineNums).fill(0.0.this.lineNums)
        );
      }

      this.canvas = document.querySelector('canvas');
      // Set the canvas size to exactly the size of 20 squares
      this.canvas.width = this.checkerBoradWidth * (this.lineNums - 1);
      this.canvas.height = this.checkerBoradWidth * (this.lineNums - 1);
      this.ctx = this.canvas.getContext('2d');
    }
Copy the code

Describe the board

In the following code example, the annotated representation is new code

The requirement is that you want a board with 18 by 18 squares, so you need 19 lines, each of which is 20 by 20

      // Create a new path
      this.ctx.beginPath();
      // Set the line color
      this.ctx.strokeStyle = '#ccc';
      // Walk over every line on the drawing board
      for (let i = 0; i < this.lineNums; i++) {
        // Draw the horizontal line checkerBoradWidth is the width of the checkerboard grid
        this.ctx.moveTo(0, i * this.checkerBoradWidth);
        this.ctx.lineTo(
          // lineNums is the number of lines
          this.lineNums * this.checkerBoradWidth,
          i * this.checkerBoradWidth
        );
        // Draw vertical lines
        this.ctx.moveTo(i * this.checkerBoradWidth, 0);
        this.ctx.lineTo(
          i * this.checkerBoradWidth,
          this.lineNums * this.checkerBoradWidth
        );
      }
      this.ctx.stroke();
Copy the code

We then call the methods that initialize the canvas and draw the checkerboard in the constructor, and instantiate them

constructor() {
  this.init();
  this.drawCheckerBroad();
}
Copy the code

instantiation

const gobang = new Gobang();
Copy the code

In effect

Click on the board to play chess

In the following code example, the annotated representation is new code

Bind the click event to the Canvas tag, get the mouse coordinate, and then draw the circle in place

    checkerBroadEvent() {
      // Be careful to use the arrow function, otherwise it can lead to this pointing problem
      this.canvas.addEventListener('click'.(e) = > {
        // Get the coordinates of the mouse in the canvas
        const x = e.offsetX;
        const y = e.offsetY;
        // Each click reopens the path
        this.ctx.beginPath();
        // Draw the chess pieces according to the clicked coordinates
        this.ctx.arc(x, y, this.checkerBoradWidth / 2.0.2 * Math.PI);

        // Determine white or black
        if (this.blaorwh === 'yellow') {
          / / yellow
          this.ctx.fillStyle = 'yellow';
        } else {
          // White stroke is easy to confuse with background
          this.ctx.fillStyle = '#fff';
          this.ctx.stroke();
        }
        this.ctx.fill();
        this.blaorwh = this.blaorwh === 'yellow' ? 'white' : 'yellow';
      });
    }
Copy the code

It is also called in the constructor

    constructor() {
      this.init();
      this.drawCheckerBroad();
      this.checkerBroadEvent(); // Click the mouse to play chess
    }
Copy the code

Two problems arise

  1. The chess position is not in the center of the line segment, it may be skewed

  2. The same position, can put countless pieces (need to determine whether there are already pieces)

We need to fix it

Fixed standard positions in chess

In the following code example, the annotated representation is new code

Mainly by judging the current chess coordinate position and horizontal or vertical direction on which line segment position close, then set chess position is equal to the distance from the nearest line segment coordinates.

        /** Canvas bind click event to implement chess function */
        checkerBroadEvent() {
          // Be careful to use the arrow function, otherwise it can lead to this pointing problem
          this.canvas.addEventListener('click'.(e) = > {
            const x = e.offsetX;
            const y = e.offsetY;
            // To set the center of the arc, add 20px for each grid
            let arcX, arcY;
            // it is used to record the coordinate points such as (0,1), (1,3), etc
            for (let i = 0; i <= this.lineNums; i++) {
              // Determine the horizontal direction of the mouse's x coordinate
              if (
                Math.abs(x - i * this.checkerBoradWidth) <=
                this.checkerBoradWidth / 2
              ) {
                arcX = i * this.checkerBoradWidth;
              }
              // Determine which side line the mouse is perpendicular to
              if (
                Math.abs(y - i * this.checkerBoradWidth) <=
                this.checkerBoradWidth / 2
              ) {
                arcY = i * this.checkerBoradWidth; }}// If another location is selected, return directly
            if (arcX === undefined || arcY === undefined) return;

            this.ctx.beginPath();
            //...
          });
        }
Copy the code

Check if there are already chess pieces in the current position

In the following code example, the annotated representation is new code

Use a two-dimensional array this.checkerBoradWidth to record the coordinate position of the chess, and determine whether there are pieces in the current position when the chess is played again

   checkerBroadEvent() {
          this.canvas.addEventListener('click'.(e) = > {
            const x = e.offsetX;
            const y = e.offsetY;
            let arcX, arcY;
            Record the position of the current chess piece in the chess matrix
            let chessX, chessY;
            for (let i = 0; i <= this.lineNums; i++) {
              if (
                Math.abs(x - i * this.checkerBoradWidth) <=
                this.checkerBoradWidth / 2
              ) {
                arcX = i * this.checkerBoradWidth;
                // Record the position of the current piece
                chessX = i;
              }
              if (
                Math.abs(y - i * this.checkerBoradWidth) <=
                this.checkerBoradWidth / 2
              ) {
                arcY = i * this.checkerBoradWidth;
                // Record the position of the current piecechessY = i; }}// If another location is selected, return directly
            if (arcX === undefined || arcY === undefined) return;

            // If someone did, return
            if (this.chessMartrixs[chessY][chessX]) return;
            // Store a copy of the current chess position in the matrix
            this.chessMartrixs[chessY][chessX] = this.blaorwh;
            this.ctx.beginPath();
            this.ctx.arc(
              arcX,
              arcY,
              this.checkerBoradWidth / 2.0.2 * Math.PI
            );

         
            if (this.blaorwh === 'yellow') {
              this.ctx.fillStyle = 'yellow';
            } else {
              this.ctx.fillStyle = '#fff';
              this.ctx.stroke();
            }

            this.ctx.fill();
            this.blaorwh = this.blaorwh === 'yellow' ? 'white' : 'yellow';
          });
        }
Copy the code

Judge the winner. – Point

Our approach to determining whether a player has won is as follows

Victory is represented by 5 pieces of the same color on any of the following four axes (which are connected by two short axes)

Looking at the diagram above, a rule emerges: We need to set up a key array to determine whether the pieces are connected

[[-1, -1], [...1.0], [1, -1], [0.1]]
Copy the code
  1. Let’s say we iterate over the array above let’s say we get the first element [-1,-1]

  2. At the same time, when we take the inverse of [-1,-1], we also get [1,1], and then we can determine a long axis

  3. At this point, we take [-1,-1] separately open traversal, calculate the number of pieces in this direction

  4. At the same time, we take [1,1] also began to traverse, calculate the number of pieces in this direction

  5. If they add up to 5 or more, victory is declared

code

        /** * Determine whether a contestant has won */
        victory(matrix, x, y, borw) {
          // Determine the four axes of victory conditions
          let victoryUnits = [
            [-1, -1], [...1.0],
            [1, -1],
            [0.1]];const getLineNums = (matrix, direcsx, x, y, borw) = > {
            // Count the number of chessmen of the same color
            let results = 0;
            for (let i = 0; i < 5; i++) {
              const vicx = x + i * direcsx[0];
              const vicy = y + i * direcsx[1];
              // Check that the position is not exceeded
              if( matrix[vicy] ! = =undefined&& matrix[vicy][vicx] ! = =undefined &&
                borw === matrix[vicy][vicx]
              ) {
                // Check whether it is equal to the current color
                results++;
              } else {
                break; }}return results;
          };

          // Determine victory
          const result = victoryUnits.some((item) = > {
            // Count the number of pieces of the same color on one side of the short axis
            const n1 = getLineNums(matrix, item, x, y, borw);
            // Count the number of pieces of the same color on the other short axis
            const n2 = getLineNums(
              matrix,
              item.map((v) = > -v),
              x,
              y,
              borw
            );
            // Because I am one of them
            return n1 + n2 - 1> =5;
          });
          return result;
        }
Copy the code

The complete code

<! DOCTYPEhtml>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta
      name="viewport"
      content="Width = device - width, initial - scale = 1.0, the maximum - scale = 1, minimum - scale = 1, the user - scalable = no"
    />
    <title>H5 - gobang</title>
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }
      body {
        text-align: center;
        height: 100vh;
      }
      canvas {
        background-image: url(./images/20211227092309212.jpg);
        background-repeat: no-repeat;
        background-size: cover;
        background-position: center;
      }
    </style>
  </head>
  <body>
    <canvas></canvas>
    <script>
      class Gobang {
        constructor() {
          this.init();
          this.drawCheckerBroad();
          this.checkerBroadEvent();
        }

        init() {
          this.checkerBoradWidth = 20;
          this.lineNums = 19;
          this.blaorwh = 'yellow';
          this.chessMartrixs = [];
          for (let i = 0; i <= this.lineNums; i++) {
            this.chessMartrixs.push(
              new Array(this.lineNums).fill(0.0.this.lineNums)
            );
          }

          this.canvas = document.querySelector('canvas');
          this.canvas.width = this.checkerBoradWidth * (this.lineNums - 1);
          this.canvas.height = this.checkerBoradWidth * (this.lineNums - 1);
          this.ctx = this.canvas.getContext('2d');
        }

        drawCheckerBroad() {
          this.ctx.beginPath();
          this.ctx.strokeStyle = '#ccc';
          for (let i = 0; i < this.lineNums; i++) {
            this.ctx.moveTo(i * this.checkerBoradWidth, 0);
            this.ctx.lineTo(
              i * this.checkerBoradWidth,
              this.lineNums * this.checkerBoradWidth
            );
            this.ctx.moveTo(0, i * this.checkerBoradWidth);
            this.ctx.lineTo(
              this.lineNums * this.checkerBoradWidth,
              i * this.checkerBoradWidth
            );
          }
          this.ctx.stroke();
        }

        checkerBroadEvent() {
          this.canvas.addEventListener('click'.(e) = > {
            const x = e.offsetX;
            const y = e.offsetY;
            let arcX, arcY;
            let chessX, chessY;
            for (let i = 0; i <= this.lineNums; i++) {
              if (
                Math.abs(x - i * this.checkerBoradWidth) <=
                this.checkerBoradWidth / 2
              ) {
                arcX = i * this.checkerBoradWidth;
                chessX = i;
              }
              if (
                Math.abs(y - i * this.checkerBoradWidth) <=
                this.checkerBoradWidth / 2
              ) {
                arcY = i * this.checkerBoradWidth; chessY = i; }}if (arcX === undefined || arcY === undefined) return;

            if (this.chessMartrixs[chessY][chessX]) return;
            this.chessMartrixs[chessY][chessX] = this.blaorwh;
            this.ctx.beginPath();
            this.ctx.arc(
              arcX,
              arcY,
              this.checkerBoradWidth / 2.0.2 * Math.PI
            );

            if (this.blaorwh === 'yellow') {
              this.ctx.fillStyle = 'yellow';
            } else {
              this.ctx.fillStyle = '#fff';
              this.ctx.stroke();
            }

            this.ctx.fill();
            if (
              this.victory(this.chessMartrixs, chessX, chessY, this.blaorwh)
            ) {
              console.log(this.blaorwh + 'victory');
              return;
            }
            this.blaorwh = this.blaorwh === 'yellow' ? 'white' : 'yellow';
          });
        }

        victory(matrix, x, y, borw) {
          let victoryUnits = [
            [-1, -1], [...1.0],
            [1, -1],
            [0.1]];const getLineNums = (matrix, direcsx, x, y, borw) = > {
            let results = 0;
            for (let i = 0; i < 5; i++) {
              const vicx = x + i * direcsx[0];
              const vicy = y + i * direcsx[1];
              if( matrix[vicy] ! = =undefined&& matrix[vicy][vicx] ! = =undefined &&
                borw === matrix[vicy][vicx]
              ) {
                results++;
              } else {
                break; }}return results;
          };

          const result = victoryUnits.some((item) = > {
            const n1 = getLineNums(matrix, item, x, y, borw);
            const n2 = getLineNums(
              matrix,
              item.map((v) = > -v),
              x,
              y,
              borw
            );
            return n1 + n2 - 1> =5;
          });
          returnresult; }}const gobang = new Gobang();
    </script>
  </body>
</html>

Copy the code