
弹簧解析解常被用来生成关键帧。公式看上去很完整,实际接入却可能在 t=0 就改了用户传入的速度,或在动画第一次经过目标点时提前停下。先验证初值,比先给曲线起一个情绪名称更重要。
这里研究相对目标的位移 x,方程为 m·x''+c·x'+k·x=0,时间单位秒,速度单位为位移单位每秒。目标改变时,先求出当前的位置和速度,再把相对新目标的位移作为下一段初值。解析解解决的是这个模型,不自动保证界面手感。
三种阻尼共享一份初值约定
令 a=c/(2m),判别量 d=a²−k/m。负值对应欠阻尼,零附近采用临界式,正值采用两个实根。临界附近的数值稳定需要明确处理,不能用浮点数严格相等决定分支。
下面代码返回位置与速度;没有使用“位置足够近就结束”的捷径。参数范围有意限定为有限正质量、正刚度与非负阻尼,不承诺任意极端数值都稳定。
import assert from 'node:assert/strict';
function spring(m,k,c,x0,v0,t){
if(![m,k,c,x0,v0,t].every(Number.isFinite) ||
m<=0||k<=0||c<0||t<0)throw Error('parameters');
const a=c/(2*m),d=a*a-k/m;
if(Math.abs(d)<1e-8){
const B=v0+a*x0,E=Math.exp(-a*t);
return {x:E*(x0+B*t),v:E*(v0-a*B*t)};
}
if(d<0){
const b=Math.sqrt(-d),C=Math.cos(b*t),S=Math.sin(b*t)/b;
const B=v0+a*x0,E=Math.exp(-a*t);
return {x:E*(x0*C+B*S),v:E*(v0*C+(d*x0-a*B)*S)};
}
const b=Math.sqrt(d),r1=-a+b,r2=-a-b;
const A=(v0-r2*x0)/(r1-r2),B=x0-A;
const p=A*Math.exp(r1*t),q=B*Math.exp(r2*t);
return {x:p+q,v:r1*p+r2*q};
}
const near=(a,b,eps=1e-7)=>assert.ok(Math.abs(a-b)<eps);
for(const c of [0,1,4,4-1e-8,4+1e-8,8]){
const at0=spring(1,4,c,7,-3,0);
near(at0.x,7);near(at0.v,-3);
const t=0.7,h=1e-6,s=spring(1,4,c,7,-3,t);
const derivative=(spring(1,4,c,7,-3,t+h).x-
spring(1,4,c,7,-3,t-h).x)/(2*h);
near(derivative,s.v,2e-5);
const continued=spring(1,4,c,s.x,s.v,0.4);
const direct=spring(1,4,c,7,-3,1.1);
near(continued.x,direct.x);near(continued.v,direct.v);
}
const crossing=spring(1,1,0,1,0,Math.PI/2);
assert.ok(Math.abs(crossing.x)<1e-6 && Math.abs(crossing.v)>0.9);
console.log('initial values, derivative, near-critical cases and interruption continuity passed');
中断时保留速度,不等于保留旧目标
若位置在 60px、速度为 −20px/s 时目标从 0 改成 100px,新段的 x0 应为 −40px,v0 仍为 −20px/s。先把位置变到新目标坐标系,再沿用速度。若直接从关键帧数组猜一个索引,时间采样误差也会进入新段,需要额外记录。
测试里的续接检查在目标不变时进行,验证两段求解与一次求解一致。目标变化的坐标换算已在文字中说明,但当前代码没有封装完整拖拽组件,也没有验证 WAAPI 在中断瞬间读取样式的行为。
停止条件同时看位置、速度与最长时限
无阻尼样例经过目标时速度仍很大;只检查位置会错误停止。低阻尼动画可能很久不收敛,生成关键帧时应设置最长时限,超时就标明截断策略。不能一边截断,一边把最后一帧写成“自然静止”。
abs(d)<1e-8 是此示例的近临界近似条件,不是通用数值库标准。极大阻尼、极长时间和不同量纲需要更稳健的缩放与误差分析。

图列出求解器需要检查的五类问题;没有把示意曲线或插画当成测量结果。
本文实际验证了初值、数值导数和同目标续接,尚未生成浏览器关键帧或评估视觉手感。解析解可以提供可信起点,但停止策略、输入连续性和资源收尾仍是组件作者的责任。
验证范围与资料
本地使用 v26.3.0 检查上述示例。纯 JavaScript 示例已在 Node 权限隔离模式执行,断言通过。Node 中执行了解析函数断言;临界附近采用近似分支,未覆盖极端数值范围、真实拖拽或 WAAPI 行为。
以下发布日期或版本日期与访问日期分开记录;24—31日的新消息须发稿前再核对。
Using the Web Animations API:2025-11-07(最后修改)。
How to create high-performance CSS animations:2020-10-06(最后更新)。
Web Animations 官方草案:2023-06-05,W3C Working Draft。
本文聚焦于使用弹簧解析解生成动画关键帧时的关键技术难点:初值设定(位置与速度在目标变更时的坐标系转换)、中断续接的正确性(保留速度但需重算相对位移)、以及多条件停止策略(同时判断位置、速度与最大时限)。强调数值稳定性、临界阻尼分支处理及Web Animations API集成中的常见陷阱,不依赖视觉直觉,而以数学一致性和可验证性为基准。

553

被折叠的 条评论
为什么被折叠?



