第五集:交互!让鼠标成为上帝的指挥棒
96
0
0
“老板说要‘沉浸式体验’,我把画布点出了烟花,他以为我偷偷学了 Unity。”
—— 某位刚被加薪的前端小可爱
大家好,欢迎来到《Canvas 翻车指南》第五集!
前面我们让火柴人狂奔到冒烟,今天把鼠标变成魔法棒:
- 点击 → 粒子烟花
- 拖拽 → 橡皮擦
- 防抖 & 节流 → 鼠标疯移也不卡成狗
准备好冰可乐,咱们让用户体验升维打击!
一、监听三剑客:down、move、up
canvas.addEventListener('mousedown', start);
canvas.addEventListener('mousemove', move);
canvas.addEventListener('mouseup', end);手机端再补个 touchstart / touchmove / touchend,一套代码两端通吃。
坐标转换别忘了:
function getPos(e) {
const rect = canvas.getBoundingClientRect();
return {
x: (e.clientX || e.touches[0].clientX) - rect.left,
y: (e.clientY || e.touches[0].clientY) - rect.top
};
}口诀:
“拿坐标先 getBounding,不然全屏乱飞像鬼魂。”
二、点击放烟花:粒子池 + radial 爆炸
目标:点哪炸哪, 0.5 秒后消失,帧率稳如狗。
1. 对象池提前 new 好
const MAX = 300;
const pool = [];
for (let i = 0; i < MAX; i++) pool.push(new Particle());粒子类:
class Particle {
init(x, y, color) {
this.x = x; this.y = y;
this.vx = (Math.random() - 0.5) * 6;
this.vy = (Math.random() - 0.5) * 6;
this.life = 1;
this.decay = 0.02;
this.color = color;
}
update() {
this.x += this.vx;
this.y += this.vy;
this.vy += 0.1; // 重力
this.life -= this.decay;
}
draw(ctx) {
ctx.save();
ctx.globalAlpha = this.life;
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x, this.y, 3, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
}2. 点击事件
let tail = 0;
function boom(x, y) {
const color = `hsl(${Math.random()*360},100%,60%)`;
for (let i = 0; i < 50; i++) {
pool[tail].init(x, y, color);
tail = (tail + 1) % MAX;
}
}
canvas.addEventListener('click', e => {
const p = getPos(e);
boom(p.x, p.y);
});3. RAF 里更新 & 绘制
function loop() {
requestAnimationFrame(loop);
ctx.fillStyle = 'rgba(0,0,0,0.08)'; // 拖尾
ctx.fillRect(0, 0, w, h);
pool.forEach(p => {
if (p.life <= 0) return;
p.update();
p.draw(ctx);
});
}
loop();刷新:
点哪炸哪,夜空拖尾,像 Winamp 可视化!
把 globalAlpha 拖尾改成 clearRect,就是硬核 Starfield。
三、拖拽橡皮擦:把画布当沙盘
目标:按住鼠标擦除像素,松手即停。
1. 状态机
let isDrawing = false;
function start(e) { isDrawing = true; move(e); }
function end() { isDrawing = false; }
function move(e) {
if (!isDrawing) return;
const p = getPos(e);
ctx.save();
ctx.globalCompositeOperation = 'destination-out'; // 橡皮擦模式
ctx.beginPath();
ctx.arc(p.x, p.y, 20, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}口诀:
“destination-out 就是橡皮擦,画哪哪消失,爽到飞起。”2. 手机兼容
canvas.addEventListener('touchstart', e => start(e.touches[0]));
canvas.addEventListener('touchmove', e => {
e.preventDefault(); // 防止页面滚动
move(e.touches[0]);
});
canvas.addEventListener('touchend', end);现在:
鼠标 / 手指双通,像 Procreate 橡皮,老板看完默默给你买 iPad。
四、防抖 & 节流:鼠标疯移也不卡
1. 节流(throttle)
function throttle(fn, wait) {
let last = 0;
return function (...args) {
const now = Date.now();
if (now - last > wait) {
last = now;
fn.apply(this, args);
}
};
}
canvas.addEventListener('mousemove', throttle(move, 16)); // 60Hz2. 防抖(debounce)
适合“鼠标停止后保存”:
function debounce(fn, delay) {
let timer = null;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
window.addEventListener('resize', debounce(saveCanvas, 500));口诀:
“节流像水龙头,防抖像电梯门。”
五、合体技:烟花 + 橡皮擦,双模式切换
加按钮:
<button id="mode">切换到橡皮擦</button>let mode = 'firework';
document.getElementById('mode').onclick = () => {
mode = mode === 'firework' ? 'eraser' : 'firework';
this.textContent = mode === 'eraser' ? '切换到烟花' : '切换到橡皮擦';
};在 move 里分流:
function move(e) {
if (mode === 'firework') return; // 烟花只点
if (!isDrawing) return;
// 橡皮擦逻辑
}瞬间:一个页面两种玩法,像 Switch 卡带!
六、性能锦囊:离屏擦除 & 分层 Canvas
- 背景静态?放
bgCanvas,主画布只清局部 - 橡皮擦太大?用
globalCompositeOperation = 'destination-out'+drawImage,GPU 帮你算 - 手机老机型?把
touchmove节流 24Hz,肉眼无感,帧率翻倍
七、本集知识点一口闷
| 技能 | 口诀 | 坑 |
|---|---|---|
| getBoundingClientRect | 拿坐标 | 忘记转手机坐标 |
| destination-out | 橡皮擦 | 用完不回 source-over 全消失 |
| 对象池 | 提前 new | 爆炸时 malloc 会卡 |
| throttle | 水龙头 | 16ms 是 60Hz 门槛 |
八、课后作业(翻车 field)
画五彩画板:
- 调色盘选色 → 鼠标拖拽画笔(
globalCompositeOperation = 'source-over') - 橡皮擦按钮切换,一键擦除
- 调色盘选色 → 鼠标拖拽画笔(
- 加撤销(用数组保存每一步
ImageData,50 步上限) - Bonus:双指缩放 & 移动,像地图一样拖,适配触摸板
九、下集预告
第六集《性能翻车现场:十万颗粒子如何不卡成 PPT》
带你上:
- WebWorker 算物理,主线程只画画
- 顶点着色器 trick,GPU 批渲染
OffscreenCanvas+transferControlToOffscreen,帧率锁 144Hz!
“人生就像交互事件,
你不监听 move,
就永远不知道用户下一秒想擦除谁。”
—— 我说的。
第五集结束,点赞、转发、晒烟花,
下集一起把粒子干到十万级,回见!
0
快来点个赞吧
发表评论
评论列表