UNPKG

remotion

Version:

Make videos programmatically

106 lines (105 loc) 3.58 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.springCalculation = springCalculation; const defaultSpringConfig = { damping: 10, mass: 1, stiffness: 100, overshootClamping: false, }; const advanceCache = {}; function advance({ animation, now, config, }) { const { toValue, lastTimestamp, current, velocity } = animation; const deltaTime = Math.min(now - lastTimestamp, 64); if (config.damping <= 0) { throw new Error('Spring damping must be greater than 0, otherwise the spring() animation will never end, causing an infinite loop.'); } const c = config.damping; const m = config.mass; const k = config.stiffness; const cacheKey = [ toValue, lastTimestamp, current, velocity, c, m, k, now, ].join('-'); if (advanceCache[cacheKey]) { return advanceCache[cacheKey]; } const v0 = -velocity; const x0 = toValue - current; const zeta = c / (2 * Math.sqrt(k * m)); // damping ratio const omega0 = Math.sqrt(k / m); // undamped angular frequency of the oscillator (rad/ms) const omega1 = omega0 * Math.sqrt(1 - zeta ** 2); // exponential decay const t = deltaTime / 1000; const sin1 = Math.sin(omega1 * t); const cos1 = Math.cos(omega1 * t); // under damped const underDampedEnvelope = Math.exp(-zeta * omega0 * t); const underDampedFrag1 = underDampedEnvelope * (sin1 * ((v0 + zeta * omega0 * x0) / omega1) + x0 * cos1); const underDampedPosition = toValue - underDampedFrag1; // This looks crazy -- it's actually just the derivative of the oscillation function const underDampedVelocity = zeta * omega0 * underDampedFrag1 - underDampedEnvelope * (cos1 * (v0 + zeta * omega0 * x0) - omega1 * x0 * sin1); // critically damped const criticallyDampedEnvelope = Math.exp(-omega0 * t); const criticallyDampedPosition = toValue - criticallyDampedEnvelope * (x0 + (v0 + omega0 * x0) * t); const criticallyDampedVelocity = criticallyDampedEnvelope * (v0 * (t * omega0 - 1) + t * x0 * omega0 * omega0); const animationNode = { toValue, prevPosition: current, lastTimestamp: now, current: zeta < 1 ? underDampedPosition : criticallyDampedPosition, velocity: zeta < 1 ? underDampedVelocity : criticallyDampedVelocity, }; advanceCache[cacheKey] = animationNode; return animationNode; } const calculationCache = {}; function springCalculation({ frame, fps, config = {}, }) { const from = 0; const to = 1; const cacheKey = [ frame, fps, config.damping, config.mass, config.overshootClamping, config.stiffness, ].join('-'); if (calculationCache[cacheKey]) { return calculationCache[cacheKey]; } let animation = { lastTimestamp: 0, current: from, toValue: to, velocity: 0, prevPosition: 0, }; const frameClamped = Math.max(0, frame); const unevenRest = frameClamped % 1; for (let f = 0; f <= Math.floor(frameClamped); f++) { if (f === Math.floor(frameClamped)) { f += unevenRest; } const time = (f / fps) * 1000; animation = advance({ animation, now: time, config: { ...defaultSpringConfig, ...config, }, }); } calculationCache[cacheKey] = animation; return animation; }