| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687 |
- import { spring } from './spring/index.mjs';
- import { calcGeneratorVelocity } from './utils/velocity.mjs';
- function inertia({ keyframes, velocity = 0.0, power = 0.8, timeConstant = 325, bounceDamping = 10, bounceStiffness = 500, modifyTarget, min, max, restDelta = 0.5, restSpeed, }) {
- const origin = keyframes[0];
- const state = {
- done: false,
- value: origin,
- };
- const isOutOfBounds = (v) => (min !== undefined && v < min) || (max !== undefined && v > max);
- const nearestBoundary = (v) => {
- if (min === undefined)
- return max;
- if (max === undefined)
- return min;
- return Math.abs(min - v) < Math.abs(max - v) ? min : max;
- };
- let amplitude = power * velocity;
- const ideal = origin + amplitude;
- const target = modifyTarget === undefined ? ideal : modifyTarget(ideal);
- /**
- * If the target has changed we need to re-calculate the amplitude, otherwise
- * the animation will start from the wrong position.
- */
- if (target !== ideal)
- amplitude = target - origin;
- const calcDelta = (t) => -amplitude * Math.exp(-t / timeConstant);
- const calcLatest = (t) => target + calcDelta(t);
- const applyFriction = (t) => {
- const delta = calcDelta(t);
- const latest = calcLatest(t);
- state.done = Math.abs(delta) <= restDelta;
- state.value = state.done ? target : latest;
- };
- /**
- * Ideally this would resolve for t in a stateless way, we could
- * do that by always precalculating the animation but as we know
- * this will be done anyway we can assume that spring will
- * be discovered during that.
- */
- let timeReachedBoundary;
- let spring$1;
- const checkCatchBoundary = (t) => {
- if (!isOutOfBounds(state.value))
- return;
- timeReachedBoundary = t;
- spring$1 = spring({
- keyframes: [state.value, nearestBoundary(state.value)],
- velocity: calcGeneratorVelocity(calcLatest, t, state.value), // TODO: This should be passing * 1000
- damping: bounceDamping,
- stiffness: bounceStiffness,
- restDelta,
- restSpeed,
- });
- };
- checkCatchBoundary(0);
- return {
- calculatedDuration: null,
- next: (t) => {
- /**
- * We need to resolve the friction to figure out if we need a
- * spring but we don't want to do this twice per frame. So here
- * we flag if we updated for this frame and later if we did
- * we can skip doing it again.
- */
- let hasUpdatedFrame = false;
- if (!spring$1 && timeReachedBoundary === undefined) {
- hasUpdatedFrame = true;
- applyFriction(t);
- checkCatchBoundary(t);
- }
- /**
- * If we have a spring and the provided t is beyond the moment the friction
- * animation crossed the min/max boundary, use the spring.
- */
- if (timeReachedBoundary !== undefined && t >= timeReachedBoundary) {
- return spring$1.next(t - timeReachedBoundary);
- }
- else {
- !hasUpdatedFrame && applyFriction(t);
- return state;
- }
- },
- };
- }
- export { inertia };
|