如何在JavaScript Canvas实现火柴人挥手动画?
实现Canvas火柴人挥手动画的完整方案
当然可以在Canvas上实现火柴人肢体动画啦!而且完全不需要依赖setTimeout——浏览器原生的requestAnimationFrame才是做Canvas动画的最佳选择,它能和浏览器的重绘节奏同步,动画更流畅,还不会像定时器那样容易出现卡顿或跳帧问题。
下面直接上一个可运行的挥手动画示例,一步步拆解实现逻辑:
1. 基础Canvas准备
先在HTML里添加Canvas元素,再在JS中获取绘图上下文:
<canvas id="stickmanCanvas" width="400" height="400"></canvas>
const canvas = document.getElementById('stickmanCanvas'); const ctx = canvas.getContext('2d');
2. 定义动画状态变量
我们需要几个变量来控制手臂的摆动节奏,实现自然的挥手效果:
// 手臂摆动的角度(弧度),初始为0(自然下垂状态) let armAngle = 0; // 摆动方向:1代表向上挥,-1代表摆回原位 let swingDirection = 1; // 手臂摆动的最大角度(60度,转成弧度方便计算) const maxSwingAngle = Math.PI / 3;
3. 绘制火柴人的核心函数
这个函数会根据当前的armAngle值,绘制出对应姿态的火柴人:
function drawStickman() { // 清空画布,准备绘制下一帧 ctx.clearRect(0, 0, canvas.width, canvas.height); // 绘制头部 ctx.beginPath(); ctx.arc(200, 120, 30, 0, Math.PI * 2); ctx.strokeStyle = '#000'; ctx.lineWidth = 3; ctx.stroke(); // 绘制身体 ctx.beginPath(); ctx.moveTo(200, 150); ctx.lineTo(200, 250); ctx.stroke(); // 绘制腿部(固定姿态) ctx.beginPath(); ctx.moveTo(200, 250); ctx.lineTo(170, 350); ctx.moveTo(200, 250); ctx.lineTo(230, 350); ctx.stroke(); // 绘制静止的手臂 ctx.beginPath(); ctx.moveTo(200, 170); ctx.lineTo(140, 220); ctx.stroke(); // 绘制挥手的手臂(根据角度动态计算位置) ctx.beginPath(); const shoulderX = 200; const shoulderY = 170; const armLength = 60; // 用三角函数计算手臂末端的坐标 const armEndX = shoulderX + Math.cos(armAngle) * armLength; const armEndY = shoulderY + Math.sin(armAngle) * armLength; ctx.moveTo(shoulderX, shoulderY); ctx.lineTo(armEndX, armEndY); ctx.stroke(); }
4. 启动动画循环
用requestAnimationFrame驱动动画,实现连续的帧更新:
function animate() { // 更新手臂角度:根据摆动方向增减角度值 armAngle += swingDirection * 0.05; // 当角度达到最大值时,反转摆动方向,实现往返挥手 if (armAngle >= maxSwingAngle || armAngle <= 0) { swingDirection *= -1; } // 绘制当前帧的火柴人 drawStickman(); // 请求浏览器绘制下一帧,形成循环 requestAnimationFrame(animate); } // 启动动画 animate();
为什么不用setTimeout?
setTimeout的时间间隔是固定的,但浏览器的重绘周期不一定和它同步,容易出现卡顿、跳帧的情况。requestAnimationFrame会自动对齐浏览器的刷新频率(通常60Hz),只在浏览器准备好重绘时执行,动画更流畅;而且当页面处于后台时会自动暂停,节省系统资源。
扩展优化思路
如果想要更复杂的肢体动作,可以尝试:
- 给每个肢体部位(比如另一只手臂、腿部)设置独立的角度变量和动画逻辑,实现多部位联动。
- 用
Math.sin函数替代线性角度变化,模拟更自然的弹性摆动效果。 - 加入状态切换逻辑,比如挥手3次后切换到抬手、走路等其他动作。
这样就能轻松实现灵活可控的火柴人肢体动画啦,比CSS Animation更适合做这种需要精细控制的动态效果~
内容的提问来源于stack exchange,提问作者I'm just a coder for fun




