Canvas是Flappy Bird唯一可行方案,需用requestAnimationFrame驱动、ctx2d绘制、数值化状态管理、AABB碰撞检测,并分离update/render逻辑。
Canvas 画布是唯一可行的渲染路径
纯 HTML + CSS 做不了 Flappy Bird——没有帧动画控制、无法实时检测碰撞、DOM 操作太慢。必须用
,配合
驱动游戏循环。Canvas 2D 上下文提供像素级绘制能力,是这类简单物理游戏的底线要求。
常见错误:试图用
+
模拟下落,结果跳跃卡顿、碰撞检测失灵、重力响应延迟。这不是“够用”,而是根本不可靠。
初始化时务必用
+
别用
替代
,后者能对齐屏幕刷新率,避免掉帧
Canvas 尺寸必须用 JS 显式设置
和
,不能只靠 CSS 缩放,否则图像模糊、坐标错乱
小鸟状态必须用数值变量而非 CSS 类切换
小鸟的旋转角度、Y 位置、速度,本质是连续变化的数值。用
或
切换,既无法精确映射物理状态,又会因浏览器重排/重绘引入延迟和抖动。
正确做法是全程维护一个状态对象:
,每一帧根据物理规则更新它,再用
+
+
绘制。
立即学习
“
前端免费学习笔记(深入)
”;
重力加速度建议设为
,点击跳跃初速度设为
(负值向上),数值过大会导致操作不跟手
旋转角度不要直接绑定
,用
限幅,否则翻转过度失真
跳操作必须监听
和
(空格键),但要防连点:在
后加个
标志,松开后才重置
管道碰撞检测只需判断矩形相交,别碰 SVG 或 getBoundingClientRect
Flappy Bird 的管道是左右两块固定宽度的矩形(上管+下管),小鸟是带旋转的小矩形。用
算 DOM 位置?不行——Canvas 是位图,没 DOM 节点。用 SVG?过度设计,且事件和性能都不如原生 Canvas。
使用HTML,CSS,JavaScript开发Android应用程序 英文文字pdf版附源文件
如果你了解HTML,CSS和JavaScript,您已经拥有所需的工具开发Android应用程序。本动手本书展示了如何使用这些开源web标准设计和建造,可适应任何Android设备的应用程序 - 无需使用Java。您将学习如何创建一个在您选择的平台的Android友好的网络应用程序,然后转换与自由PhoneGap框架到一个原生的Android应用程序。了解为什么设备无关的移动应用是未来的潮流,并开始构建应用程序,提供更
下载
真实做法:把小鸟当前包围盒(考虑旋转后的四角)投影到轴对齐矩形,再和每组管道的上下两个
做 AABB(Axis-Aligned Bounding Box)检测。简单、快、100% 可控。
小鸟包围盒可简化为:以
和
为中心、宽高固定(如 34×24)、旋转影响仅用于视觉,碰撞仍用未旋转矩形(玩家感知不到误差)
管道数据存成数组:
,每帧移动
,移出屏幕就
碰撞条件就是:
requestAnimationFrame 循环里必须分离 update 和 render
新手常把所有逻辑塞进一个函数里:计算位置、画背景、画鸟、画管道、检测碰撞……一旦某帧绘制耗时略高(比如首次加载图片),
就会掉帧,物理模拟失步,小鸟突然“瞬移”或穿管。
必须拆成两层:先
更新所有数值状态(位置、速度、管道生成/销毁),再
单纯调用
和
。这样即使渲染慢,逻辑仍按恒定节奏推进。
里做:重力累加、键盘状态采样、管道移动、新管道生成(当最右管道
里只做:清屏(
)、画背景、画管道、画小鸟(含旋转)、画分数(
)
分数统计逻辑放在
:当管道
从 > bird.x 变为 ≤ bird.x,且之前未计分,就
并标记已计
Canvas 的坐标系、浮点精度、定时器漂移、图片加载时机——这些细节不写几十行注释压不住。真正卡住人的从来不是“怎么画一只鸟”,而是第 37 帧时
算出来是
,或者管道间隙刚好比小鸟高 0.3 像素却判为碰撞。
requestAnimationFramedivtransform: translateYconst canvas = document.getElementById('game');const ctx = canvas.getContext('2d');setIntervalrequestAnimationFramecanvas.widthcanvas.heightclass="rotated"style="transform: rotate(30deg)"const bird = { y: 200, velocity: 0, rotation: 0 };ctx.save()ctx.translate()ctx.rotate()0.5-10velocityMath.min(Math.max(bird.velocity * 3, -30), 90)clickkeydownbird.velocity = -10isJumping = truegetBoundingClientRect(){ x, y, width, height }bird.xbird.yconst pipes = [{ x: 300, topHeight: 120, bottomY: 280 }];pipe.x -= 2splice(0, 1)bird.x + 4 pipe.x && (bird.y pipe.bottomY)requestAnimationFrameupdate()render()ctx.drawImagectx.fillRectupdate()x 时推入)、碰撞判定并设 gameOver = truerender()ctx.clearRectctx.fillTextupdate()xscore++bird.yNaN