1/5/1404
This commit is contained in:
+19
@@ -0,0 +1,19 @@
|
||||
function decay({ velocity = 0, from = 0, power = 0.8, timeConstant = 350, restDelta = 0.5, modifyTarget, }) {
|
||||
const state = { done: false, value: from };
|
||||
let amplitude = power * velocity;
|
||||
const ideal = from + amplitude;
|
||||
const target = modifyTarget === undefined ? ideal : modifyTarget(ideal);
|
||||
if (target !== ideal)
|
||||
amplitude = target - from;
|
||||
return {
|
||||
next: (t) => {
|
||||
const delta = -amplitude * Math.exp(-t / timeConstant);
|
||||
state.done = !(delta > restDelta || delta < -restDelta);
|
||||
state.value = state.done ? target : target + delta;
|
||||
return state;
|
||||
},
|
||||
flipTarget: () => { },
|
||||
};
|
||||
}
|
||||
|
||||
export { decay };
|
||||
Generated
Vendored
+39
@@ -0,0 +1,39 @@
|
||||
import { interpolate } from '../../utils/interpolate.mjs';
|
||||
import { easeInOut } from '../../easing/index.mjs';
|
||||
|
||||
function defaultEasing(values, easing) {
|
||||
return values.map(() => easing || easeInOut).splice(0, values.length - 1);
|
||||
}
|
||||
function defaultOffset(values) {
|
||||
const numValues = values.length;
|
||||
return values.map((_value, i) => i !== 0 ? i / (numValues - 1) : 0);
|
||||
}
|
||||
function convertOffsetToTimes(offset, duration) {
|
||||
return offset.map((o) => o * duration);
|
||||
}
|
||||
function keyframes({ from = 0, to = 1, ease, offset, duration = 300, }) {
|
||||
const state = { done: false, value: from };
|
||||
const values = Array.isArray(to) ? to : [from, to];
|
||||
const times = convertOffsetToTimes(offset && offset.length === values.length
|
||||
? offset
|
||||
: defaultOffset(values), duration);
|
||||
function createInterpolator() {
|
||||
return interpolate(times, values, {
|
||||
ease: Array.isArray(ease) ? ease : defaultEasing(values, ease),
|
||||
});
|
||||
}
|
||||
let interpolator = createInterpolator();
|
||||
return {
|
||||
next: (t) => {
|
||||
state.value = interpolator(t);
|
||||
state.done = t >= duration;
|
||||
return state;
|
||||
},
|
||||
flipTarget: () => {
|
||||
values.reverse();
|
||||
interpolator = createInterpolator();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export { convertOffsetToTimes, defaultEasing, defaultOffset, keyframes };
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
import { __rest } from 'tslib';
|
||||
import { findSpring, calcAngularFreq } from '../utils/find-spring.mjs';
|
||||
|
||||
const durationKeys = ["duration", "bounce"];
|
||||
const physicsKeys = ["stiffness", "damping", "mass"];
|
||||
function isSpringType(options, keys) {
|
||||
return keys.some((key) => options[key] !== undefined);
|
||||
}
|
||||
function getSpringOptions(options) {
|
||||
let springOptions = Object.assign({ velocity: 0.0, stiffness: 100, damping: 10, mass: 1.0, isResolvedFromDuration: false }, options);
|
||||
if (!isSpringType(options, physicsKeys) &&
|
||||
isSpringType(options, durationKeys)) {
|
||||
const derived = findSpring(options);
|
||||
springOptions = Object.assign(Object.assign(Object.assign({}, springOptions), derived), { velocity: 0.0, mass: 1.0 });
|
||||
springOptions.isResolvedFromDuration = true;
|
||||
}
|
||||
return springOptions;
|
||||
}
|
||||
function spring(_a) {
|
||||
var { from = 0.0, to = 1.0, restSpeed = 2, restDelta } = _a, options = __rest(_a, ["from", "to", "restSpeed", "restDelta"]);
|
||||
const state = { done: false, value: from };
|
||||
let { stiffness, damping, mass, velocity, duration, isResolvedFromDuration, } = getSpringOptions(options);
|
||||
let resolveSpring = zero;
|
||||
let resolveVelocity = zero;
|
||||
function createSpring() {
|
||||
const initialVelocity = velocity ? -(velocity / 1000) : 0.0;
|
||||
const initialDelta = to - from;
|
||||
const dampingRatio = damping / (2 * Math.sqrt(stiffness * mass));
|
||||
const undampedAngularFreq = Math.sqrt(stiffness / mass) / 1000;
|
||||
if (restDelta === undefined) {
|
||||
restDelta = Math.min(Math.abs(to - from) / 100, 0.4);
|
||||
}
|
||||
if (dampingRatio < 1) {
|
||||
const angularFreq = calcAngularFreq(undampedAngularFreq, dampingRatio);
|
||||
resolveSpring = (t) => {
|
||||
const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
|
||||
return (to -
|
||||
envelope *
|
||||
(((initialVelocity +
|
||||
dampingRatio * undampedAngularFreq * initialDelta) /
|
||||
angularFreq) *
|
||||
Math.sin(angularFreq * t) +
|
||||
initialDelta * Math.cos(angularFreq * t)));
|
||||
};
|
||||
resolveVelocity = (t) => {
|
||||
const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
|
||||
return (dampingRatio *
|
||||
undampedAngularFreq *
|
||||
envelope *
|
||||
((Math.sin(angularFreq * t) *
|
||||
(initialVelocity +
|
||||
dampingRatio *
|
||||
undampedAngularFreq *
|
||||
initialDelta)) /
|
||||
angularFreq +
|
||||
initialDelta * Math.cos(angularFreq * t)) -
|
||||
envelope *
|
||||
(Math.cos(angularFreq * t) *
|
||||
(initialVelocity +
|
||||
dampingRatio *
|
||||
undampedAngularFreq *
|
||||
initialDelta) -
|
||||
angularFreq *
|
||||
initialDelta *
|
||||
Math.sin(angularFreq * t)));
|
||||
};
|
||||
}
|
||||
else if (dampingRatio === 1) {
|
||||
resolveSpring = (t) => to -
|
||||
Math.exp(-undampedAngularFreq * t) *
|
||||
(initialDelta +
|
||||
(initialVelocity + undampedAngularFreq * initialDelta) *
|
||||
t);
|
||||
}
|
||||
else {
|
||||
const dampedAngularFreq = undampedAngularFreq * Math.sqrt(dampingRatio * dampingRatio - 1);
|
||||
resolveSpring = (t) => {
|
||||
const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
|
||||
const freqForT = Math.min(dampedAngularFreq * t, 300);
|
||||
return (to -
|
||||
(envelope *
|
||||
((initialVelocity +
|
||||
dampingRatio * undampedAngularFreq * initialDelta) *
|
||||
Math.sinh(freqForT) +
|
||||
dampedAngularFreq *
|
||||
initialDelta *
|
||||
Math.cosh(freqForT))) /
|
||||
dampedAngularFreq);
|
||||
};
|
||||
}
|
||||
}
|
||||
createSpring();
|
||||
return {
|
||||
next: (t) => {
|
||||
const current = resolveSpring(t);
|
||||
if (!isResolvedFromDuration) {
|
||||
const currentVelocity = resolveVelocity(t) * 1000;
|
||||
const isBelowVelocityThreshold = Math.abs(currentVelocity) <= restSpeed;
|
||||
const isBelowDisplacementThreshold = Math.abs(to - current) <= restDelta;
|
||||
state.done =
|
||||
isBelowVelocityThreshold && isBelowDisplacementThreshold;
|
||||
}
|
||||
else {
|
||||
state.done = t >= duration;
|
||||
}
|
||||
state.value = state.done ? to : current;
|
||||
return state;
|
||||
},
|
||||
flipTarget: () => {
|
||||
velocity = -velocity;
|
||||
[from, to] = [to, from];
|
||||
createSpring();
|
||||
},
|
||||
};
|
||||
}
|
||||
spring.needsInterpolation = (a, b) => typeof a === "string" || typeof b === "string";
|
||||
const zero = (_t) => 0;
|
||||
|
||||
export { spring };
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
import { __rest } from 'tslib';
|
||||
import { detectAnimationFromOptions } from './utils/detect-animation-from-options.mjs';
|
||||
import sync, { cancelSync } from 'framesync';
|
||||
import { interpolate } from '../utils/interpolate.mjs';
|
||||
import { hasRepeatDelayElapsed, reverseElapsed, loopElapsed } from './utils/elapsed.mjs';
|
||||
|
||||
const framesync = (update) => {
|
||||
const passTimestamp = ({ delta }) => update(delta);
|
||||
return {
|
||||
start: () => sync.update(passTimestamp, true),
|
||||
stop: () => cancelSync.update(passTimestamp),
|
||||
};
|
||||
};
|
||||
function animate(_a) {
|
||||
var _b, _c;
|
||||
var { from, autoplay = true, driver = framesync, elapsed = 0, repeat: repeatMax = 0, repeatType = "loop", repeatDelay = 0, onPlay, onStop, onComplete, onRepeat, onUpdate } = _a, options = __rest(_a, ["from", "autoplay", "driver", "elapsed", "repeat", "repeatType", "repeatDelay", "onPlay", "onStop", "onComplete", "onRepeat", "onUpdate"]);
|
||||
let { to } = options;
|
||||
let driverControls;
|
||||
let repeatCount = 0;
|
||||
let computedDuration = options.duration;
|
||||
let latest;
|
||||
let isComplete = false;
|
||||
let isForwardPlayback = true;
|
||||
let interpolateFromNumber;
|
||||
const animator = detectAnimationFromOptions(options);
|
||||
if ((_c = (_b = animator).needsInterpolation) === null || _c === void 0 ? void 0 : _c.call(_b, from, to)) {
|
||||
interpolateFromNumber = interpolate([0, 100], [from, to], {
|
||||
clamp: false,
|
||||
});
|
||||
from = 0;
|
||||
to = 100;
|
||||
}
|
||||
const animation = animator(Object.assign(Object.assign({}, options), { from, to }));
|
||||
function repeat() {
|
||||
repeatCount++;
|
||||
if (repeatType === "reverse") {
|
||||
isForwardPlayback = repeatCount % 2 === 0;
|
||||
elapsed = reverseElapsed(elapsed, computedDuration, repeatDelay, isForwardPlayback);
|
||||
}
|
||||
else {
|
||||
elapsed = loopElapsed(elapsed, computedDuration, repeatDelay);
|
||||
if (repeatType === "mirror")
|
||||
animation.flipTarget();
|
||||
}
|
||||
isComplete = false;
|
||||
onRepeat && onRepeat();
|
||||
}
|
||||
function complete() {
|
||||
driverControls.stop();
|
||||
onComplete && onComplete();
|
||||
}
|
||||
function update(delta) {
|
||||
if (!isForwardPlayback)
|
||||
delta = -delta;
|
||||
elapsed += delta;
|
||||
if (!isComplete) {
|
||||
const state = animation.next(Math.max(0, elapsed));
|
||||
latest = state.value;
|
||||
if (interpolateFromNumber)
|
||||
latest = interpolateFromNumber(latest);
|
||||
isComplete = isForwardPlayback ? state.done : elapsed <= 0;
|
||||
}
|
||||
onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate(latest);
|
||||
if (isComplete) {
|
||||
if (repeatCount === 0)
|
||||
computedDuration !== null && computedDuration !== void 0 ? computedDuration : (computedDuration = elapsed);
|
||||
if (repeatCount < repeatMax) {
|
||||
hasRepeatDelayElapsed(elapsed, computedDuration, repeatDelay, isForwardPlayback) && repeat();
|
||||
}
|
||||
else {
|
||||
complete();
|
||||
}
|
||||
}
|
||||
}
|
||||
function play() {
|
||||
onPlay === null || onPlay === void 0 ? void 0 : onPlay();
|
||||
driverControls = driver(update);
|
||||
driverControls.start();
|
||||
}
|
||||
autoplay && play();
|
||||
return {
|
||||
stop: () => {
|
||||
onStop === null || onStop === void 0 ? void 0 : onStop();
|
||||
driverControls.stop();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export { animate };
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
import { animate } from './index.mjs';
|
||||
import { velocityPerSecond } from '../utils/velocity-per-second.mjs';
|
||||
import { getFrameData } from 'framesync';
|
||||
|
||||
function inertia({ from = 0, velocity = 0, min, max, power = 0.8, timeConstant = 750, bounceStiffness = 500, bounceDamping = 10, restDelta = 1, modifyTarget, driver, onUpdate, onComplete, onStop, }) {
|
||||
let currentAnimation;
|
||||
function isOutOfBounds(v) {
|
||||
return (min !== undefined && v < min) || (max !== undefined && v > max);
|
||||
}
|
||||
function boundaryNearest(v) {
|
||||
if (min === undefined)
|
||||
return max;
|
||||
if (max === undefined)
|
||||
return min;
|
||||
return Math.abs(min - v) < Math.abs(max - v) ? min : max;
|
||||
}
|
||||
function startAnimation(options) {
|
||||
currentAnimation === null || currentAnimation === void 0 ? void 0 : currentAnimation.stop();
|
||||
currentAnimation = animate(Object.assign(Object.assign({}, options), { driver, onUpdate: (v) => {
|
||||
var _a;
|
||||
onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate(v);
|
||||
(_a = options.onUpdate) === null || _a === void 0 ? void 0 : _a.call(options, v);
|
||||
}, onComplete,
|
||||
onStop }));
|
||||
}
|
||||
function startSpring(options) {
|
||||
startAnimation(Object.assign({ type: "spring", stiffness: bounceStiffness, damping: bounceDamping, restDelta }, options));
|
||||
}
|
||||
if (isOutOfBounds(from)) {
|
||||
startSpring({ from, velocity, to: boundaryNearest(from) });
|
||||
}
|
||||
else {
|
||||
let target = power * velocity + from;
|
||||
if (typeof modifyTarget !== "undefined")
|
||||
target = modifyTarget(target);
|
||||
const boundary = boundaryNearest(target);
|
||||
const heading = boundary === min ? -1 : 1;
|
||||
let prev;
|
||||
let current;
|
||||
const checkBoundary = (v) => {
|
||||
prev = current;
|
||||
current = v;
|
||||
velocity = velocityPerSecond(v - prev, getFrameData().delta);
|
||||
if ((heading === 1 && v > boundary) ||
|
||||
(heading === -1 && v < boundary)) {
|
||||
startSpring({ from: v, to: boundary, velocity });
|
||||
}
|
||||
};
|
||||
startAnimation({
|
||||
type: "decay",
|
||||
from,
|
||||
velocity,
|
||||
timeConstant,
|
||||
power,
|
||||
restDelta,
|
||||
modifyTarget,
|
||||
onUpdate: isOutOfBounds(target) ? checkBoundary : undefined,
|
||||
});
|
||||
}
|
||||
return {
|
||||
stop: () => currentAnimation === null || currentAnimation === void 0 ? void 0 : currentAnimation.stop(),
|
||||
};
|
||||
}
|
||||
|
||||
export { inertia };
|
||||
Generated
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
import { spring } from '../generators/spring.mjs';
|
||||
import { keyframes } from '../generators/keyframes.mjs';
|
||||
import { decay } from '../generators/decay.mjs';
|
||||
|
||||
const types = { keyframes, spring, decay };
|
||||
function detectAnimationFromOptions(config) {
|
||||
if (Array.isArray(config.to)) {
|
||||
return keyframes;
|
||||
}
|
||||
else if (types[config.type]) {
|
||||
return types[config.type];
|
||||
}
|
||||
const keys = new Set(Object.keys(config));
|
||||
if (keys.has("ease") ||
|
||||
(keys.has("duration") && !keys.has("dampingRatio"))) {
|
||||
return keyframes;
|
||||
}
|
||||
else if (keys.has("dampingRatio") ||
|
||||
keys.has("stiffness") ||
|
||||
keys.has("mass") ||
|
||||
keys.has("damping") ||
|
||||
keys.has("restSpeed") ||
|
||||
keys.has("restDelta")) {
|
||||
return spring;
|
||||
}
|
||||
return keyframes;
|
||||
}
|
||||
|
||||
export { detectAnimationFromOptions };
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
function loopElapsed(elapsed, duration, delay = 0) {
|
||||
return elapsed - duration - delay;
|
||||
}
|
||||
function reverseElapsed(elapsed, duration, delay = 0, isForwardPlayback = true) {
|
||||
return isForwardPlayback
|
||||
? loopElapsed(duration + -elapsed, duration, delay)
|
||||
: duration - (elapsed - duration) + delay;
|
||||
}
|
||||
function hasRepeatDelayElapsed(elapsed, duration, delay, isForwardPlayback) {
|
||||
return isForwardPlayback ? elapsed >= duration + delay : elapsed <= -delay;
|
||||
}
|
||||
|
||||
export { hasRepeatDelayElapsed, loopElapsed, reverseElapsed };
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
import { warning } from 'hey-listen';
|
||||
import { clamp } from '../../utils/clamp.mjs';
|
||||
|
||||
const safeMin = 0.001;
|
||||
const minDuration = 0.01;
|
||||
const maxDuration = 10.0;
|
||||
const minDamping = 0.05;
|
||||
const maxDamping = 1;
|
||||
function findSpring({ duration = 800, bounce = 0.25, velocity = 0, mass = 1, }) {
|
||||
let envelope;
|
||||
let derivative;
|
||||
warning(duration <= maxDuration * 1000, "Spring duration must be 10 seconds or less");
|
||||
let dampingRatio = 1 - bounce;
|
||||
dampingRatio = clamp(minDamping, maxDamping, dampingRatio);
|
||||
duration = clamp(minDuration, maxDuration, duration / 1000);
|
||||
if (dampingRatio < 1) {
|
||||
envelope = (undampedFreq) => {
|
||||
const exponentialDecay = undampedFreq * dampingRatio;
|
||||
const delta = exponentialDecay * duration;
|
||||
const a = exponentialDecay - velocity;
|
||||
const b = calcAngularFreq(undampedFreq, dampingRatio);
|
||||
const c = Math.exp(-delta);
|
||||
return safeMin - (a / b) * c;
|
||||
};
|
||||
derivative = (undampedFreq) => {
|
||||
const exponentialDecay = undampedFreq * dampingRatio;
|
||||
const delta = exponentialDecay * duration;
|
||||
const d = delta * velocity + velocity;
|
||||
const e = Math.pow(dampingRatio, 2) * Math.pow(undampedFreq, 2) * duration;
|
||||
const f = Math.exp(-delta);
|
||||
const g = calcAngularFreq(Math.pow(undampedFreq, 2), dampingRatio);
|
||||
const factor = -envelope(undampedFreq) + safeMin > 0 ? -1 : 1;
|
||||
return (factor * ((d - e) * f)) / g;
|
||||
};
|
||||
}
|
||||
else {
|
||||
envelope = (undampedFreq) => {
|
||||
const a = Math.exp(-undampedFreq * duration);
|
||||
const b = (undampedFreq - velocity) * duration + 1;
|
||||
return -safeMin + a * b;
|
||||
};
|
||||
derivative = (undampedFreq) => {
|
||||
const a = Math.exp(-undampedFreq * duration);
|
||||
const b = (velocity - undampedFreq) * (duration * duration);
|
||||
return a * b;
|
||||
};
|
||||
}
|
||||
const initialGuess = 5 / duration;
|
||||
const undampedFreq = approximateRoot(envelope, derivative, initialGuess);
|
||||
duration = duration * 1000;
|
||||
if (isNaN(undampedFreq)) {
|
||||
return {
|
||||
stiffness: 100,
|
||||
damping: 10,
|
||||
duration,
|
||||
};
|
||||
}
|
||||
else {
|
||||
const stiffness = Math.pow(undampedFreq, 2) * mass;
|
||||
return {
|
||||
stiffness,
|
||||
damping: dampingRatio * 2 * Math.sqrt(mass * stiffness),
|
||||
duration,
|
||||
};
|
||||
}
|
||||
}
|
||||
const rootIterations = 12;
|
||||
function approximateRoot(envelope, derivative, initialGuess) {
|
||||
let result = initialGuess;
|
||||
for (let i = 1; i < rootIterations; i++) {
|
||||
result = result - envelope(result) / derivative(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function calcAngularFreq(undampedFreq, dampingRatio) {
|
||||
return undampedFreq * Math.sqrt(1 - dampingRatio * dampingRatio);
|
||||
}
|
||||
|
||||
export { calcAngularFreq, findSpring, maxDamping, maxDuration, minDamping, minDuration };
|
||||
Reference in New Issue
Block a user