復刻小畫家先做到昨天作為最後一篇,接下來會帶各位,利用前其所學的功能,製作各種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;
}
};
在剛才 mouseMove
的 fill
後補上 getFilledPercentage()
判斷,就效果啦!但需要注意的是,當我在參考別人的 imgData.data
時發現,可能因為我的data比較大,所以無法 n+=1
回圈,會造成過大的 for loop , 所以才把間距拉到100,各位也可以依需求去判斷需要給多少。