diff --git a/src/runtime/easing/index.ts b/src/runtime/easing/index.ts index c23019b320..d7871b67c6 100644 --- a/src/runtime/easing/index.ts +++ b/src/runtime/easing/index.ts @@ -1,3 +1,5 @@ +import { springFrames } from "svelte/motion"; + /* Adapted from https://github.com/mattdesl Distributed under MIT License https://github.com/mattdesl/eases/blob/master/LICENSE.md @@ -169,3 +171,35 @@ export function sineIn(t: number) { export function sineOut(t: number) { return Math.sin((t * Math.PI) / 2); } + +const springEasing = (frames, inDirection = true) => t => { + t = inDirection ? t : 1 - t; + const indexPrecise = t * frames.length; + const indexExcess = indexPrecise % 1; + + const a = frames[indexPrecise - indexExcess]; + let b = frames[indexPrecise - indexExcess + 1]; + if (b == null) { + b = a; + } + + return indexExcess ? a + (b - a) * indexExcess : a; +}; + +export function springEnter(from, to, opts) { + const frames = springFrames(from, to, opts); + + return { + duration: (frames.length * 1000) / 60, + easing: springEasing(frames) + }; +} + +export function springLeave(from, to, opts) { + const frames = springFrames(from, to, opts); + + return { + duration: (frames.length * 1000) / 60, + easing: springEasing(frames, false) + }; +} \ No newline at end of file diff --git a/src/runtime/motion/spring.ts b/src/runtime/motion/spring.ts index 5845a13a57..a59b363264 100644 --- a/src/runtime/motion/spring.ts +++ b/src/runtime/motion/spring.ts @@ -2,14 +2,41 @@ import { Readable, writable } from 'svelte/store'; import { loop, now, Task } from 'svelte/internal'; import { is_date } from './utils'; -interface TickContext { +interface TickContext { inv_mass: number; dt: number; - opts: Spring; + opts: SpringOpts; settled: boolean; } -function tick_spring(ctx: TickContext, last_value: T, current_value: T, target_value: T): T { +export function springFrames(from, to, opts: SpringOpts) { + Object.assign(opts, { stiffness: 0.15, damping: 0.8, precision: 0.01 }); + + let value = from; + let last_val = from; + let dt = 0.25; //WTF??? + const values = [from]; + + let ctx: TickContext; + do { + ctx = { + inv_mass: 1, + opts, + settled: true, + dt, + }; + + const next_value = tick_spring(ctx, last_val, value, to); + values.push(next_value); + last_val = value; + value = next_value; + dt = 1; + } while (!ctx.settled); + + return values; +} + +function tick_spring(ctx: TickContext, last_value: T, current_value: T, target_value: T): T { if (typeof current_value === 'number' || is_date(current_value)) { // @ts-ignore const delta = target_value - current_value; @@ -109,7 +136,7 @@ export function spring(value?: T, opts: SpringOpts = {}): Spring { inv_mass = Math.min(inv_mass + inv_mass_recovery_rate, 1); - const ctx: TickContext = { + const ctx: TickContext = { inv_mass, opts: spring, settled: true, // tick_spring may signal false