求可接收currentTime/currentFrame的JavaScript独立弹簧动画核心计算函数
独立弹簧状态计算函数实现(支持任意时间帧查询)
你需要的是一个纯函数式的弹簧计算实现,基于弹簧运动的解析解而非逐帧数值积分,能直接根据时间参数返回对应帧的状态,完美适配动画编辑器的回退、快进、拖动查看需求。
核心实现代码
// 弹簧缓动计算函数,返回指定时间点的目标值 function springEasing(t, { from = 0, to = 1, stiffness = 100, damping = 10, mass = 1, initialVelocity = 0 }) { const delta = to - from; const omega0 = Math.sqrt(stiffness / mass); const zeta = damping / (2 * Math.sqrt(stiffness * mass)); // 阻尼比 const omegaD = omega0 * Math.sqrt(1 - zeta ** 2); let x; if (zeta < 1) { // 欠阻尼(自然弹簧震动效果) const envelope = Math.exp(-zeta * omega0 * t); x = envelope * ( Math.cos(omegaD * t) + (zeta * omega0 / omegaD) * Math.sin(omegaD * t) ); } else if (zeta === 1) { // 临界阻尼(最快回到目标值无震动) x = (1 + omega0 * t) * Math.exp(-omega0 * t); } else { // 过阻尼(缓慢回到目标值无震动) const omegaPrime = omega0 * Math.sqrt(zeta ** 2 - 1); const exp1 = Math.exp((-zeta + Math.sqrt(zeta ** 2 - 1)) * omega0 * t); const exp2 = Math.exp((-zeta - Math.sqrt(zeta ** 2 - 1)) * omega0 * t); x = (exp1 + exp2) / 2; } // 修正初始速度影响 if (initialVelocity !== 0) { const v0Factor = initialVelocity / (omega0 * delta); if (zeta < 1) { x += envelope * v0Factor * Math.sin(omegaD * t); } else if (zeta === 1) { x += v0Factor * t * Math.exp(-omega0 * t); } else { x += v0Factor * (exp1 - exp2) / 2; } } return from + delta * (1 - x); }
参数说明
t: 动画运行时间(单位:秒,比如0.8表示动画开始后800毫秒)from: 动画起始数值to: 动画目标数值stiffness: 弹簧刚度,值越大弹簧回弹越猛damping: 阻尼系数,值越大震动衰减越快mass: 运动质量,值越大运动惯性越强initialVelocity: 初始速度,可选,默认0(用于模拟初始推力)
使用示例
// 场景:动画编辑器拖动时间轴到2.5秒处,查询元素位置 const currentPosition = springEasing(2.5, { from: 0, to: 300, stiffness: 150, damping: 20, mass: 1.2 }); console.log(currentPosition); // 输出该时间点的精确位置
为什么用解析解而非数值积分?
你提供的Vue代码采用逐帧数值积分方式,适合实时渲染动画,但无法直接跳转到任意时间点(必须从起始帧逐帧计算到目标帧)。而解析解基于物理公式直接计算任意时间点的状态,完全满足动画编辑器的时间轴操作需求,同时计算效率更高。
内容的提问来源于stack exchange,提问作者Lance Pollard
相关产品推荐
相关产品推荐

