diff options
Diffstat (limited to 'apps/web/src/lib/animation/bezier.ts')
| -rw-r--r-- | apps/web/src/lib/animation/bezier.ts | 88 |
1 files changed, 88 insertions, 0 deletions
diff --git a/apps/web/src/lib/animation/bezier.ts b/apps/web/src/lib/animation/bezier.ts new file mode 100644 index 00000000..4dce027e --- /dev/null +++ b/apps/web/src/lib/animation/bezier.ts @@ -0,0 +1,88 @@ +import type { ScalarAnimationKey } from "@/lib/animation/types"; + +export function getBezierPoint({ + progress, + p0, + p1, + p2, + p3, +}: { + progress: number; + p0: number; + p1: number; + p2: number; + p3: number; +}) { + const mt = 1 - progress; + return ( + mt * mt * mt * p0 + + 3 * mt * mt * progress * p1 + + 3 * mt * progress * progress * p2 + + progress * progress * progress * p3 + ); +} + +export function getDefaultRightHandle({ + leftKey, + rightKey, +}: { + leftKey: ScalarAnimationKey; + rightKey: ScalarAnimationKey; +}) { + const span = rightKey.time - leftKey.time; + const valueDelta = rightKey.value - leftKey.value; + return { + dt: span / 3, + dv: valueDelta / 3, + }; +} + +export function getDefaultLeftHandle({ + leftKey, + rightKey, +}: { + leftKey: ScalarAnimationKey; + rightKey: ScalarAnimationKey; +}) { + const span = rightKey.time - leftKey.time; + const valueDelta = rightKey.value - leftKey.value; + return { + dt: -span / 3, + dv: -valueDelta / 3, + }; +} + +export function solveBezierProgressForTime({ + time, + leftKey, + rightKey, +}: { + time: number; + leftKey: ScalarAnimationKey; + rightKey: ScalarAnimationKey; +}) { + let lower = 0; + let upper = 1; + const rightHandle = + leftKey.rightHandle ?? getDefaultRightHandle({ leftKey, rightKey }); + const leftHandle = + rightKey.leftHandle ?? getDefaultLeftHandle({ leftKey, rightKey }); + + for (let iteration = 0; iteration < 20; iteration++) { + const mid = (lower + upper) / 2; + const estimate = getBezierPoint({ + progress: mid, + p0: leftKey.time, + p1: leftKey.time + rightHandle.dt, + p2: rightKey.time + leftHandle.dt, + p3: rightKey.time, + }); + if (estimate < time) { + lower = mid; + } else { + upper = mid; + } + } + + return (lower + upper) / 2; +} |
