如何实现径向渐变球体在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();
关键修改点
- 给
ball对象添加radius属性,统一控制球体尺寸 - 创建径向渐变时,以
this.x、this.y(球体当前位置)为基准,内圆偏移模拟高光效果,让渐变跟随球体移动 - 将
fillRect改为绘制圆形(arc),符合球体视觉需求 - 碰撞检测加入球体半径,避免球体部分超出画布才反弹,更符合物理直觉
调试工具推荐
- 浏览器开发者工具Performance面板:录制动画过程,查看每一帧绘制情况,定位执行问题
- 开发者工具Console面板:在
draw函数中添加console.log(ball.x, ball.y),观察坐标变化,确认动画循环是否正常执行
内容的提问来源于stack exchange,提问作者ProfessorMoreRight
相关产品推荐
相关产品推荐

