作者:空心悟心 | 来源:互联网 | 2024-12-16 13:18
在前几节较为基础的Canvas API练习之后,我们进入了一个更为有趣的话题——动画制作。本节将通过一个碰撞仿真的例子,带你深入了解Canvas在动画和物理仿真方面的应用。
1. Canvas的功能扩展
Canvas不仅仅是图表绘制工具,它在2D空间中提供了丰富的功能,如创建动态效果、物理仿真、图像处理等。这些效果主要依靠Canvas对像素的操作能力和快速连续的帧渲染来实现,类似于电影通过快速播放静态图像来制造动态视觉效果。
考虑到Javascript中的时间控制函数setTimeout()
和setInterval()
可能因事件队列中的其他异步任务而受到影响,导致动画卡顿或速度不一致。因此,推荐使用requestAnimationFrame(fn)
方法,该方法能更好地与显示器的刷新率同步,确保动画流畅且高效。
2. 构建动画框架
在Canvas上实现动画,可以通过一个简单的循环函数step()
来管理每一帧的更新和渲染:
function step() {
/* 执行每一帧的逻辑 */
requestAnimationFrame(step);
}
此函数通过requestAnimationFrame
递归调用,确保动画持续运行。每一帧中,通常需要清除上一帧的画面,更新所有活动对象的状态,然后重新绘制这些对象。
3. 实现碰撞检测的动画
通过一个碰撞仿真的案例,我们可以深入理解Canvas动画的实现细节和基本的物理仿真原理。为了简化向量操作,我们将使用一个预定义的Vector2
类。
3.1 小球对象的定义
首先,定义一个Ball
类,包含小球的位置、颜色、半径和速度等属性:
class Ball {
constructor(x, y, id) {
this.pos = new Vector2(x, y);
this.id = id;
this.color = ''; // 绘制颜色
this.r = 20; // 半径
this.velocity = null; // 速度
}
}
3.2 动态生成小球
使用定时器定期生成新的小球,并赋予随机的速度和颜色:
function addBall() {
const ball = new Ball(50, 30, balls.length);
ball.color = colorPalette[Math.floor(steps / 100) % 10];
ball.velocity = new Vector2(5 * Math.random(), 5 * Math.random());
balls.push(ball);
}
每100帧(约1.5秒)生成一个小球,总数不超过15个。
3.3 动画帧处理函数
在step()
函数中,除了生成小球,还需要更新每个小球的位置,并检查碰撞情况:
function step() {
steps++;
paintBg(); // 绘制背景
if (steps % 100 === 0 && steps <1500) {
addBall();
}
balls.forEach(ball => {
ball.update();
ball.paint();
});
requestAnimationFrame(step);
}
3.4 小球状态更新
在Ball
类中实现update()
方法,用于更新小球的位置和处理碰撞:
Ball.prototype.update = function() {
this.pos = this.pos.add(this.velocity);
// 边界碰撞检测
if (this.pos.x + this.r > rightBorder || this.pos.x this.velocity.x *= -1;
}
if (this.pos.y + this.r > bottomBorder || this.pos.y this.velocity.y *= -1;
}
// 小球间碰撞检测
balls.forEach(other => {
if (other !== this && this.checkCollision(other)) {
this.handleCollision(other);
}
});
}
3.5 碰撞检测
碰撞检测是通过比较两球中心距离与半径之和来实现的:
Ball.prototype.checkCollision = function(other) {
return this.pos.distanceTo(other.pos) }
3.6 碰撞响应
碰撞响应涉及到速度的调整,模拟真实的物理现象:
Ball.prototype.handleCollision = function(other) {
const normal = this.pos.clone().subtract(other.pos).normalize();
const relativeVelocity = this.velocity.clone().subtract(other.velocity);
const velocityAlOngNormal= relativeVelocity.dot(normal);
if (velocityAlongNormal > 0) {
return; // 不会发生碰撞
}
const impulse = 2 * velocityAlongNormal / (this.mass + other.mass);
this.velocity = this.velocity.add(normal.clone().multiplyScalar(impulse * other.mass));
other.velocity = other.velocity.subtract(normal.clone().multiplyScalar(impulse * this.mass));
}
以上代码实现了小球之间的弹性碰撞,确保碰撞后速度合理调整。
4. 后续探索
掌握了基本的碰撞检测和动画模拟技巧后,你可以尝试构建更复杂的动画场景,如乒乓球游戏或台球游戏等。