iT邦幫忙

2021 iThome 鐵人賽

DAY 14
1
Modern Web

Canvas 小錦囊系列 第 14

Day 14 - 用 canvas 製作刮刮樂

  • 分享至 

  • xImage
  •  

關於前面的小畫家

復刻小畫家先做到昨天作為最後一篇,接下來會帶各位,利用前其所學的功能,製作各種canvas 互動小東西!敬請期待。

這邊也附上前面的所製作的 github

刮刮樂開始啦!

今天的挑戰相當簡單!依照前面的功能,我們可以很迅速的完成呢

import { useEffect, useRef } from "react";
import "./styles.css";

const width = 400;
const hight = 100;

export default function App() {
  const canvasRef = useRef(null);

  useEffect(() => {
    const canvas = canvasRef.current;
    const ctx = canvas.getContext("2d");

    // 填充颜色
    ctx.fillStyle = "darkgray";
    ctx.fillRect(0, 0, width, hight);
    ctx.fillStyle = "#fff";
    ctx.fillText("刮刮卡", 180, 50);

    let isDraw = false;
    canvas.onmousedown = () => {
      isDraw = true;
    };
    canvas.onmousemove = (event) => {
      if (!isDraw) return;
      const point = getClientOffset(event);
      const x = point?.x;
      const y = point?.y;

      // 遮盖策略
      ctx.globalCompositeOperation = "destination-out";
      ctx.arc(x, y, 10, 0, 2 * Math.PI);
      ctx.fill();
    };
    canvas.onmouseup = () => {
      isDraw = false;
    };
  }, [canvasRef]);

  /** 取得位置 */
  const getClientOffset = (event) => {
    if (canvasRef.current) {
      const rect = canvasRef.current.getBoundingClientRect();
      const point = {
        x: event.clientX - rect.left,
        y: event.clientY - rect.top
      };
      return point;
    }
  };

  return (
    <div className="App">
      <h2>刮刮樂小遊戲</h2>
      <div className="box">
        <canvas ref={canvasRef} id="canvas" width="400" height="100"></canvas>
        <div className="text">終於要過二分之一的鐵人賽啦!!</div>
      </div>
    </div>
  );
}

觀察

利用 globalCompositeOperation 我們可以去除滑鼠移動的地方,來達到刮刮樂的效果。

忘記這個效果的看這裡

雖然到這裡看起來完成了,但刮刮樂通常在快刮完時,將整個遮罩清空,所以我們要來補上那段程式碼。

 /** 判斷完成度百分比 */
  const getFilledPercentage = (ctx) => {
    const imgData = ctx.getImageData(0, 0, width, hight);
    let pixels = imgData.data;
    let n = 0;
    for (let i = 0; i < pixels.length; i += 100) {
      if (pixels[i + 3] < 128) {
        n += 100;
      }
    }

    if (n >= pixels.length * 0.6) {
      ctx.globalCompositeOperation = "destination-over";
      ctx.canvas.style.opacity = 0;
    }
  };

在剛才 mouseMovefill 後補上 getFilledPercentage() 判斷,就效果啦!但需要注意的是,當我在參考別人的 imgData.data 時發現,可能因為我的data比較大,所以無法 n+=1 回圈,會造成過大的 for loop , 所以才把間距拉到100,各位也可以依需求去判斷需要給多少。

完整程式碼

codesendBox

效果


上一篇
Day13 - 用 canvas 復刻 小畫家 選擇剪下移動
下一篇
Day 15 - 用 canvas 做打彈珠
系列文
Canvas 小錦囊30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言