Canvas实现刮刮乐效果:通过destination-out合成模式动态擦除黑色涂层,露出底层内容;需坐标转换、防误触及触摸适配;支持刮刀宽度调节与完成检测。

用 HTML + CSS + JavaScript 实现“刮刮乐”效果,核心是模拟“刮开遮罩层露出底层内容”,关键在于用鼠标/触摸拖动时动态擦除上层的不透明涂层(通常是 canvas 或带背景色的 div)。下面分步骤说明实用、可落地的实现方式。
用 Canvas 实现高精度刮擦(推荐)
Canvas 提供像素级控制,擦除自然、性能好,适合需要真实刮擦感的场景。
- 页面结构:一个外层容器,内部放
<canvas>(作为刮刮层)和一个隐藏的<div>或图片(作为底层内容),canvas 叠在最上层并铺满 - 初始化时,用
fillRect()绘制一层纯色(如 #000)或半透灰(rgba(0,0,0,0.8))作为“涂层” - 监听
mousedown→mousemove→mouseup(移动端对应 touchstart/move/end) - 在
mousemove中,用ctx.globalCompositeOperation = 'destination-out'设置合成模式,再用arc()或lineTo()绘制圆形/路径,即可“擦掉”涂层、露出下层 - 注意:需将鼠标坐标转换为 canvas 坐标(考虑 canvas 的缩放、偏移),可用
getBoundingClientRect()辅助计算
用 CSS + 伪元素模拟(轻量但有限)
适合简单需求(如按钮式刮刮卡),不依赖 JS,但无法真正“擦除”,只是视觉遮盖切换。
- 结构:外层
<div class="scratch-card">,内含<div class="content">中奖信息</div>和一个覆盖层<div class="mask"></div> - 给
.mask设置background: linear-gradient(...)或纯色,再加pointer-events: none让鼠标穿透 - 用
::after伪元素做“刮痕”:监听 mousemove,动态设置伪元素的width/height和left/top,配合border-radius: 50%模拟圆形擦痕 - 缺点:只能做固定形状刮痕,不能真正擦除涂层;多点触控、连续轨迹支持弱
关键细节与避坑提示
无论哪种方案,这些点直接影响体验是否“像刮刮乐”:
立即学习“前端免费学习笔记(深入)”;
-
防误触:添加
mousedown后才启用mousemove监听,松开后立即禁用,避免划出区域外还继续擦 -
响应触摸屏:必须同时绑定
touchstart/touchmove/touchend,并用event.touches[0]取坐标 -
擦除粗细可控:canvas 中通过改变
ctx.lineWidth控制“刮刀”宽度;CSS 方案可通过动态调整伪元素尺寸实现 -
完成检测(可选):canvas 可统计已擦除像素占比(读取
getImageData()),当透明像素超过阈值(如 70%)触发“刮开成功”逻辑
一个最小可用 canvas 示例片段
复制即可运行(含基础防抖和坐标校准):
<div style="position:relative;width:300px;height:150px;">
<div style="position:absolute;top:0;left:0;width:100%;height:100%;background:#ffcc00;font:bold 16px/150px sans-serif;text-align:center;color:#fff;">¥100 红包</div>
<canvas id="scratch" width="300" height="150" style="position:absolute;top:0;left:0;"></canvas>
</div>
<p><script>
const canvas = document.getElementById('scratch');
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#000';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.globalCompositeOperation = 'destination-out';
ctx.lineCap = ctx.lineJoin = 'round';
ctx.lineWidth = 30;</p><p>let isDrawing = false;</p><p>canvas.addEventListener('mousedown', start);
canvas.addEventListener('mousemove', draw);
canvas.addEventListener('mouseup', stop);
canvas.addEventListener('mouseout', stop); // 鼠标划出也停止</p><p>function getPos(e) {
const rect = canvas.getBoundingClientRect();
return {
x: e.clientX - rect.left,
y: e.clientY - rect.top
};
}</p><p>function start(e) {
isDrawing = true;
const pos = getPos(e);
ctx.beginPath();
ctx.moveTo(pos.x, pos.y);
}</p><p>function draw(e) {
if (!isDrawing) return;
const pos = getPos(e);
ctx.lineTo(pos.x, pos.y);
ctx.stroke();
}</p><p>function stop() {
isDrawing = false;
ctx.beginPath(); // 重置路径
}
</script></p>



















