动力 · 19
吸附
Snap · snap按当前位置或预计落点选定目标,再用弹簧抵达。
“最近”只有在已经停下时才符合用户意图。快速甩动时,眼睛预期会越过当前最近项;先预测自然落点再选卡片,方向才对。逐帧向最近项施力又可能很晚才做出决定,让结束阶段发软。
公式
probe = projected ? value + velocity·τ : value target = argmin(point: |point − probe|) spring(value, target, velocity)
参数
| 本页默认 | 成熟实现 | |
|---|---|---|
| mode | projected | GSAP Inertia end / Motion modifyTarget |
| spacing | card pitch | — |
| timeConstant | 0.35 s | Motion 325 ms |
怎样做好
- 甩动时在预计落点选吸附点,不要只看松手位置。GSAP Inertia 的
end在自然落点基础上选 snap。GSAP src/InertiaPlugin.js:87–97,132 - 选定目标后固定它,把释放速度传给弹簧;不要每帧重选最近卡片。
- 慢速拖动可以就近吸附;对停止后的轨道,预测与就近通常会趋于相同结果。
在我们的案例里
15 个案例有吸附。momentum-slider、drag-gallery、riffle 的质量记录都指出逐帧拉向附近卡片的问题;这页把“松手时选目标”和“随后抵达”拆开看。
出现在 15 个案例
参考实现 · basics.js
/**
* Pick where a thrown value should settle.
* nearest — closest point to where it is now (fine once it has stopped)
* projected — closest point to where it WOULD land: respects the throw (iOS, GSAP Inertia, Motion)
* Then hand { target, velocity } to a Spring so the arrival carries the throw's momentum.
*/
export function snapTarget({ value, velocity = 0, spacing, points, mode = 'projected', timeConstant = 0.35 }) {
const probe = mode === 'projected' ? value + velocity * timeConstant : value;
if (points && points.length) return points.reduce((a, b) => (Math.abs(b - probe) < Math.abs(a - probe) ? b : a));
return Math.round(probe / spacing) * spacing;
}