Day 14 - 用 canvas 制作刮刮乐

关於前面的小画家

复刻小画家先做到昨天作为最後一篇,接下来会带各位,利用前其所学的功能,制作各种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

效果


<<:  学习资源

>>:  ScrollView的Contentoffset解析 Day25

37.use API with Axios

首先,我们要通过 npm/Yarn 或一个 CDN 链接安装 axios。 我们首先创建一个 dat...

实战-我是如何挑到飙股

这几天有朋友跟我说,不要再写劝世文了啦,直接实战说明最快! 好吧,既然如此,我就直接解说,我是如何选...

[ Day 37 ] - 在 Github Release 上发布我们的安装档

之前本鲁都将打包出来的安装档 , 放到 git 中上传到 github 中 来产生一个公开连结让邦友...

[D16] CNN应用

CNN在影像处理、辨识都是很重要的技术,在上一篇已经稍微了解 CNN 的概念後,现在来看看这个实用的...