Canvas实现点击射击小球游戏时添加新球旧球消失问题求助
问题核心原因
- 每个
Ball实例都独立运行了requestAnimationFrame动画循环,并且在各自的animateBall方法里执行了clearRect清空整个画布,每个球的动画帧都会先清掉所有内容,再只绘制自己,自然看不到之前的旧球 - 原有代码中
animateBall直接传this.animateBall给requestAnimationFrame,执行时this会丢失绑定指向全局,后续运行会抛出属性未定义的错误 - 原有逻辑把速度计算放到了
drawBall方法中,每次绘制都会重新计算速度,也会导致小球运动逻辑异常
修复方案
把全局动画循环挪到Game类中统一管理,单次帧渲染的逻辑统一为:清画布 → 遍历所有ball实例依次更新位置 → 遍历所有ball实例依次绘制,同时移除Ball类里的独立动画循环和清画布逻辑。
修改后的Game类代码
class Game { constructor(props) { // 保留原有canvas、ctx、炮口位置cannonPos的初始化逻辑 this.balls = [] this.canvas.addEventListener('mousedown', (e) => { this.handleMouseClicks(e) }); // 启动全局统一动画循环 this.animate() } handleMouseClicks(e) { this.balls.push(new Ball({ // 把上下文、点击坐标、炮口位置参数传给Ball实例,降低耦合 ctx: this.ctx, mousePos: {x: e.offsetX, y: e.offsetY}, cannonPos: this.cannonPos })) } animate() { // 全局单次帧仅清一次画布 this.ctx.clearRect(0,0, this.canvas.width, this.canvas.height) // 遍历所有球更新位置 this.balls.forEach(ball => ball.updateBall()) // 遍历所有球绘制 this.balls.forEach(ball => ball.drawBall()) // 全局统一请求下一帧,箭头函数保留this指向 requestAnimationFrame(() => this.animate()) } }
修改后的Ball类代码
class Ball { constructor(props) { // 接收初始化参数 this.ctx = props.ctx this.mouseClickPosX = props.mousePos.x this.mouseClickPosY = props.mousePos.y this.cannonPosX = props.cannonPos.x this.cannonPosY = props.cannonPos.y // 初始化小球初始位置、速度,仅计算一次 this.ballPosX = this.cannonPosX this.ballPosY = this.cannonPosY this.angle = Math.atan2(this.mouseClickPosY - this.cannonPosY, this.mouseClickPosX - this.cannonPosX) this.velocity = { x: Math.cos(this.angle), y: Math.sin(this.angle) } this.ballRadius = 2 this.color = '#000' // 可自定义小球颜色 } updateBall() { this.ballPosX = this.ballPosX + this.velocity.x this.ballPosY = this.ballPosY + this.velocity.y } drawBall() { this.ctx.beginPath() this.ctx.arc(this.ballPosX, this.ballPosY, this.ballRadius, 0, Math.PI * 2, false) this.ctx.closePath() this.ctx.fillStyle = this.color; this.ctx.fill() } }
内容的提问来源于stack exchange,提问作者pileup
相关产品推荐
相关产品推荐

