canvas手寫辨色力小遊戲

canvas手寫辨色力小遊戲

前言

​ 前段時間看到掘金上有個es6手寫辨色了遊戲,以爲頗有意思,做者使用dom操做實現的遊戲邏輯,感受能夠用canvas實現,效率更高,因而閒着沒事,手寫了一個canvas版的辨色小遊戲,具體效果以下:javascript

界面寫得醜,忘輕噴。。。java

繪製遊戲格子

首先咱們須要準備一張畫布,git

var canvas = document.getElementById('canvas');
if (!canvas.getContext('2d')) {
    alert('你的瀏覽器不支持canvas,請換個瀏覽器試試');
}
var ctx = canvas.getContext('2d');
複製代碼

而後我會定義一個Rect的方塊類,這個方塊須要具有位置,寬高,填充顏色幾個屬性,根據canvas繪製矩形的api,咱們能夠用如下api繪製矩形es6

fillRect(x, y, width, height)        //填充矩形
strokeRect(x, y, width, height)       //矩形描邊
clearRect(x, y, width, height)       //清除畫布,清除部分徹底透明

rect(x, y, width, height)           //矩形路徑,須要配和fill和stroke
複製代碼

可是爲了後面事件監聽更方便,咱們這裏不使用fillRect方法,咱們使用繪製線段的方法moveTo和lineTo來繪製,github

首先經過Rect具備一個繪製路徑的函數:canvas

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);
            }
        },
複製代碼

首先經過位置和寬高構造四個點,而後在經過moveTo和lineTo構造路徑,路徑構造好後咱們須要繪製到畫布上,所以還須要一個draw函數繪製:api

draw: function () {
            ctx.save();
            ctx.fillStyle = this.fillStyle;
            ctx.beginPath();
            this.createPath();
            ctx.closePath();
            ctx.stroke();
            ctx.fill();
            ctx.restore();
        },
複製代碼

方塊類定義好後,咱們開始定義顏色函數,顏色邏輯 參考這篇文章數組

/** * 根據關卡等級返回相應的通常顏色和特殊顏色 * @param {number} step 關卡級別 */
    function getColor(step) {
        // rgb 隨機加減 random
        let random = Math.floor(100 / step);

        // 獲取隨機通常顏色,拆分三色值
        let color = randomColor(17, 255),
            m = color.match(/[\da-z]{2}/g);

        // 轉化爲 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 ['#' + color, '#' + specialColor];
    }

    /** * 獲取隨機顏色相近的 rgb 三色值 * @param {number} num 單色值 * @param {number} random 隨機加減的數值 */
    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";
        }
    }
	/** * 隨機顏色 * @param {number} min 最小值 * @param {number} 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;
    }
複製代碼

具體邏輯參考代碼,瀏覽器

而後咱們開始new小方塊,而且繪製到畫布上,bash

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);
    }
}
複製代碼

這樣咱們基本就完成了遊戲的大概了。

添加事件

小方塊已經繪製好了,那麼接下來咱們來是實現遊戲的關鍵點,那就是交互,咱們如何過去到鼠標點擊的小方塊呢,這個小方塊只是canvas畫布上的一張圖,並不能直接像dom同樣添加事件監聽,這或許就是這個遊戲有意思的地方,那麼canvas上有沒有什麼方法能讓咱們知道咱們具體點擊的是哪一個小方塊呢?搜搜MDN,果真有一個方法能夠判斷點是否在路徑上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);
複製代碼

isPointInPath方法返回一個Boolean值,當檢測點包含在當前或指定的路徑內,返回 true;不然返回 false。

具體方法的使用請參考MDN

咱們首先獲取到鼠標點擊canvas的座標點:

/** * @param {} canvas * @param {} x * @param {} y * @description 將鼠標位置定位到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)
        };
    }
複製代碼

獲取到鼠標位置後換算成canvas的相對位置,而後咱們給rect類添加一個新方法判斷點是否在當前路徑內,

/** * @param {Object} p {x: num, y: num} * @description 判斷點是否在這個路徑上, 構造路徑利用isPointInPath判斷點是否在此路徑上不用繪製到canvas上 */
        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;
        }
複製代碼

這就是爲何一開始咱們沒有使用fillRect方法繪製矩形,由於fillRect方法會繪製到畫布上,然而咱們只是須要構造路徑,來使用canvas的isPointInPath方法,而不是要繪製到畫布上,所以這裏咱們巧妙的經過moveTo和lineTo構造路徑,而後判斷當前點是否在這個小方塊內,還記得咱們在實例化小方塊的時候咱們把全部小方塊都存到一個datas的數組裏嗎?咱們經過遍歷datas數組而且判斷點是否在方塊內,這樣咱們就能實現獲取點擊的具體的某一個小方塊了,

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;
        }
    })
複製代碼

而後咱們就能實現下一關的操做,從新再繪製遊戲網格,從新生成新的顏色和位置了。

總結

經過這個小遊戲,能夠學習到,canvas點擊事件的監聽和實現,由於canvas只是一張畫布是一個狀態機,無法經過dom同樣直接操做,可是經過一些「奇淫巧技」咱們仍是能達到咱們的目的。後續我還會繼續不按期更新一些canvas的文章,歡迎你們一塊兒探討和學習。

項目地址:github.com/lspCoder/ca…

還看得過去的但願各位看官給個star。

相關文章
相關標籤/搜索