基于Canvas的SeeMusic钢琴下落动画新生成瓦片速度异常问题
问题根因
瓦片速度逐次升高的问题来自两个核心bug:
- Update渲染循环被重复触发
你在SpawnTile函数里每次生成新瓦片都会调用requestAnimationFrame(Update),而Update函数本身也在递归调用requestAnimationFrame(Update)。这意味着每生成一个瓦片,渲染循环的触发次数就会翻倍,相当于每多一个瓦片全局帧率就翻一倍,所有瓦片的运动速度自然会越来越快。 - mouseup事件重复绑定
你在Tile类的Animate方法里每帧都给window新增一个mouseup事件监听器,随着时间推移,单次mouseup触发会执行成百上千个回调,不仅导致性能卡顿,还会让所有历史瓦片的运动参数被重复修改。
修复代码
JS部分修改如下
const canvas = document.querySelector("canvas"), ctx = canvas.getContext("2d"), keys = document.querySelectorAll(".key") document.querySelector(".keyboard").style.gridTemplateColumns = "repeat("+keys.length+", 1fr)" canvas.width = window.innerWidth canvas.height = window.innerHeight*0.8 // Variables let tile, particle, tiles = [], tileX, tileWidth = keys[0].offsetWidth class Tile { constructor (x, y, h, dh, dy, c) { this.x = x this.y = y this.h = h this.c = c this.dh = dh this.dy = dy // 只在瓦片创建时绑定一次mouseup事件,触发后自动解绑避免内存泄漏 this.stopGrowing = () => { this.dh = 0 this.dy = 2 window.removeEventListener('mouseup', this.stopGrowing) } window.addEventListener('mouseup', this.stopGrowing) } Animate() { this.h += this.dh this.y += this.dy this.Draw() } Draw() { ctx.beginPath() ctx.fillStyle = this.c ctx.fillRect(this.x, this.y, keys[0].offsetWidth, this.h) ctx.closePath() } } function SpawnTile() { tiles.push(new Tile(tileX, 0, 0, 2, 0, "red")) } // 全局仅启动一次渲染循环即可 requestAnimationFrame(Update) function Update() { requestAnimationFrame(Update) ctx.clearRect(0,0,canvas.width, canvas.height) tiles.forEach(tile => { tile.Animate() }) } keys.forEach(key => { key.addEventListener("mousedown", () => { tileX = key.getBoundingClientRect().left SpawnTile() }) })
额外优化建议
可以添加瓦片超出画布边界后的自动销毁逻辑,避免tiles数组无限增长占用内存:
// 替换原来的tiles.forEach逻辑 tiles = tiles.filter(tile => { tile.Animate() // 瓦片完全移出画布后就从数组中移除 return tile.y < canvas.height })
内容的提问来源于stack exchange,提问作者Amadeus
相关产品推荐
相关产品推荐

