很明显,这样的速度曲线过于线性平滑,减速效果不明显。我们参考 iOS 滚动回弹的效果重复测试,调整贝塞尔曲线的参数为 cubic-bezier(.17, .89, .45, 1):
调整曲线后的效果理想很多:
回弹接下来模拟惯性滚动时触碰到容器边界触发回弹的情况。
我们基于滑块模型来模拟这样的场景:滑块左端与一根弹簧连接,弹簧另一端固定在墙体上,在滑块向右滑动的过程中,当滑块到达临界点(弹簧即将发生形变时)而速度还没有降到 0m/s 时,滑块会继续滑动并拉动弹簧使其发生形变,同时滑块会受到弹簧的反拉力作减速运动(动能转化为内能);当滑块速度降为 0m/s 时,此时弹簧的形变量最大,由于弹性特质弹簧会恢复原状(内能转化成动能),并拉动滑块反向(左)运动。
类似地,回弹过程也可以分为下面两个阶段:
滑块拉动弹簧往右做变减速运动;
此阶段滑块受到摩擦力 F摩 和越来越大的弹簧拉力 F弹 共同作用,加速度越来越大,导致速度降为 0m/s 的时间会非常短。
弹簧恢复原状,拉动滑块向左做先变加速后变减速运动;
此阶段滑块受到的摩擦力 F摩 和越来越小的弹簧拉力 F弹 相互抵消,刚开始 F弹 > F摩,滑块做加速度越来越小的变加速运动;随后 F弹 < F摩,滑块做加速度越来越大的变减速运动,直至最终静止。这里为了方便实际计算,我们不妨假设一个理想状态:当滑块静止时弹簧刚好恢复形变。
回弹距离根据上面的模型分析,回弹的第一阶段做加速度越来越大的变减速直线运动,不妨设此阶段的初速度为 v0,末速度为 v1,那么可以与滑块位移建立关系:
其中 a 为加速度变量,这里暂不展开讨论。那么,根据物理学的弹性模型,第二阶段的回弹距离为
微积分都来了,简直没法计算……
然而,我们可以根据运动模型适当简化 S回弹 值的计算。由于 回弹第二阶段的加速度 是大于 非回弹惯性滚动阶段的加速度(F弹 + F摩 > F摩)的,不妨设非回弹惯性滚动阶段的总距离为 S滑,那么
因此,我们可以设置一个较为合理的常量 B,使其满足这样的等式:
经大量实践得出,常量 B 的合理值为 10。
回弹速度曲线触发回弹的整个惯性滚动轨迹可以拆分成三个运动阶段:
然而,如果要把阶段 a 和阶段 b 准确描绘成 CSS 动画是有很高的复杂度的:
阶段 b 中的变减速运动难以准确描绘;
这两个阶段虽运动方向相同但动画速度曲线不连贯,很容易造成用户体验断层;
为了简化流程,我们把阶段 a 和 b 合并成一个运动阶段,那么简化后的轨迹就变成: