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

如何实现径向渐变球体在Canvas上的弹跳动画?

径向渐变球体Canvas弹跳动画无法移动的问题

问题描述

我想在Canvas上实现一个径向渐变球体的弹跳动画,目前能成功渲染球体,但无法让它移动。代码语法没问题,推测是结构逻辑导致动画未正常执行。选择径向渐变而非纯色圆或预制PNG,是因为视觉效果更好,也能为后续学习提供更灵活的基础。

现有代码

HTML代码

<canvas id="ballCanvas" width="1000" height="600" style="border:1px solid #000000;"></canvas>
<script src="ballAnimation.js"></script>

JavaScript代码

const canvas = document.getElementById("ballCanvas");
const ctx = canvas.getContext("2d");
let raf;

const ball = {
 vx: 5,
 vy: 2,
 x: 100,
 y: 100,
 draw() {
    // Create a radial gradient
    // The inner circle is at x=250, y=90, with radius=30
    // The outer circle is at x=240, y=100, with radius=70
    var gradient = ctx.createRadialGradient(250, 90, 30, 240, 100, 70);
     
    // Add three color stops
    gradient.addColorStop(0, "rgba(155, 0, 5, 0.8 )");
    gradient.addColorStop(0.9, "rgba(50, 0, 5, 0.5 )");
    gradient.addColorStop(1, "rgba(145, 150, 150, 1 )");

    // Set the fill style and draw a rectangle
    ctx.fillStyle = gradient;
    ctx.fillRect(0, 0, 500, 300);
 },
};

function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ball.draw();
  ball.x += ball.vx;
  ball.y += ball.vy;

 if (ball.y + ball.vy > canvas.height || ball.y + ball.vy < 0) {
    ball.vy = -ball.vy;
  }
  if (ball.x + ball.vx > canvas.width || ball.x + ball.vx < 0) {
    ball.vx = -ball.vx;
  }

  raf = window.requestAnimationFrame(draw);
}

canvas.addEventListener("mouseover", (e) => {
  raf = window.requestAnimationFrame(draw);
});

canvas.addEventListener("mouseout", (e) => {
  window.cancelAnimationFrame(raf);
});

ball.draw();

参考示例代码

我的代码改编自以下示例:

const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
let raf;

const ball = {
  x: 100,
  y: 100,
  vx: 5,
  vy: 2,
  radius: 25,
  color: "blue",
  draw() {
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
    ctx.closePath();
    ctx.fillStyle = this.color;
    ctx.fill();
  },
};

function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ball.draw();
  ball.x += ball.vx;
  ball.y += ball.vy;
  ball.vy *= 0.99;
  ball.vy += 0.25;

  if (ball.y + ball.vy > canvas.height || ball.y + ball.vy < 0) {
    ball.vy = -ball.vy;
  }
  if (ball.x + ball.vx > canvas.width || ball.x + ball.vx < 0) {
    ball.vx = -ball.vx;
  }

  raf = window.requestAnimationFrame(draw);
}

canvas.addEventListener("mouseover", (e) => {
  raf = window.requestAnimationFrame(draw);
});

canvas.addEventListener("mouseout", (e) => {
  window.cancelAnimationFrame(raf);
});

ball.draw();

已做排查与尝试

  • 代码经验证工具检查无语法错误
  • 之前版本因结构问题无法渲染,已修复至当前可渲染状态
  • 尝试过CSS3径向渐变动画,效果不佳且扩展性不足
  • 正在寻找能直观查看代码运行过程的工具,目标是实现自定义径向渐变球体弹跳

解决方案

问题核心在于ball.draw()方法未使用球体自身的x、y属性定位,且绘制的是固定尺寸的矩形而非圆形。调整后代码如下:

const canvas = document.getElementById("ballCanvas");
const ctx = canvas.getContext("2d");
let raf;

const ball = {
  vx: 5,
  vy: 2,
  x: 100,
  y: 100,
  radius: 70, // 统一控制球体大小
  draw() {
    // 基于球体当前位置创建径向渐变,内圆偏移模拟高光
    const innerX = this.x + 10;
    const innerY = this.y - 10;
    const gradient = ctx.createRadialGradient(innerX, innerY, 30, this.x, this.y, this.radius);
    
    gradient.addColorStop(0, "rgba(155, 0, 5, 0.8 )");
    gradient.addColorStop(0.9, "rgba(50, 0, 5, 0.5 )");
    gradient.addColorStop(1, "rgba(145, 150, 150, 1 )");

    // 绘制圆形作为球体
    ctx.beginPath();
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2, true);
    ctx.closePath();
    ctx.fillStyle = gradient;
    ctx.fill();
  },
};

function draw() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ball.draw();
  ball.x += ball.vx;
  ball.y += ball.vy;

  // 碰撞检测加入球体半径,避免部分出界才反弹
  if (ball.y + ball.vy + ball.radius > canvas.height || ball.y + ball.vy - ball.radius < 0) {
    ball.vy = -ball.vy;
  }
  if (ball.x + ball.vx + ball.radius > canvas.width || ball.x + ball.vx - ball.radius < 0) {
    ball.vx = -ball.vx;
  }

  raf = window.requestAnimationFrame(draw);
}

canvas.addEventListener("mouseover", (e) => {
  raf = window.requestAnimationFrame(draw);
});

canvas.addEventListener("mouseout", (e) => {
  window.cancelAnimationFrame(raf);
});

ball.draw();

关键修改点

  1. 给ball对象添加radius属性,统一控制球体尺寸
  2. 创建径向渐变时,以this.x、this.y(球体当前位置)为基准,内圆偏移模拟高光效果,让渐变跟随球体移动
  3. 将fillRect改为绘制圆形(arc),符合球体视觉需求
  4. 碰撞检测加入球体半径,避免球体部分超出画布才反弹,更符合物理直觉

调试工具推荐

  • 浏览器开发者工具Performance面板:录制动画过程,查看每一帧绘制情况,定位执行问题
  • 开发者工具Console面板:在draw函数中添加console.log(ball.x, ball.y),观察坐标变化,确认动画循环是否正常执行

内容的提问来源于stack exchange,提问作者ProfessorMoreRight

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 05:55:07