关键在于将物理引擎自然嵌入拼图交互:拖拽具惯性、松手滑行、碰撞反弹、空位吸附;需从坐标更新、力计算、碰撞响应协同设计,每块作为带质量、速度、阻尼的刚体,统一力计算与积分,并轻量精准检测碰撞,渲染适配DPI。

Canvas 实现带物理效果的拼图,关键不在“拼图逻辑”本身,而在于把物理引擎自然地嵌入到拼图交互中——比如拖拽时有惯性、松手后滑行、块与块之间轻微碰撞反弹、空位吸附等。这不是给拼图加个动画库就能解决的事,得从坐标更新、力计算、碰撞响应三个层面协同设计。
用物理引擎接管拼图块的运动状态
每个拼图块不能只是“跳到目标位置”,而应是一个带质量、速度、阻尼的刚体:
- 为每块定义 position(当前画布坐标)、velocity(像素/秒)、mass(默认设为 1 即可)和 damping(如 0.92,模拟空气阻力)
- 鼠标/触摸拖拽时,不直接赋值坐标,而是设置目标位置,并施加一个朝向它的“牵引力”:
force = (target - position) × stiffness,再通过acceleration = force / mass更新速度 - 每一帧用
velocity *= damping衰减,并执行position += velocity × deltaTime—— 这样松手后会自然滑行一段,而非突兀停止
空位与邻接块之间的约束模拟
传统拼图只允许“交换相邻块”,但加入物理后,可升级为“磁性吸附+弹性碰撞”:
生成可在 deepbi 沙箱 iframe 内运行或作为独立文件运行的交互式 HTML 页面,使用 `window.deepbi` JS SDK 实现数据查询、空间管理、文件浏览及用户操作。当用户提出以下需求时启用该能力: - 创建 KPI 仪表盘、数据表格、文件浏览器、成员列表、管理页面; - 明确要求“转成 dhtml”、“转译成 HTML”、“把 XX 页面转成……”、“控制台页面”、“管理后台”、“DeepBI 页面迁移”; - 提及位于 `apps/console/src/featur
- 将空位视为一个质量极大、静止不动的“锚点”,对邻近块产生弱吸引力(距离越近力越大),使其在靠近时空位自动“拉一把”
- 当拖动块快速撞向空位边界时,检测相对速度方向,若垂直分量足够大,就触发一次微小反弹(反向速度 × 0.3),增强真实感
- 所有这些力都统一进
applyForces()函数,在update(deltaTime)中统一积分,避免逻辑碎片化
碰撞检测必须轻量且精准
拼图块之间一般不发生真实碰撞(除空位边界外),但若想支持“多块同时拖拽”或“推挤式移动”,需最小化开销的检测策略:
立即学习“前端免费学习笔记(深入)”;
- 禁用全量两两检测:只检查被拖动块与空位、以及它可能推到的**最多两个邻位块**(上/下/左/右中实际存在且未被拖动的)
- 用 AABB(轴对齐包围盒)判断是否重叠:
if (a.x b.x && a.y b.y) - 重叠发生时,不立即硬修正位置,而是生成一个“排斥力”,让两块缓慢分离,保持运动连续性
渲染与 DPI 适配不可妥协
物理计算在逻辑坐标系(如 CSS 像素)中进行,但绘制必须匹配设备物理像素:
- 初始化 canvas 时调用适配函数:
canvas.width = rect.width * dpr; canvas.height = rect.height * dpr; ctx.scale(dpr, dpr); - 所有物理参数(速度、加速度、力)单位保持为“CSS 像素/秒”,缩放由 ctx.scale 自动处理,无需换算
- requestAnimationFrame 中先
ctx.clearRect(0, 0, canvas.width, canvas.height),再按顺序绘制背景网格、静态块、拖动块——确保无残影


















