必须用 canvas。因其可实现不可逆擦除、防截图/刷新泄露结果,而div遮罩易被破解;需正确处理坐标映射、像素判定阈值及微信跨域限制。

刮刮卡底层用 canvas 还是 div 覆盖层?
必须用 canvas。用 div 或伪元素做“刮开”效果,本质是靠遮罩层显隐切换,用户一刷新或截图就全露,毫无抽奖随机性可言——这根本不是刮刮卡,是翻牌动画。
真实刮刮卡的核心是:在 canvas 上绘制两层——底层是奖品文字/图片,上层是不透明涂层(如灰色 fillRect),再监听 touchmove/mousemove 动态清除上层像素(globalCompositeOperation = 'destination-out')。只有这样,刮开区域才真正“不可逆擦除”,且无法通过 DOM 检查提前获知结果。
实操建议:
- 初始化时先用
drawImage把奖品内容绘到底层 canvas,再用fillRect盖满灰色涂层 - 清除操作务必设
ctx.globalCompositeOperation = 'destination-out',否则会覆盖而非擦除 - 移动端注意禁用
touchstart默认行为(e.preventDefault()),否则 iOS 上touchmove不触发
globalCompositeOperation = 'destination-out' 为什么总失效?
常见原因是 canvas 尺寸和 CSS 显示尺寸不一致。Canvas 的宽高由 width/height 属性控制,而 CSS style.width 只缩放显示,不改变绘图坐标系。如果只用 CSS 缩放 canvas,鼠标坐标映射到 canvas 坐标时会严重偏移,导致擦除位置错乱、甚至完全不响应。
正确做法是让 canvas 的 width 和 height 属性值等于其实际渲染像素尺寸(例如 300×200),再用 CSS 控制布局大小。可通过 getBoundingClientRect() 获取真实显示区域,再按比例换算鼠标坐标:
const rect = canvas.getBoundingClientRect(); const x = (e.touches ? e.touches[0].clientX : e.clientX) - rect.left; const y = (e.touches ? e.touches[0].clientY : e.clientY) - rect.top; const scaleX = canvas.width / rect.width; const scaleY = canvas.height / rect.height; ctx.clearRect(x * scaleX - 5, y * scaleY - 5, 10, 10); // 擦除半径 5px 圆点
怎么判断“刮开面积达标”才算中奖?
不能只看是否调用了擦除,得量化刮开的像素占比。最可靠方式是定期(如每 200ms)读取 canvas 上层涂层的像素数据,统计非透明像素剩余量:
将 Markdown 文章转换为微信公众号兼容的纯 HTML 格式。当用户要求"转成微信格式"、"生成公众号文章 HTML"、"排版到微信草稿箱"、"微信粘贴格式"时触发。输出带内联样式的纯 HTML,微信编辑器可直接渲染,无需额外适配。支持标题、段落、列表、粗体、行内代码、表格、引用块、代码块、配图的完整转换。
实操要点:
- 用
ctx.getImageData(0, 0, canvas.width, canvas.height)获取上层图层数据 - 遍历
data数组,每 4 个值为一个像素(RGBA),检查第 3 位(alpha)是否 > 0;注意:destination-out擦除后 alpha 为 0,未刮区域 alpha 是 255 - 设定阈值(如 70% 像素 alpha=0),达到即触发中奖逻辑并禁用后续触摸
- 避免高频读取:用
requestAnimationFrame或节流控制频率,否则安卓低端机易卡顿
微信内 H5 刮刮卡为什么点不动或闪退?
微信内置浏览器(X5 内核)对 canvas 的 getImageData 有严格限制:若 canvas 内容来自跨域图片(比如奖品图存在 CDN),直接调用会报 SecurityError: The operation is insecure.,部分版本甚至直接崩溃。
绕过方法只有两个:
- 所有图片资源走同域,或使用 base64 内联(适合小图标/文字奖品)
- 改用服务端预合成:把奖品图 + 涂层图在后端拼成一张图返回,前端只做纯擦除,不读像素 —— 这样完全避开跨域问题,也更省客户端性能
另外,iOS 微信 8.0.30+ 对 touchmove 的 passive 默认值更激进,务必给 addEventListener('touchmove', handler, { passive: false }),否则滑动直接被拦截。
刮开逻辑本身不难,但像素级判定、跨域安全、多端事件兼容这三块,漏掉任一细节都会导致活动上线即翻车。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










