Canvas handwritten color discrimination small game

preface

Some time ago, I saw an ES6 handwritten color discrimination game on Nuggets, which was very interesting. The author felt that the game logic realized by DOM operation could be realized by Canvas, which would be more efficient, so he wrote a small color discrimination game of Canvas version by hand, and the specific effects are as follows:

Interface write ugly, forget to lightly spray…

Draw a game grid

First we need to prepare a canvas,

var canvas = document.getElementById('canvas');
if(! canvas.getContext('2d')) {
    alert('Your browser doesn't support Canvas, please try another browser');
}
var ctx = canvas.getContext('2d');
Copy the code

Then I will define a Rect square class, which needs to have several properties such as position, width, height and fill color. According to the Canvas drawing rectangle API, we can use the following API to draw the rectangle

FillRect (x, y, width, height) // Clear the canvas, Clear part of completely transparent rect(x, y, width, height) // rectangle path with fill and strokeCopy the code

But instead of using fillRect, we use moveTo and lineTo to draw a line segment,

First through Rect has a function to draw a path:

	getPoints: function () {
            var p1 = { x: this.x, y: this.y };
            var p2 = { x: this.x + this.width, y: this.y };
            var p3 = { x: this.x + this.width, y: this.y + this.height };
            var p4 = { x: this.x, y: this.y + this.height };
            this.points = [p1, p2, p3, p4];
            return this.points;
        },

        createPath: function () {
            var points = this.getPoints();
            points.forEach(function (point, i) {
                ctx[i == 0 ? 'moveTo' : 'lineTo'](point.x, point.y);
            })
            if (this.closed) {
                ctx.lineTo(this.points[0].x, this.points[0].y); }},Copy the code

First we construct the four points by position and width and height. Then we construct the path through moveTo and lineTo. Once the path is constructed, we need to draw it onto the canvas, so we also need to draw with a draw function:

	draw: function () {
            ctx.save();
            ctx.fillStyle = this.fillStyle;
            ctx.beginPath();
            this.createPath();
            ctx.closePath();
            ctx.stroke();
            ctx.fill();
            ctx.restore();
        },
Copy the code

After the square class is defined, we start to define the color function. The color logic refers to this article;

    /** * Return normal and special colors according to level * @param {number} step level */
    function getColor(step) {
        // RGB random plus or minus random
        let random = Math.floor(100 / step);

        // Get random general color, split tri-color value
        let color = randomColor(17.255),
            m = color.match(/[\da-z]{2}/g);

        // Convert to base 10
        for (let i = 0; i < m.length; i++) m[i] = parseInt(m[i], 16); //rgb
        let specialColor =
            getRandomColorNumber(m[0], random) +
            getRandomColorNumber(m[1], random) +
            getRandomColorNumber(m[2], random);
        return [The '#' + color, The '#' + specialColor];
    }

    @param {number} num Monochromatic value @param {number} random random plus or minus value */
    function getRandomColorNumber(num, random) {
        let temp = Math.floor(num + (Math.random() < 0.5 ? - 1 : 1) * random);
        if (temp > 255) {
            return "ff";
        } else if (temp > 16) {
            return temp.toString(16);
        } else if (temp > 0) {
            return "0" + temp.toString(16);
        } else {
            return "00"; }}/** * Random color * @param {number} min min * @param {number} Max Max */
    function randomColor(min, max) {
        var r = randomNum(min, max).toString(16);
        var g = randomNum(min, max).toString(16);
        var b = randomNum(min, max).toString(16);
        return r + g + b;
    }
Copy the code

Specific logical reference code,

And then we start new little squares and draw them on the canvas,

var blockWidth = ((500 / col).toFixed(2) * 500 - 1) / 500;
var randomCol = Math.floor(col * Math.random());
var randomCell = Math.floor(col * Math.random());

var colorObj = getColor(step);

for (var i = 0; i < col ; i++) {
    for (var j = 0; j < col; j++) {
        var rect = new Rect({
            x: (blockWidth + 5) * i + (canvas.width - blockWidth * col - (col - 1) * 5) / 2.y: (blockWidth + 5) * j + (canvas.width - blockWidth * col - (col - 1) * 5) / 2.width: blockWidth,
            height: blockWidth,
            fillStyle: colorObj[0]});if (i == randomCol && j == randomCell) {
            rect.updateStyle(colorObj[1]); } rect.draw(); datas.push(rect); }}Copy the code

So we’re pretty much done with the game.

Add event

The small square has been drawn, so now we are going to realize the key point of the game, that is interaction, how do we get to the small square of mouse click, this small square is just a picture on canvas, can not directly add event listening like DOM, this may be the interesting part of the game. So is there any way on the canvas to tell us which square we’re clicking on? Search MDN, there is a way to determine whether the point is on the path isPointInPath();

boolean ctx.isPointInPath(x, y);
boolean ctx.isPointInPath(x, y, fillRule);

boolean ctx.isPointInPath(path, x, y);
boolean ctx.isPointInPath(path, x, y, fillRule);
Copy the code

The isPointInPath method returns a Boolean that returns true if the checkpoint is contained in the current or specified path; Otherwise return false.

For details, see MDN

We first get the coordinate point of the mouse click on canvas:

/** * @param {} canvas * @param {} x * @param {} y * @description moves the mouse to canvas */
    function WindowToCanvas(canvas, x, y) {
        var box = canvas.getBoundingClientRect();
        return {
            x: x - box.left * (canvas.width / box.width),
            y: y - box.top * (canvas.height / box.height)
        };
    }
Copy the code

After obtaining the mouse position, convert it to the relative position of canvas. Then we add a new method to rect class to determine whether the point is in the current path.

/** * @param {Object} p {x: num, y: num} * @description specifies whether a point is on this path
        isPointInPath: function (p) {
            var isIn = false;
            ctx.save();
            ctx.beginPath();
            this.createPath();
            if (ctx.isPointInPath(p.x, p.y)) {
                isIn = true;
            }
            ctx.closePath();
            ctx.restore();
            return isIn;
        }
Copy the code

That’s why we didn’t use fillRect to draw the rectangle in the first place, because fillRect would draw to the canvas, but we just needed to construct a path to use canvas’s isPointInPath method, instead of drawing to the canvas, So here we cleverly construct paths with moveTo and lineTo, and then determine if the current point is in the little box. Remember when we instantiated the little box we stored all the little boxes in an array of datAs? We can get the specific little square of the click by iterating through the datas array and determining if the point is in the square,

canvas.addEventListener('mousemove'.function (e) {
        var pos = WindowToCanvas(canvas, e.clientX, e.clientY);
        for (var i = 0; i < datas.length; i++) {
            var rect = datas[i];
            if (rect.isPointInPath(pos) && rect.isSpecial) {
                isOn = true;
                break;
            } else {
                isOn = false; }}},false);

    canvas.addEventListener('click'.function (e) {
        if(! START)return;
        if(isOn) { drawGame(); score++; scoreDom.innerText = score; }})Copy the code

Then we can move on to the next level, redrawing the game grid and generating new colors and positions.

conclusion

Through this small game, we can learn the monitoring and implementation of canvas click events, because canvas is just a canvas and a state machine, which cannot be operated directly like DOM. However, we can still achieve our goal through some “strange and clever techniques”. I will continue to update some Canvas articles from time to time. Welcome to discuss and learn together.

Project address: github.com/lspCoder/ca…

I hope you can give me a star.