iT邦幫忙

2021 iThome 鐵人賽

DAY 26
1
Modern Web

Canvas 小錦囊系列 第 26

Day 26 - 用canvas 做顏色遊戲

連假過得太快了吧

無法接受... /images/emoticon/emoticon02.gif

今天來玩個判斷不同顏色的遊戲

codesendbox

import React, { useEffect, useRef, useState } from "react";

const width = 500;
const height = 500;

function ColorGame() {
  const canvasRef = useRef(null);
  const [canvas, setCanvas] = useState(null);
  const [ctx, setCtx] = useState(null);
  useEffect(() => {
    if (canvasRef.current) {
      const c = canvasRef.current;
      setCanvas(c);
      setCtx(c.getContext("2d"));
    }
  }, [canvasRef]);

  let datas = [];
  let step = 0;

  let TIME = 30;
  let timeDom = document.getElementById("time");
  timeDom = { innerText: TIME };

  let score = 0;
  let scoreDom = document.getElementById("score");
  scoreDom = { innerText: score };

  let START = false;
  let timer;

  function startGame() {
    START = true;
    datas = [];
    step = 0;
    score = 0;
    TIME = 30;

    drawGame();

    if (!timer) {
      timer = window.setInterval(function () {
        timeDom.innerText = TIME--;
        if (TIME === -1) {
          clearInterval(timer);
          gameOver();
        }
      }, 1000);
    }
  }

  function gameOver() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    var text = "Game Over!";
    ctx.fillStyle = "red";
    ctx.font = "50px sans-serif";
    var measure = ctx.measureText(text);
    ctx.fillText(
      text,
      (canvas.width - measure.width) / 2,
      (canvas.height - 50) / 2
    );

    START = false;
  }

  /**
   * @param  {} canvas
   * @param  {} x
   * @param  {} y
   * @description 定位滑鼠位置
   */
  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)
    };
  }

  /**
   * 根據等級給顏色
   * @param {number} step
   */
  function getColor(step) {
    // 隨機顏色
    let random = Math.floor(100 / step);
    let color = randomColor(17, 255),
      m = color.match(/[\da-z]{2}/g);

    // 轉十進位
    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];
  }

  /**
   * 隨機取相近色
   * @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;
  }
  /**
   * 隨機數量
   * @param {number} min 最小值
   * @param {number} max 最大值
   */
  function randomNum(min, max) {
    return Math.floor(Math.random() * (max - min) + min);
  }

  var isOn = false;

  var Rect = function (opt) {
    this.x = opt.x;
    this.y = opt.y;
    this.width = opt.width;
    this.height = opt.height;
    this.fillStyle = opt.fillStyle;
  };

  Rect.prototype = {
    constructor: Rect,

    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);
      }
    },

    updateStyle: function (fillStyle) {
      this.fillStyle = fillStyle;
      this.isSpecial = true;
      return this;
    },

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

    /**
     * @param  {Object} p {x: num, y: num}
     * @description 判斷使否在範圍內上色
     */
    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;
    }
  };

  function drawGame() {
    datas = []; //清空狀態
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    step++;
    let col; // 設置列數
    if (step < 6) {
      col = step + 1;
    } else if (step < 12) {
      col = Math.floor(step / 2) * 2;
    } else if (step < 18) {
      col = Math.floor(step / 3) * 3;
    } else {
      col = 16;
    }

    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);
      }
    }
  }

  const handleStar = () => {
    startGame();
  };

  const handleMouseMove = (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;
      }
    }
  };

  const handleClickCanvas = () => {
    if (!START) return;
    if (isOn) {
      drawGame();
      score++;
      scoreDom.innerText = score;
    }
  };

  return (
    <div>
      <canvas
        onMouseMove={handleMouseMove}
        onClick={handleClickCanvas}
        ref={canvasRef}
        width={width}
        height={height}
      ></canvas>
      <button onClick={handleStar}>star</button>
    </div>
  );
}

export default ColorGame;


上一篇
Day 25 - 用 canvas 做 煙火
下一篇
Day 27 - 用 canvas 模擬手機圖型解鎖
系列文
Canvas 小錦囊30

尚未有邦友留言

立即登入留言