核心逻辑是用globalCompositeOperation="destination-out"在纯色不透明遮罩层上“挖除”像素,露出底层奖品内容;需适配高DPR、阻止触摸默认行为,并通过区域累加或getImageData量化刮开面积判定完成。

刮刮卡用 canvas 实现的核心逻辑是什么
刮刮卡效果本质是「局部擦除遮罩层」,不是真的“刮开”内容,而是用 canvas 的 globalCompositeOperation = 'destination-out' 模式,在不透明图层上“挖掉”一块区域,露出底层文字或图片。关键点在于:遮罩层必须是纯色(通常是灰色或黑色),且 fillStyle 不透明度要设为 1;底层内容(如“谢谢参与”)需提前绘制在 canvas 底部或作为背景图叠放。
touchstart/touchmove 在移动端刮不动?
移动端默认会触发滚动或缩放,导致 touchmove 被拦截或坐标偏移。必须显式阻止默认行为,并用 preventDefault() —— 但只对目标 canvas 元素生效,不能写在 document 上:
canvas.addEventListener('touchstart', e => {
e.preventDefault();
isDrawing = true;
});
canvas.addEventListener('touchmove', e => {
e.preventDefault(); // 关键:否则手指一滑就页面滚动了
const touch = e.touches[0];
const rect = canvas.getBoundingClientRect();
const x = touch.clientX - rect.left;
const y = touch.clientY - rect.top;
ctx.lineTo(x, y);
ctx.stroke();
});</p><H3>刮开面积判断用 <code>ctx.getImageData()</code> 还是计数像素?</H3><p>用 <code>getImageData</code> 获取整个遮罩区域的像素数据再遍历,性能差、卡顿明显,尤其在低端安卓机上。更实用的做法是:记录每次笔画覆盖的矩形区域(<code>strokeRect</code> 或缓存路径 bounding box),累加面积后与阈值(如总遮罩面积的 60%)比较。注意:</p><div class="aritcle_card flexRow">
<div class="artcardd flexRow">
<a class="aritcle_card_img" href="/xiazai/skill8272" title="anything-to-html"><img
src="https://img.php.cn/upload/skill/000/000/081/179195588450431.jpg" alt="anything-to-html" ></a>
<div class="aritcle_card_info flexColumn">
<a href="/xiazai/skill8272" title="anything-to-html">anything-to-html</a>
<p>将调研报告、方案对比、数据分析、需求梳理、会议纪要、设计说明、代码评审、学习笔记等任务产物输出为可在浏览器直接打开的单文件 HTML,替代 Markdown 作为最终交付物。Markdown 在信息密度、视觉清晰度和可分享性上已跟不上 AI 产物的丰富度,HTML 能同时承载排版、表格、SVG 等。</p>
</div>
<a href="/xiazai/skill8272" title="anything-to-html" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a>
</div>
</div><ul><li>不要每帧都调用 <code>getImageData</code>,它触发 GPU 同步,代价高</li><li>用 <code>ctx.isPointInPath(x, y)</code> 判断单点是否在已刮区域,适合做“是否已刮开”的轻量判断</li><li>若需精确识别“刮开图案”,建议预先用 SVG 或 CSS mask 做静态遮罩,canvas 只负责响应交互</li></ul><H3>IE 或旧版 Safari 不支持 <code>destination-out</code> 怎么办</H3><p>IE11 及更早版本不支持该合成模式,Safari 12 之前也有兼容问题。替代方案是用两层 canvas:上层画半透灰(<code>rgba(128,128,128,0.8)</code>),下层放奖品文字;刮的时候用 <code>clearRect</code> 清空上层对应区域。虽然效果略“毛边”,但稳定可靠:</p><p><span>立即学习</span>“<a href="https://pan.quark.cn/s/cb6835dc7db1" style="text-decoration: underline !important; color: blue; font-weight: bolder;" rel="nofollow" target="_blank">前端免费学习笔记(深入)</a>”;</p><pre class="brush:php;toolbar:false;">// 上层 canvas(遮罩层)
topCtx.clearRect(x - 10, y - 10, 20, 20); // 刮一个圆点,实际用 path 更顺滑
// 下层 canvas(奖品层)保持不变,自然透出真正难的不是画出刮刮卡,而是让不同设备上的手指轨迹平滑、响应及时、刮开判定不误判——这些细节藏在事件节流、坐标归一化和 canvas 像素比适配里,容易被当成“样式问题”忽略。

















