如何用requestAnimationFrame实现Canvas内多对象动画?
修复Canvas动画(基于requestAnimationFrame)
你之前的代码已经搭好了基础框架,动画没跑起来主要是几个关键步骤没到位,下面是修正后的完整实现:
核心修复点
- 给
Agent类补上速度属性和位置更新方法 - 实现正确的
requestAnimationFrame动画循环,每帧要清空画布→更新所有圆的位置→重绘所有圆 - 把原代码中放在
for循环里的forEach移出来,避免重复绘制
完整代码
CSS 样式
body { background-color: #000000; } canvas { padding: 0; margin: auto; display: block; position: absolute; top: 0; bottom: 0; left: 0; right: 0; background-color: #111416; border: 10px solid blue; border-style: double; box-shadow: 0 0 20px 5px blue; }
HTML & JavaScript
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Canvas Animated Circles</title> <link rel="stylesheet" href="canvas.css"> </head> <body> <canvas id="myCanvas" width="350" height="350"></canvas> <script> // 获取画布元素 var canvas = document.getElementById("myCanvas"); // 获取2D绘图上下文 var context = canvas.getContext("2d"); // 获取画布宽高 var canvW = canvas.width; var canvH = canvas.height; // 生成指定范围的随机数 const randomRange = (min, max) => { return Math.random() * (max - min) + min; } // 存储坐标点的类 class Point { constructor(x, y){ this.x = x; this.y = y; } } // 单个空心圆的类 class Agent { constructor(x, y){ this.pos = new Point(x, y); this.radius = randomRange(4, 12); // 新增:给每个圆添加随机速度 this.vel = new Point(randomRange(-1, 1), randomRange(-1, 1)); } // 绘制单个圆 draw(context) { context.save(); context.translate(this.pos.x, this.pos.y); context.beginPath(); context.arc(0, 0, this.radius, 0, Math.PI * 2); context.lineWidth = 4; context.strokeStyle = "blue"; context.stroke(); context.restore(); } // 新增:更新圆的位置 update() { this.pos.x += this.vel.x; this.pos.y += this.vel.y; // 可选:添加边界碰撞,让圆在画布内反弹 if (this.pos.x - this.radius < 0 || this.pos.x + this.radius > canvW) { this.vel.x *= -1; } if (this.pos.y - this.radius < 0 || this.pos.y + this.radius > canvH) { this.vel.y *= -1; } } } // 存储所有圆的数组 const agents = []; // 创建40个随机位置的圆 for (let i = 0; i < 40; i++){ const x = randomRange(0, canvW); const y = randomRange(0, canvH); agents.push(new Agent(x, y)); } // 动画循环函数 const animate = () => { // 清空画布(必须,否则会留下轨迹) context.clearRect(0, 0, canvW, canvH); // 更新所有圆的位置 + 重绘所有圆 agents.forEach(agent => { agent.update(); agent.draw(context); }); // 递归调用,持续动画 requestAnimationFrame(animate); } // 启动动画 animate(); </script> </body> </html>
额外说明
- 新增了边界碰撞逻辑,让圆碰到画布边缘会反弹,你可以根据需要删除这部分代码
- 动画循环里必须先清空画布,不然每次绘制都会叠加,导致画面模糊
- 原代码里把
forEach放在for循环内部,会导致每个圆被重复绘制40次,现在移到循环外只执行一次
内容的提问来源于stack exchange,提问作者elbertainstein
相关产品推荐
相关产品推荐

