canvas.getContext('2d') 是绘图唯一入口,必须先获取上下文再调用其方法;推荐用 requestAnimationFrame 实现平滑动画并基于 delta time 计算位移;砖块碰撞需手动实现 AABB 检测而非依赖 getBoundingClientRect()。
不通过 getContext('2d') 获取绘图上下文,<canvas></canvas> 就只是个空盒子,fillRect()、clearRect()、drawImage() 全部无效。常见错误是直接操作 canvas 元素本身,比如写 canvas.fillRect() —— 这会报 TypeError: canvas.fillRect is not a function。
实操建议:
立即学习“前端免费学习笔记(深入)”;
const ctx = canvas.getContext('2d') 获取上下文,后续所有绘制都调用 ctx 上的方法document.getElementById() 返回 null
canvas.width / canvas.height,而非 CSS,否则图像会被拉伸用 setInterval() 控制球速,容易出现卡顿、掉帧、时间漂移,尤其在页面切到后台再切回时,setInterval 可能批量触发,球瞬间“瞬移”。而 requestAnimationFrame() 由浏览器调度,与屏幕刷新率同步(通常 60fps),且在页面不可见时自动暂停。
实操建议:
立即学习“前端免费学习笔记(深入)”;
requestAnimationFrame(gameLoop) 递归调用,不要用 setInterval(gameLoop, 1000/60)
ball.x += ball.vx * deltaTime / 16(16ms ≈ 1 帧)gameLoop 中做耗时操作(如 DOM 查询、大数组遍历),否则会拖慢帧率getBoundingClientRect() 返回的是元素在视口中的矩形位置,但 canvas 里的砖块是纯绘制图形,没有 DOM 节点;即使你用 div 模拟砖块,该方法也无法处理旋转、缩放或像素级碰撞(比如球从砖块斜角擦过却判定击中)。真实弹球游戏必须手动实现 AABB(轴对齐包围盒)检测。
实操建议:
立即学习“前端免费学习笔记(深入)”;
x、y、width、height;球保存 x、y、radius
ball.x + ball.radius > brick.x && ball.x - ball.radius
ball.vy = -ball.vy(上下)或 ball.vx = -ball.vx(左右),否则球会“粘”在砖块上直接监听 keydown 并立即移动挡板,会导致按住方向键时挡板只动一格(因为浏览器默认会触发一次 keydown + 长按后间歇触发)。更糟的是,canvas 失去焦点(比如点了页面其他地方)后,keydown 就完全不响应了。
实操建议:
立即学习“前端免费学习笔记(深入)”;
const keys = { ArrowLeft: false, ArrowRight: false },在 keydown 设为 true,keyup 设为 false
gameLoop 中根据 keys.ArrowLeft 状态持续更新挡板位置,不是在事件里改位置tabindex="0" 并在初始化时调用 canvas.focus(),确保键盘事件能被捕获砖块阵列的生成、生命值管理、球速随关卡递增、音效触发时机——这些都不是“加几行代码就能跑”的部分。最容易被忽略的是:球在高速下可能一帧穿过砖块(即“隧道效应”),此时 AABB 检测会失效,必须做扫掠检测或缩小时间步长。这一步没处理好,玩家就会觉得“明明打中了却不计分”。