You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 02:05:26