feature: vor und nachteile

This commit is contained in:
Hikyu
2026-05-31 13:54:31 +02:00
parent 552a947a1c
commit e3dad17446
23270 changed files with 3024794 additions and 200 deletions
+1726
View File
File diff suppressed because it is too large Load Diff
+9
View File
@@ -0,0 +1,9 @@
The MIT License (MIT)
Copyright © 2019 Framer BV
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+1
View File
@@ -0,0 +1 @@
# <a href="https://popmotion.io"><img src="https://cloud.githubusercontent.com/assets/7850794/21642571/1910a15e-d27b-11e6-84c7-19e88e207c14.png" height="52" width="243" alt="Popmotion" /></a>
@@ -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 };
@@ -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 };
@@ -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
View File
@@ -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
View File
@@ -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 };
@@ -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 };
@@ -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 };
@@ -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 };
+74
View File
@@ -0,0 +1,74 @@
import { linear } from './index.mjs';
const a = (a1, a2) => 1.0 - 3.0 * a2 + 3.0 * a1;
const b = (a1, a2) => 3.0 * a2 - 6.0 * a1;
const c = (a1) => 3.0 * a1;
const calcBezier = (t, a1, a2) => ((a(a1, a2) * t + b(a1, a2)) * t + c(a1)) * t;
const getSlope = (t, a1, a2) => 3.0 * a(a1, a2) * t * t + 2.0 * b(a1, a2) * t + c(a1);
const subdivisionPrecision = 0.0000001;
const subdivisionMaxIterations = 10;
function binarySubdivide(aX, aA, aB, mX1, mX2) {
let currentX;
let currentT;
let i = 0;
do {
currentT = aA + (aB - aA) / 2.0;
currentX = calcBezier(currentT, mX1, mX2) - aX;
if (currentX > 0.0) {
aB = currentT;
}
else {
aA = currentT;
}
} while (Math.abs(currentX) > subdivisionPrecision &&
++i < subdivisionMaxIterations);
return currentT;
}
const newtonIterations = 8;
const newtonMinSlope = 0.001;
function newtonRaphsonIterate(aX, aGuessT, mX1, mX2) {
for (let i = 0; i < newtonIterations; ++i) {
const currentSlope = getSlope(aGuessT, mX1, mX2);
if (currentSlope === 0.0) {
return aGuessT;
}
const currentX = calcBezier(aGuessT, mX1, mX2) - aX;
aGuessT -= currentX / currentSlope;
}
return aGuessT;
}
const kSplineTableSize = 11;
const kSampleStepSize = 1.0 / (kSplineTableSize - 1.0);
function cubicBezier(mX1, mY1, mX2, mY2) {
if (mX1 === mY1 && mX2 === mY2)
return linear;
const sampleValues = new Float32Array(kSplineTableSize);
for (let i = 0; i < kSplineTableSize; ++i) {
sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2);
}
function getTForX(aX) {
let intervalStart = 0.0;
let currentSample = 1;
const lastSample = kSplineTableSize - 1;
for (; currentSample !== lastSample && sampleValues[currentSample] <= aX; ++currentSample) {
intervalStart += kSampleStepSize;
}
--currentSample;
const dist = (aX - sampleValues[currentSample]) /
(sampleValues[currentSample + 1] - sampleValues[currentSample]);
const guessForT = intervalStart + dist * kSampleStepSize;
const initialSlope = getSlope(guessForT, mX1, mX2);
if (initialSlope >= newtonMinSlope) {
return newtonRaphsonIterate(aX, guessForT, mX1, mX2);
}
else if (initialSlope === 0.0) {
return guessForT;
}
else {
return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, mX1, mX2);
}
}
return (t) => t === 0 || t === 1 ? t : calcBezier(getTForX(t), mY1, mY2);
}
export { cubicBezier };
+38
View File
@@ -0,0 +1,38 @@
import { createExpoIn, reverseEasing, mirrorEasing, createBackIn, createAnticipate } from './utils.mjs';
const DEFAULT_OVERSHOOT_STRENGTH = 1.525;
const BOUNCE_FIRST_THRESHOLD = 4.0 / 11.0;
const BOUNCE_SECOND_THRESHOLD = 8.0 / 11.0;
const BOUNCE_THIRD_THRESHOLD = 9.0 / 10.0;
const linear = p => p;
const easeIn = createExpoIn(2);
const easeOut = reverseEasing(easeIn);
const easeInOut = mirrorEasing(easeIn);
const circIn = p => 1 - Math.sin(Math.acos(p));
const circOut = reverseEasing(circIn);
const circInOut = mirrorEasing(circOut);
const backIn = createBackIn(DEFAULT_OVERSHOOT_STRENGTH);
const backOut = reverseEasing(backIn);
const backInOut = mirrorEasing(backIn);
const anticipate = createAnticipate(DEFAULT_OVERSHOOT_STRENGTH);
const ca = 4356.0 / 361.0;
const cb = 35442.0 / 1805.0;
const cc = 16061.0 / 1805.0;
const bounceOut = (p) => {
if (p === 1 || p === 0)
return p;
const p2 = p * p;
return p < BOUNCE_FIRST_THRESHOLD
? 7.5625 * p2
: p < BOUNCE_SECOND_THRESHOLD
? 9.075 * p2 - 9.9 * p + 3.4
: p < BOUNCE_THIRD_THRESHOLD
? ca * p2 - cb * p + cc
: 10.8 * p * p - 20.52 * p + 10.72;
};
const bounceIn = reverseEasing(bounceOut);
const bounceInOut = (p) => p < 0.5
? 0.5 * (1.0 - bounceOut(1.0 - p * 2.0))
: 0.5 * bounceOut(p * 2.0 - 1.0) + 0.5;
export { anticipate, backIn, backInOut, backOut, bounceIn, bounceInOut, bounceOut, circIn, circInOut, circOut, easeIn, easeInOut, easeOut, linear };
+11
View File
@@ -0,0 +1,11 @@
import { clamp } from '../utils/clamp.mjs';
const steps = (steps, direction = 'end') => (progress) => {
progress =
direction === 'end' ? Math.min(progress, 0.999) : Math.max(progress, 0.001);
const expanded = progress * steps;
const rounded = direction === 'end' ? Math.floor(expanded) : Math.ceil(expanded);
return clamp(0, 1, rounded / steps);
};
export { steps };
+12
View File
@@ -0,0 +1,12 @@
const reverseEasing = easing => p => 1 - easing(1 - p);
const mirrorEasing = easing => p => p <= 0.5 ? easing(2 * p) / 2 : (2 - easing(2 * (1 - p))) / 2;
const createExpoIn = (power) => p => Math.pow(p, power);
const createBackIn = (power) => p => p * p * ((power + 1) * p - power);
const createAnticipate = (power) => {
const backEasing = createBackIn(power);
return p => (p *= 2) < 1
? 0.5 * backEasing(p)
: 0.5 * (2 - Math.pow(2, -10 * (p - 1)));
};
export { createAnticipate, createBackIn, createExpoIn, mirrorEasing, reverseEasing };
+32
View File
@@ -0,0 +1,32 @@
export { animate } from './animations/index.mjs';
export { inertia } from './animations/inertia.mjs';
export { decay } from './animations/generators/decay.mjs';
export { spring } from './animations/generators/spring.mjs';
export { keyframes } from './animations/generators/keyframes.mjs';
export { angle } from './utils/angle.mjs';
export { applyOffset } from './utils/apply-offset.mjs';
export { attract, attractExpo, createAttractor } from './utils/attract.mjs';
export { clamp } from './utils/clamp.mjs';
export { degreesToRadians } from './utils/degrees-to-radians.mjs';
export { distance } from './utils/distance.mjs';
export { interpolate } from './utils/interpolate.mjs';
export { isPoint3D } from './utils/is-point-3d.mjs';
export { isPoint } from './utils/is-point.mjs';
export { mixColor } from './utils/mix-color.mjs';
export { mixComplex } from './utils/mix-complex.mjs';
export { mix } from './utils/mix.mjs';
export { pipe } from './utils/pipe.mjs';
export { pointFromVector } from './utils/point-from-vector.mjs';
export { progress } from './utils/progress.mjs';
export { radiansToDegrees } from './utils/radians-to-degrees.mjs';
export { smoothFrame } from './utils/smooth-frame.mjs';
export { smooth } from './utils/smooth.mjs';
export { snap } from './utils/snap.mjs';
export { toDecimal } from './utils/to-decimal.mjs';
export { velocityPerFrame } from './utils/velocity-per-frame.mjs';
export { velocityPerSecond } from './utils/velocity-per-second.mjs';
export { wrap } from './utils/wrap.mjs';
export { anticipate, backIn, backInOut, backOut, bounceIn, bounceInOut, bounceOut, circIn, circInOut, circOut, easeIn, easeInOut, easeOut, linear } from './easing/index.mjs';
export { cubicBezier } from './easing/cubic-bezier.mjs';
export { steps } from './easing/steps.mjs';
export { createAnticipate, createBackIn, createExpoIn, mirrorEasing, reverseEasing } from './easing/utils.mjs';
+6
View File
@@ -0,0 +1,6 @@
import { radiansToDegrees } from './radians-to-degrees.mjs';
import { zeroPoint } from './inc.mjs';
const angle = (a, b = zeroPoint) => radiansToDegrees(Math.atan2(b.y - a.y, b.x - a.x));
export { angle };
+19
View File
@@ -0,0 +1,19 @@
const applyOffset = (from, to) => {
let hasReceivedFrom = true;
if (to === undefined) {
to = from;
hasReceivedFrom = false;
}
return (v) => {
if (hasReceivedFrom) {
return v - from + to;
}
else {
from = v;
hasReceivedFrom = true;
return to;
}
};
};
export { applyOffset };
+12
View File
@@ -0,0 +1,12 @@
const identity = (v) => v;
const createAttractor = (alterDisplacement = identity) => (constant, origin, v) => {
const displacement = origin - v;
const springModifiedDisplacement = -(0 - constant + 1) * (0 - alterDisplacement(Math.abs(displacement)));
return displacement <= 0
? origin + springModifiedDisplacement
: origin - springModifiedDisplacement;
};
const attract = createAttractor();
const attractExpo = createAttractor(Math.sqrt);
export { attract, attractExpo, createAttractor };
+3
View File
@@ -0,0 +1,3 @@
const clamp = (min, max, v) => Math.min(Math.max(v, min), max);
export { clamp };
@@ -0,0 +1,3 @@
const degreesToRadians = (degrees) => (degrees * Math.PI) / 180;
export { degreesToRadians };
+18
View File
@@ -0,0 +1,18 @@
import { isPoint } from './is-point.mjs';
import { isPoint3D } from './is-point-3d.mjs';
import { isNum } from './inc.mjs';
const distance1D = (a, b) => Math.abs(a - b);
function distance(a, b) {
if (isNum(a) && isNum(b)) {
return distance1D(a, b);
}
else if (isPoint(a) && isPoint(b)) {
const xDelta = distance1D(a.x, b.x);
const yDelta = distance1D(a.y, b.y);
const zDelta = isPoint3D(a) && isPoint3D(b) ? distance1D(a.z, b.z) : 0;
return Math.sqrt(Math.pow(xDelta, 2) + Math.pow(yDelta, 2) + Math.pow(zDelta, 2));
}
}
export { distance };
+41
View File
@@ -0,0 +1,41 @@
function hueToRgb(p, q, t) {
if (t < 0)
t += 1;
if (t > 1)
t -= 1;
if (t < 1 / 6)
return p + (q - p) * 6 * t;
if (t < 1 / 2)
return q;
if (t < 2 / 3)
return p + (q - p) * (2 / 3 - t) * 6;
return p;
}
function hslaToRgba({ hue, saturation, lightness, alpha }) {
hue /= 360;
saturation /= 100;
lightness /= 100;
let red = 0;
let green = 0;
let blue = 0;
if (!saturation) {
red = green = blue = lightness;
}
else {
const q = lightness < 0.5
? lightness * (1 + saturation)
: lightness + saturation - lightness * saturation;
const p = 2 * lightness - q;
red = hueToRgb(p, q, hue + 1 / 3);
green = hueToRgb(p, q, hue);
blue = hueToRgb(p, q, hue - 1 / 3);
}
return {
red: Math.round(red * 255),
green: Math.round(green * 255),
blue: Math.round(blue * 255),
alpha,
};
}
export { hslaToRgba };
+8
View File
@@ -0,0 +1,8 @@
const zeroPoint = {
x: 0,
y: 0,
z: 0
};
const isNum = (v) => typeof v === 'number';
export { isNum, zeroPoint };
+92
View File
@@ -0,0 +1,92 @@
import { progress } from './progress.mjs';
import { mix } from './mix.mjs';
import { mixColor } from './mix-color.mjs';
import { mixComplex, mixArray, mixObject } from './mix-complex.mjs';
import { color } from 'style-value-types';
import { clamp } from './clamp.mjs';
import { pipe } from './pipe.mjs';
import { invariant } from 'hey-listen';
const mixNumber = (from, to) => (p) => mix(from, to, p);
function detectMixerFactory(v) {
if (typeof v === 'number') {
return mixNumber;
}
else if (typeof v === 'string') {
if (color.test(v)) {
return mixColor;
}
else {
return mixComplex;
}
}
else if (Array.isArray(v)) {
return mixArray;
}
else if (typeof v === 'object') {
return mixObject;
}
}
function createMixers(output, ease, customMixer) {
const mixers = [];
const mixerFactory = customMixer || detectMixerFactory(output[0]);
const numMixers = output.length - 1;
for (let i = 0; i < numMixers; i++) {
let mixer = mixerFactory(output[i], output[i + 1]);
if (ease) {
const easingFunction = Array.isArray(ease) ? ease[i] : ease;
mixer = pipe(easingFunction, mixer);
}
mixers.push(mixer);
}
return mixers;
}
function fastInterpolate([from, to], [mixer]) {
return (v) => mixer(progress(from, to, v));
}
function slowInterpolate(input, mixers) {
const inputLength = input.length;
const lastInputIndex = inputLength - 1;
return (v) => {
let mixerIndex = 0;
let foundMixerIndex = false;
if (v <= input[0]) {
foundMixerIndex = true;
}
else if (v >= input[lastInputIndex]) {
mixerIndex = lastInputIndex - 1;
foundMixerIndex = true;
}
if (!foundMixerIndex) {
let i = 1;
for (; i < inputLength; i++) {
if (input[i] > v || i === lastInputIndex) {
break;
}
}
mixerIndex = i - 1;
}
const progressInRange = progress(input[mixerIndex], input[mixerIndex + 1], v);
return mixers[mixerIndex](progressInRange);
};
}
function interpolate(input, output, { clamp: isClamp = true, ease, mixer } = {}) {
const inputLength = input.length;
invariant(inputLength === output.length, 'Both input and output ranges must be the same length');
invariant(!ease || !Array.isArray(ease) || ease.length === inputLength - 1, 'Array of easing functions must be of length `input.length - 1`, as it applies to the transitions **between** the defined values.');
if (input[0] > input[inputLength - 1]) {
input = [].concat(input);
output = [].concat(output);
input.reverse();
output.reverse();
}
const mixers = createMixers(output, ease, mixer);
const interpolator = inputLength === 2
? fastInterpolate(input, mixers)
: slowInterpolate(input, mixers);
return isClamp
? (v) => interpolator(clamp(input[0], input[inputLength - 1], v))
: interpolator;
}
export { interpolate };
+5
View File
@@ -0,0 +1,5 @@
import { isPoint } from './is-point.mjs';
const isPoint3D = (point) => isPoint(point) && point.hasOwnProperty('z');
export { isPoint3D };
+3
View File
@@ -0,0 +1,3 @@
const isPoint = (point) => point.hasOwnProperty('x') && point.hasOwnProperty('y');
export { isPoint };
+41
View File
@@ -0,0 +1,41 @@
import { mix } from './mix.mjs';
import { hsla, rgba, hex } from 'style-value-types';
import { invariant } from 'hey-listen';
import { hslaToRgba } from './hsla-to-rgba.mjs';
const mixLinearColor = (from, to, v) => {
const fromExpo = from * from;
const toExpo = to * to;
return Math.sqrt(Math.max(0, v * (toExpo - fromExpo) + fromExpo));
};
const colorTypes = [hex, rgba, hsla];
const getColorType = (v) => colorTypes.find((type) => type.test(v));
const notAnimatable = (color) => `'${color}' is not an animatable color. Use the equivalent color code instead.`;
const mixColor = (from, to) => {
let fromColorType = getColorType(from);
let toColorType = getColorType(to);
invariant(!!fromColorType, notAnimatable(from));
invariant(!!toColorType, notAnimatable(to));
let fromColor = fromColorType.parse(from);
let toColor = toColorType.parse(to);
if (fromColorType === hsla) {
fromColor = hslaToRgba(fromColor);
fromColorType = rgba;
}
if (toColorType === hsla) {
toColor = hslaToRgba(toColor);
toColorType = rgba;
}
const blended = Object.assign({}, fromColor);
return (v) => {
for (const key in blended) {
if (key !== "alpha") {
blended[key] = mixLinearColor(fromColor[key], toColor[key], v);
}
}
blended.alpha = mix(fromColor.alpha, toColor.alpha, v);
return fromColorType.transform(blended);
};
};
export { mixColor, mixLinearColor };
+82
View File
@@ -0,0 +1,82 @@
import { complex, color } from 'style-value-types';
import { mix } from './mix.mjs';
import { mixColor } from './mix-color.mjs';
import { isNum } from './inc.mjs';
import { pipe } from './pipe.mjs';
import { warning } from 'hey-listen';
function getMixer(origin, target) {
if (isNum(origin)) {
return (v) => mix(origin, target, v);
}
else if (color.test(origin)) {
return mixColor(origin, target);
}
else {
return mixComplex(origin, target);
}
}
const mixArray = (from, to) => {
const output = [...from];
const numValues = output.length;
const blendValue = from.map((fromThis, i) => getMixer(fromThis, to[i]));
return (v) => {
for (let i = 0; i < numValues; i++) {
output[i] = blendValue[i](v);
}
return output;
};
};
const mixObject = (origin, target) => {
const output = Object.assign(Object.assign({}, origin), target);
const blendValue = {};
for (const key in output) {
if (origin[key] !== undefined && target[key] !== undefined) {
blendValue[key] = getMixer(origin[key], target[key]);
}
}
return (v) => {
for (const key in blendValue) {
output[key] = blendValue[key](v);
}
return output;
};
};
function analyse(value) {
const parsed = complex.parse(value);
const numValues = parsed.length;
let numNumbers = 0;
let numRGB = 0;
let numHSL = 0;
for (let i = 0; i < numValues; i++) {
if (numNumbers || typeof parsed[i] === "number") {
numNumbers++;
}
else {
if (parsed[i].hue !== undefined) {
numHSL++;
}
else {
numRGB++;
}
}
}
return { parsed, numNumbers, numRGB, numHSL };
}
const mixComplex = (origin, target) => {
const template = complex.createTransformer(target);
const originStats = analyse(origin);
const targetStats = analyse(target);
const canInterpolate = originStats.numHSL === targetStats.numHSL &&
originStats.numRGB === targetStats.numRGB &&
originStats.numNumbers >= targetStats.numNumbers;
if (canInterpolate) {
return pipe(mixArray(originStats.parsed, targetStats.parsed), template);
}
else {
warning(true, `Complex values '${origin}' and '${target}' too different to mix. Ensure all colors are of the same type, and that each contains the same quantity of number and color values. Falling back to instant transition.`);
return (p) => `${p > 0 ? target : origin}`;
}
};
export { mixArray, mixComplex, mixObject };
+3
View File
@@ -0,0 +1,3 @@
const mix = (from, to, progress) => -progress * from + progress * to + from;
export { mix };
+4
View File
@@ -0,0 +1,4 @@
const combineFunctions = (a, b) => (v) => b(a(v));
const pipe = (...transformers) => transformers.reduce(combineFunctions);
export { pipe };
@@ -0,0 +1,11 @@
import { degreesToRadians } from './degrees-to-radians.mjs';
const pointFromVector = (origin, angle, distance) => {
angle = degreesToRadians(angle);
return {
x: distance * Math.cos(angle) + origin.x,
y: distance * Math.sin(angle) + origin.y
};
};
export { pointFromVector };
+6
View File
@@ -0,0 +1,6 @@
const progress = (from, to, value) => {
const toFromDifference = to - from;
return toFromDifference === 0 ? 1 : (value - from) / toFromDifference;
};
export { progress };
@@ -0,0 +1,3 @@
const radiansToDegrees = (radians) => (radians * 180) / Math.PI;
export { radiansToDegrees };
+6
View File
@@ -0,0 +1,6 @@
import { toDecimal } from './to-decimal.mjs';
const smoothFrame = (prevValue, nextValue, duration, smoothing = 0) => toDecimal(prevValue +
(duration * (nextValue - prevValue)) / Math.max(smoothing, duration));
export { smoothFrame };
+19
View File
@@ -0,0 +1,19 @@
import { smoothFrame } from './smooth-frame.mjs';
import { getFrameData } from 'framesync';
const smooth = (strength = 50) => {
let previousValue = 0;
let lastUpdated = 0;
return (v) => {
const currentFramestamp = getFrameData().timestamp;
const timeDelta = currentFramestamp !== lastUpdated ? currentFramestamp - lastUpdated : 0;
const newValue = timeDelta
? smoothFrame(previousValue, v, timeDelta, strength)
: previousValue;
lastUpdated = currentFramestamp;
previousValue = newValue;
return newValue;
};
};
export { smooth };
+25
View File
@@ -0,0 +1,25 @@
const snap = (points) => {
if (typeof points === 'number') {
return (v) => Math.round(v / points) * points;
}
else {
let i = 0;
const numPoints = points.length;
return (v) => {
let lastDistance = Math.abs(points[0] - v);
for (i = 1; i < numPoints; i++) {
const point = points[i];
const distance = Math.abs(point - v);
if (distance === 0)
return point;
if (distance > lastDistance)
return points[i - 1];
if (i === numPoints - 1)
return point;
lastDistance = distance;
}
};
}
};
export { snap };
+6
View File
@@ -0,0 +1,6 @@
const toDecimal = (num, precision = 2) => {
precision = Math.pow(10, precision);
return Math.round(num * precision) / precision;
};
export { toDecimal };
@@ -0,0 +1,5 @@
function velocityPerFrame(xps, frameDuration) {
return xps / (1000 / frameDuration);
}
export { velocityPerFrame };
@@ -0,0 +1,5 @@
function velocityPerSecond(velocity, frameDuration) {
return frameDuration ? velocity * (1000 / frameDuration) : 0;
}
export { velocityPerSecond };
+6
View File
@@ -0,0 +1,6 @@
const wrap = (min, max, v) => {
const rangeSize = max - min;
return ((((v - min) % rangeSize) + rangeSize) % rangeSize) + min;
};
export { wrap };
+980
View File
@@ -0,0 +1,980 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var tslib = require('tslib');
var heyListen = require('hey-listen');
var styleValueTypes = require('style-value-types');
var sync = require('framesync');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var sync__default = /*#__PURE__*/_interopDefaultLegacy(sync);
const clamp = (min, max, v) => Math.min(Math.max(v, min), max);
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;
heyListen.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);
}
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 = tslib.__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;
const progress = (from, to, value) => {
const toFromDifference = to - from;
return toFromDifference === 0 ? 1 : (value - from) / toFromDifference;
};
const mix = (from, to, progress) => -progress * from + progress * to + from;
function hueToRgb(p, q, t) {
if (t < 0)
t += 1;
if (t > 1)
t -= 1;
if (t < 1 / 6)
return p + (q - p) * 6 * t;
if (t < 1 / 2)
return q;
if (t < 2 / 3)
return p + (q - p) * (2 / 3 - t) * 6;
return p;
}
function hslaToRgba({ hue, saturation, lightness, alpha }) {
hue /= 360;
saturation /= 100;
lightness /= 100;
let red = 0;
let green = 0;
let blue = 0;
if (!saturation) {
red = green = blue = lightness;
}
else {
const q = lightness < 0.5
? lightness * (1 + saturation)
: lightness + saturation - lightness * saturation;
const p = 2 * lightness - q;
red = hueToRgb(p, q, hue + 1 / 3);
green = hueToRgb(p, q, hue);
blue = hueToRgb(p, q, hue - 1 / 3);
}
return {
red: Math.round(red * 255),
green: Math.round(green * 255),
blue: Math.round(blue * 255),
alpha,
};
}
const mixLinearColor = (from, to, v) => {
const fromExpo = from * from;
const toExpo = to * to;
return Math.sqrt(Math.max(0, v * (toExpo - fromExpo) + fromExpo));
};
const colorTypes = [styleValueTypes.hex, styleValueTypes.rgba, styleValueTypes.hsla];
const getColorType = (v) => colorTypes.find((type) => type.test(v));
const notAnimatable = (color) => `'${color}' is not an animatable color. Use the equivalent color code instead.`;
const mixColor = (from, to) => {
let fromColorType = getColorType(from);
let toColorType = getColorType(to);
heyListen.invariant(!!fromColorType, notAnimatable(from));
heyListen.invariant(!!toColorType, notAnimatable(to));
let fromColor = fromColorType.parse(from);
let toColor = toColorType.parse(to);
if (fromColorType === styleValueTypes.hsla) {
fromColor = hslaToRgba(fromColor);
fromColorType = styleValueTypes.rgba;
}
if (toColorType === styleValueTypes.hsla) {
toColor = hslaToRgba(toColor);
toColorType = styleValueTypes.rgba;
}
const blended = Object.assign({}, fromColor);
return (v) => {
for (const key in blended) {
if (key !== "alpha") {
blended[key] = mixLinearColor(fromColor[key], toColor[key], v);
}
}
blended.alpha = mix(fromColor.alpha, toColor.alpha, v);
return fromColorType.transform(blended);
};
};
const zeroPoint = {
x: 0,
y: 0,
z: 0
};
const isNum = (v) => typeof v === 'number';
const combineFunctions = (a, b) => (v) => b(a(v));
const pipe = (...transformers) => transformers.reduce(combineFunctions);
function getMixer(origin, target) {
if (isNum(origin)) {
return (v) => mix(origin, target, v);
}
else if (styleValueTypes.color.test(origin)) {
return mixColor(origin, target);
}
else {
return mixComplex(origin, target);
}
}
const mixArray = (from, to) => {
const output = [...from];
const numValues = output.length;
const blendValue = from.map((fromThis, i) => getMixer(fromThis, to[i]));
return (v) => {
for (let i = 0; i < numValues; i++) {
output[i] = blendValue[i](v);
}
return output;
};
};
const mixObject = (origin, target) => {
const output = Object.assign(Object.assign({}, origin), target);
const blendValue = {};
for (const key in output) {
if (origin[key] !== undefined && target[key] !== undefined) {
blendValue[key] = getMixer(origin[key], target[key]);
}
}
return (v) => {
for (const key in blendValue) {
output[key] = blendValue[key](v);
}
return output;
};
};
function analyse(value) {
const parsed = styleValueTypes.complex.parse(value);
const numValues = parsed.length;
let numNumbers = 0;
let numRGB = 0;
let numHSL = 0;
for (let i = 0; i < numValues; i++) {
if (numNumbers || typeof parsed[i] === "number") {
numNumbers++;
}
else {
if (parsed[i].hue !== undefined) {
numHSL++;
}
else {
numRGB++;
}
}
}
return { parsed, numNumbers, numRGB, numHSL };
}
const mixComplex = (origin, target) => {
const template = styleValueTypes.complex.createTransformer(target);
const originStats = analyse(origin);
const targetStats = analyse(target);
const canInterpolate = originStats.numHSL === targetStats.numHSL &&
originStats.numRGB === targetStats.numRGB &&
originStats.numNumbers >= targetStats.numNumbers;
if (canInterpolate) {
return pipe(mixArray(originStats.parsed, targetStats.parsed), template);
}
else {
heyListen.warning(true, `Complex values '${origin}' and '${target}' too different to mix. Ensure all colors are of the same type, and that each contains the same quantity of number and color values. Falling back to instant transition.`);
return (p) => `${p > 0 ? target : origin}`;
}
};
const mixNumber = (from, to) => (p) => mix(from, to, p);
function detectMixerFactory(v) {
if (typeof v === 'number') {
return mixNumber;
}
else if (typeof v === 'string') {
if (styleValueTypes.color.test(v)) {
return mixColor;
}
else {
return mixComplex;
}
}
else if (Array.isArray(v)) {
return mixArray;
}
else if (typeof v === 'object') {
return mixObject;
}
}
function createMixers(output, ease, customMixer) {
const mixers = [];
const mixerFactory = customMixer || detectMixerFactory(output[0]);
const numMixers = output.length - 1;
for (let i = 0; i < numMixers; i++) {
let mixer = mixerFactory(output[i], output[i + 1]);
if (ease) {
const easingFunction = Array.isArray(ease) ? ease[i] : ease;
mixer = pipe(easingFunction, mixer);
}
mixers.push(mixer);
}
return mixers;
}
function fastInterpolate([from, to], [mixer]) {
return (v) => mixer(progress(from, to, v));
}
function slowInterpolate(input, mixers) {
const inputLength = input.length;
const lastInputIndex = inputLength - 1;
return (v) => {
let mixerIndex = 0;
let foundMixerIndex = false;
if (v <= input[0]) {
foundMixerIndex = true;
}
else if (v >= input[lastInputIndex]) {
mixerIndex = lastInputIndex - 1;
foundMixerIndex = true;
}
if (!foundMixerIndex) {
let i = 1;
for (; i < inputLength; i++) {
if (input[i] > v || i === lastInputIndex) {
break;
}
}
mixerIndex = i - 1;
}
const progressInRange = progress(input[mixerIndex], input[mixerIndex + 1], v);
return mixers[mixerIndex](progressInRange);
};
}
function interpolate(input, output, { clamp: isClamp = true, ease, mixer } = {}) {
const inputLength = input.length;
heyListen.invariant(inputLength === output.length, 'Both input and output ranges must be the same length');
heyListen.invariant(!ease || !Array.isArray(ease) || ease.length === inputLength - 1, 'Array of easing functions must be of length `input.length - 1`, as it applies to the transitions **between** the defined values.');
if (input[0] > input[inputLength - 1]) {
input = [].concat(input);
output = [].concat(output);
input.reverse();
output.reverse();
}
const mixers = createMixers(output, ease, mixer);
const interpolator = inputLength === 2
? fastInterpolate(input, mixers)
: slowInterpolate(input, mixers);
return isClamp
? (v) => interpolator(clamp(input[0], input[inputLength - 1], v))
: interpolator;
}
const reverseEasing = easing => p => 1 - easing(1 - p);
const mirrorEasing = easing => p => p <= 0.5 ? easing(2 * p) / 2 : (2 - easing(2 * (1 - p))) / 2;
const createExpoIn = (power) => p => Math.pow(p, power);
const createBackIn = (power) => p => p * p * ((power + 1) * p - power);
const createAnticipate = (power) => {
const backEasing = createBackIn(power);
return p => (p *= 2) < 1
? 0.5 * backEasing(p)
: 0.5 * (2 - Math.pow(2, -10 * (p - 1)));
};
const DEFAULT_OVERSHOOT_STRENGTH = 1.525;
const BOUNCE_FIRST_THRESHOLD = 4.0 / 11.0;
const BOUNCE_SECOND_THRESHOLD = 8.0 / 11.0;
const BOUNCE_THIRD_THRESHOLD = 9.0 / 10.0;
const linear = p => p;
const easeIn = createExpoIn(2);
const easeOut = reverseEasing(easeIn);
const easeInOut = mirrorEasing(easeIn);
const circIn = p => 1 - Math.sin(Math.acos(p));
const circOut = reverseEasing(circIn);
const circInOut = mirrorEasing(circOut);
const backIn = createBackIn(DEFAULT_OVERSHOOT_STRENGTH);
const backOut = reverseEasing(backIn);
const backInOut = mirrorEasing(backIn);
const anticipate = createAnticipate(DEFAULT_OVERSHOOT_STRENGTH);
const ca = 4356.0 / 361.0;
const cb = 35442.0 / 1805.0;
const cc = 16061.0 / 1805.0;
const bounceOut = (p) => {
if (p === 1 || p === 0)
return p;
const p2 = p * p;
return p < BOUNCE_FIRST_THRESHOLD
? 7.5625 * p2
: p < BOUNCE_SECOND_THRESHOLD
? 9.075 * p2 - 9.9 * p + 3.4
: p < BOUNCE_THIRD_THRESHOLD
? ca * p2 - cb * p + cc
: 10.8 * p * p - 20.52 * p + 10.72;
};
const bounceIn = reverseEasing(bounceOut);
const bounceInOut = (p) => p < 0.5
? 0.5 * (1.0 - bounceOut(1.0 - p * 2.0))
: 0.5 * bounceOut(p * 2.0 - 1.0) + 0.5;
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();
},
};
}
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: () => { },
};
}
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;
}
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;
}
const framesync = (update) => {
const passTimestamp = ({ delta }) => update(delta);
return {
start: () => sync__default['default'].update(passTimestamp, true),
stop: () => sync.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 = tslib.__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();
},
};
}
function velocityPerSecond(velocity, frameDuration) {
return frameDuration ? velocity * (1000 / frameDuration) : 0;
}
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, sync.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(),
};
}
const radiansToDegrees = (radians) => (radians * 180) / Math.PI;
const angle = (a, b = zeroPoint) => radiansToDegrees(Math.atan2(b.y - a.y, b.x - a.x));
const applyOffset = (from, to) => {
let hasReceivedFrom = true;
if (to === undefined) {
to = from;
hasReceivedFrom = false;
}
return (v) => {
if (hasReceivedFrom) {
return v - from + to;
}
else {
from = v;
hasReceivedFrom = true;
return to;
}
};
};
const identity = (v) => v;
const createAttractor = (alterDisplacement = identity) => (constant, origin, v) => {
const displacement = origin - v;
const springModifiedDisplacement = -(0 - constant + 1) * (0 - alterDisplacement(Math.abs(displacement)));
return displacement <= 0
? origin + springModifiedDisplacement
: origin - springModifiedDisplacement;
};
const attract = createAttractor();
const attractExpo = createAttractor(Math.sqrt);
const degreesToRadians = (degrees) => (degrees * Math.PI) / 180;
const isPoint = (point) => point.hasOwnProperty('x') && point.hasOwnProperty('y');
const isPoint3D = (point) => isPoint(point) && point.hasOwnProperty('z');
const distance1D = (a, b) => Math.abs(a - b);
function distance(a, b) {
if (isNum(a) && isNum(b)) {
return distance1D(a, b);
}
else if (isPoint(a) && isPoint(b)) {
const xDelta = distance1D(a.x, b.x);
const yDelta = distance1D(a.y, b.y);
const zDelta = isPoint3D(a) && isPoint3D(b) ? distance1D(a.z, b.z) : 0;
return Math.sqrt(Math.pow(xDelta, 2) + Math.pow(yDelta, 2) + Math.pow(zDelta, 2));
}
}
const pointFromVector = (origin, angle, distance) => {
angle = degreesToRadians(angle);
return {
x: distance * Math.cos(angle) + origin.x,
y: distance * Math.sin(angle) + origin.y
};
};
const toDecimal = (num, precision = 2) => {
precision = Math.pow(10, precision);
return Math.round(num * precision) / precision;
};
const smoothFrame = (prevValue, nextValue, duration, smoothing = 0) => toDecimal(prevValue +
(duration * (nextValue - prevValue)) / Math.max(smoothing, duration));
const smooth = (strength = 50) => {
let previousValue = 0;
let lastUpdated = 0;
return (v) => {
const currentFramestamp = sync.getFrameData().timestamp;
const timeDelta = currentFramestamp !== lastUpdated ? currentFramestamp - lastUpdated : 0;
const newValue = timeDelta
? smoothFrame(previousValue, v, timeDelta, strength)
: previousValue;
lastUpdated = currentFramestamp;
previousValue = newValue;
return newValue;
};
};
const snap = (points) => {
if (typeof points === 'number') {
return (v) => Math.round(v / points) * points;
}
else {
let i = 0;
const numPoints = points.length;
return (v) => {
let lastDistance = Math.abs(points[0] - v);
for (i = 1; i < numPoints; i++) {
const point = points[i];
const distance = Math.abs(point - v);
if (distance === 0)
return point;
if (distance > lastDistance)
return points[i - 1];
if (i === numPoints - 1)
return point;
lastDistance = distance;
}
};
}
};
function velocityPerFrame(xps, frameDuration) {
return xps / (1000 / frameDuration);
}
const wrap = (min, max, v) => {
const rangeSize = max - min;
return ((((v - min) % rangeSize) + rangeSize) % rangeSize) + min;
};
const a = (a1, a2) => 1.0 - 3.0 * a2 + 3.0 * a1;
const b = (a1, a2) => 3.0 * a2 - 6.0 * a1;
const c = (a1) => 3.0 * a1;
const calcBezier = (t, a1, a2) => ((a(a1, a2) * t + b(a1, a2)) * t + c(a1)) * t;
const getSlope = (t, a1, a2) => 3.0 * a(a1, a2) * t * t + 2.0 * b(a1, a2) * t + c(a1);
const subdivisionPrecision = 0.0000001;
const subdivisionMaxIterations = 10;
function binarySubdivide(aX, aA, aB, mX1, mX2) {
let currentX;
let currentT;
let i = 0;
do {
currentT = aA + (aB - aA) / 2.0;
currentX = calcBezier(currentT, mX1, mX2) - aX;
if (currentX > 0.0) {
aB = currentT;
}
else {
aA = currentT;
}
} while (Math.abs(currentX) > subdivisionPrecision &&
++i < subdivisionMaxIterations);
return currentT;
}
const newtonIterations = 8;
const newtonMinSlope = 0.001;
function newtonRaphsonIterate(aX, aGuessT, mX1, mX2) {
for (let i = 0; i < newtonIterations; ++i) {
const currentSlope = getSlope(aGuessT, mX1, mX2);
if (currentSlope === 0.0) {
return aGuessT;
}
const currentX = calcBezier(aGuessT, mX1, mX2) - aX;
aGuessT -= currentX / currentSlope;
}
return aGuessT;
}
const kSplineTableSize = 11;
const kSampleStepSize = 1.0 / (kSplineTableSize - 1.0);
function cubicBezier(mX1, mY1, mX2, mY2) {
if (mX1 === mY1 && mX2 === mY2)
return linear;
const sampleValues = new Float32Array(kSplineTableSize);
for (let i = 0; i < kSplineTableSize; ++i) {
sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2);
}
function getTForX(aX) {
let intervalStart = 0.0;
let currentSample = 1;
const lastSample = kSplineTableSize - 1;
for (; currentSample !== lastSample && sampleValues[currentSample] <= aX; ++currentSample) {
intervalStart += kSampleStepSize;
}
--currentSample;
const dist = (aX - sampleValues[currentSample]) /
(sampleValues[currentSample + 1] - sampleValues[currentSample]);
const guessForT = intervalStart + dist * kSampleStepSize;
const initialSlope = getSlope(guessForT, mX1, mX2);
if (initialSlope >= newtonMinSlope) {
return newtonRaphsonIterate(aX, guessForT, mX1, mX2);
}
else if (initialSlope === 0.0) {
return guessForT;
}
else {
return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, mX1, mX2);
}
}
return (t) => t === 0 || t === 1 ? t : calcBezier(getTForX(t), mY1, mY2);
}
const steps = (steps, direction = 'end') => (progress) => {
progress =
direction === 'end' ? Math.min(progress, 0.999) : Math.max(progress, 0.001);
const expanded = progress * steps;
const rounded = direction === 'end' ? Math.floor(expanded) : Math.ceil(expanded);
return clamp(0, 1, rounded / steps);
};
exports.angle = angle;
exports.animate = animate;
exports.anticipate = anticipate;
exports.applyOffset = applyOffset;
exports.attract = attract;
exports.attractExpo = attractExpo;
exports.backIn = backIn;
exports.backInOut = backInOut;
exports.backOut = backOut;
exports.bounceIn = bounceIn;
exports.bounceInOut = bounceInOut;
exports.bounceOut = bounceOut;
exports.circIn = circIn;
exports.circInOut = circInOut;
exports.circOut = circOut;
exports.clamp = clamp;
exports.createAnticipate = createAnticipate;
exports.createAttractor = createAttractor;
exports.createBackIn = createBackIn;
exports.createExpoIn = createExpoIn;
exports.cubicBezier = cubicBezier;
exports.decay = decay;
exports.degreesToRadians = degreesToRadians;
exports.distance = distance;
exports.easeIn = easeIn;
exports.easeInOut = easeInOut;
exports.easeOut = easeOut;
exports.inertia = inertia;
exports.interpolate = interpolate;
exports.isPoint = isPoint;
exports.isPoint3D = isPoint3D;
exports.keyframes = keyframes;
exports.linear = linear;
exports.mirrorEasing = mirrorEasing;
exports.mix = mix;
exports.mixColor = mixColor;
exports.mixComplex = mixComplex;
exports.pipe = pipe;
exports.pointFromVector = pointFromVector;
exports.progress = progress;
exports.radiansToDegrees = radiansToDegrees;
exports.reverseEasing = reverseEasing;
exports.smooth = smooth;
exports.smoothFrame = smoothFrame;
exports.snap = snap;
exports.spring = spring;
exports.steps = steps;
exports.toDecimal = toDecimal;
exports.velocityPerFrame = velocityPerFrame;
exports.velocityPerSecond = velocityPerSecond;
exports.wrap = wrap;
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,4 @@
export declare const syncDriver: (interval?: number) => (update: (v: number) => void) => {
start: () => void;
stop: () => boolean;
};
+14
View File
@@ -0,0 +1,14 @@
export const syncDriver = (interval = 10) => (update) => {
let isRunning = true;
return {
start: () => {
setTimeout(() => {
update(0);
while (isRunning)
update(interval);
}, 0);
},
stop: () => (isRunning = false),
};
};
//# sourceMappingURL=utils.js.map
@@ -0,0 +1,2 @@
import { Animation } from "../../types";
export declare function animateSync(animation: Animation<number>, timeStep?: number, round?: boolean): number[];
@@ -0,0 +1,13 @@
export function animateSync(animation, timeStep = 200, round = true) {
const output = [];
let step = 0;
let done = false;
while (!done) {
const latest = animation.next(step * timeStep);
output.push(round ? Math.round(latest.value) : latest.value);
done = latest.done;
step++;
}
return output;
}
//# sourceMappingURL=utils.js.map
@@ -0,0 +1,2 @@
import { Animation, DecayOptions } from "../types";
export declare function decay({ velocity, from, power, timeConstant, restDelta, modifyTarget, }: DecayOptions): Animation<number>;
@@ -0,0 +1,18 @@
export 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: () => { },
};
}
//# sourceMappingURL=decay.js.map
@@ -0,0 +1,6 @@
import { Animation, KeyframeOptions } from "../types";
import { Easing } from "../../easing/types";
export declare function defaultEasing(values: any[], easing?: Easing): Easing[];
export declare function defaultOffset(values: any[]): number[];
export declare function convertOffsetToTimes(offset: number[], duration: number): number[];
export declare function keyframes<V>({ from, to, ease, offset, duration, }: KeyframeOptions): Animation<number | string>;
@@ -0,0 +1,37 @@
import { interpolate } from "../../utils/interpolate";
import { easeInOut } from "../../easing";
export function defaultEasing(values, easing) {
return values.map(() => easing || easeInOut).splice(0, values.length - 1);
}
export function defaultOffset(values) {
const numValues = values.length;
return values.map((_value, i) => i !== 0 ? i / (numValues - 1) : 0);
}
export function convertOffsetToTimes(offset, duration) {
return offset.map((o) => o * duration);
}
export 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();
},
};
}
//# sourceMappingURL=keyframes.js.map
@@ -0,0 +1,5 @@
import { SpringOptions, Animation } from "../types";
export declare function spring({ from, to, restSpeed, restDelta, ...options }: SpringOptions): Animation<number>;
export declare namespace spring {
var needsInterpolation: (a: any, b: any) => boolean;
}
@@ -0,0 +1,117 @@
import { __rest } from "tslib";
import { calcAngularFreq, findSpring } from "../utils/find-spring";
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;
}
export 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;
//# sourceMappingURL=spring.js.map
+4
View File
@@ -0,0 +1,4 @@
import { AnimationOptions } from "./types";
export declare function animate<V = number>({ from, autoplay, driver, elapsed, repeat: repeatMax, repeatType, repeatDelay, onPlay, onStop, onComplete, onRepeat, onUpdate, ...options }: AnimationOptions<V>): {
stop: () => void;
};
+87
View File
@@ -0,0 +1,87 @@
import { __rest } from "tslib";
import { detectAnimationFromOptions } from "./utils/detect-animation-from-options";
import sync, { cancelSync } from "framesync";
import { interpolate } from "../utils/interpolate";
import { loopElapsed, reverseElapsed, hasRepeatDelayElapsed, } from "./utils/elapsed";
const framesync = (update) => {
const passTimestamp = ({ delta }) => update(delta);
return {
start: () => sync.update(passTimestamp, true),
stop: () => cancelSync.update(passTimestamp),
};
};
export 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();
},
};
}
//# sourceMappingURL=index.js.map
+4
View File
@@ -0,0 +1,4 @@
import { InertiaOptions } from "./types";
export declare function inertia({ from, velocity, min, max, power, timeConstant, bounceStiffness, bounceDamping, restDelta, modifyTarget, driver, onUpdate, onComplete, onStop, }: InertiaOptions): {
stop: () => void;
};
+63
View File
@@ -0,0 +1,63 @@
import { animate } from ".";
import { velocityPerSecond } from "../utils/velocity-per-second";
import { getFrameData } from "framesync";
export 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(),
};
}
//# sourceMappingURL=inertia.js.map
+80
View File
@@ -0,0 +1,80 @@
import { Easing } from "../easing/types";
export interface Animation<V> {
next: (t: number) => {
value: V;
done: boolean;
};
flipTarget: () => void;
}
export interface AnimationState<V> {
value: V;
done: boolean;
}
export interface PlaybackControls {
stop: () => void;
}
declare type Update = (timestamp: number) => void;
export interface DriverControls {
start: () => void;
stop: () => void;
}
export declare type Driver = (update: Update) => DriverControls;
export interface PlaybackOptions<V> {
autoplay?: boolean;
driver?: Driver;
elapsed?: number;
from?: V;
repeat?: number;
repeatType?: "loop" | "reverse" | "mirror";
repeatDelay?: number;
type?: "spring" | "decay" | "keyframes";
onUpdate?: (latest: V) => void;
onPlay?: () => void;
onComplete?: () => void;
onRepeat?: () => void;
onStop?: () => void;
}
export interface KeyframeOptions<V = number> {
to: V | V[];
from?: V;
duration?: number;
ease?: Easing | Easing[];
offset?: number[];
}
export interface DecayOptions {
from?: number;
to?: number;
velocity?: number;
power?: number;
timeConstant?: number;
modifyTarget?: (target: number) => number;
restDelta?: number;
}
export interface PhysicsSpringOptions {
velocity?: number;
stiffness?: number;
damping?: number;
mass?: number;
}
export interface SpringOptions extends PhysicsSpringOptions {
from?: number;
to?: number;
duration?: number;
bounce?: number;
restSpeed?: number;
restDelta?: number;
}
export interface InertiaOptions extends DecayOptions {
bounceStiffness?: number;
bounceDamping?: number;
min?: number;
max?: number;
restSpeed?: number;
restDelta?: number;
driver?: Driver;
onUpdate?: (v: number) => void;
onComplete?: () => void;
onStop?: () => void;
}
export declare type AnimationOptions<V> = PlaybackOptions<V> & (DecayOptions | KeyframeOptions<V> | SpringOptions);
export {};
+2
View File
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=types.js.map
@@ -0,0 +1,9 @@
import { spring } from "../generators/spring";
import { keyframes } from "../generators/keyframes";
import { decay } from "../generators/decay";
interface Options {
to?: any;
type?: "decay" | "keyframes" | "spring";
}
export declare function detectAnimationFromOptions<T extends Options>(config: T): typeof spring | typeof keyframes | typeof decay;
export {};
@@ -0,0 +1,27 @@
import { spring } from "../generators/spring";
import { keyframes } from "../generators/keyframes";
import { decay } from "../generators/decay";
const types = { keyframes, spring, decay };
export 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;
}
//# sourceMappingURL=detect-animation-from-options.js.map
@@ -0,0 +1,3 @@
export declare function loopElapsed(elapsed: number, duration: number, delay?: number): number;
export declare function reverseElapsed(elapsed: number, duration: number, delay?: number, isForwardPlayback?: boolean): number;
export declare function hasRepeatDelayElapsed(elapsed: number, duration: number, delay: number, isForwardPlayback: boolean): boolean;
+12
View File
@@ -0,0 +1,12 @@
export function loopElapsed(elapsed, duration, delay = 0) {
return elapsed - duration - delay;
}
export function reverseElapsed(elapsed, duration, delay = 0, isForwardPlayback = true) {
return isForwardPlayback
? loopElapsed(duration + -elapsed, duration, delay)
: duration - (elapsed - duration) + delay;
}
export function hasRepeatDelayElapsed(elapsed, duration, delay, isForwardPlayback) {
return isForwardPlayback ? elapsed >= duration + delay : elapsed <= -delay;
}
//# sourceMappingURL=elapsed.js.map
@@ -0,0 +1,11 @@
import { SpringOptions } from "../types";
export declare const minDuration = 0.01;
export declare const maxDuration = 10;
export declare const minDamping = 0.05;
export declare const maxDamping = 1;
export declare function findSpring({ duration, bounce, velocity, mass, }: SpringOptions): {
stiffness: number;
damping: number;
duration: number;
};
export declare function calcAngularFreq(undampedFreq: number, dampingRatio: number): number;
@@ -0,0 +1,77 @@
import { warning } from "hey-listen";
import { clamp } from "../../utils/clamp";
const safeMin = 0.001;
export const minDuration = 0.01;
export const maxDuration = 10.0;
export const minDamping = 0.05;
export const maxDamping = 1;
export 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;
}
export function calcAngularFreq(undampedFreq, dampingRatio) {
return undampedFreq * Math.sqrt(1 - dampingRatio * dampingRatio);
}
//# sourceMappingURL=find-spring.js.map
+1
View File
@@ -0,0 +1 @@
export declare function cubicBezier(mX1: number, mY1: number, mX2: number, mY2: number): import("./types").Easing;
+72
View File
@@ -0,0 +1,72 @@
import { linear } from ".";
const a = (a1, a2) => 1.0 - 3.0 * a2 + 3.0 * a1;
const b = (a1, a2) => 3.0 * a2 - 6.0 * a1;
const c = (a1) => 3.0 * a1;
const calcBezier = (t, a1, a2) => ((a(a1, a2) * t + b(a1, a2)) * t + c(a1)) * t;
const getSlope = (t, a1, a2) => 3.0 * a(a1, a2) * t * t + 2.0 * b(a1, a2) * t + c(a1);
const subdivisionPrecision = 0.0000001;
const subdivisionMaxIterations = 10;
function binarySubdivide(aX, aA, aB, mX1, mX2) {
let currentX;
let currentT;
let i = 0;
do {
currentT = aA + (aB - aA) / 2.0;
currentX = calcBezier(currentT, mX1, mX2) - aX;
if (currentX > 0.0) {
aB = currentT;
}
else {
aA = currentT;
}
} while (Math.abs(currentX) > subdivisionPrecision &&
++i < subdivisionMaxIterations);
return currentT;
}
const newtonIterations = 8;
const newtonMinSlope = 0.001;
function newtonRaphsonIterate(aX, aGuessT, mX1, mX2) {
for (let i = 0; i < newtonIterations; ++i) {
const currentSlope = getSlope(aGuessT, mX1, mX2);
if (currentSlope === 0.0) {
return aGuessT;
}
const currentX = calcBezier(aGuessT, mX1, mX2) - aX;
aGuessT -= currentX / currentSlope;
}
return aGuessT;
}
const kSplineTableSize = 11;
const kSampleStepSize = 1.0 / (kSplineTableSize - 1.0);
export function cubicBezier(mX1, mY1, mX2, mY2) {
if (mX1 === mY1 && mX2 === mY2)
return linear;
const sampleValues = new Float32Array(kSplineTableSize);
for (let i = 0; i < kSplineTableSize; ++i) {
sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2);
}
function getTForX(aX) {
let intervalStart = 0.0;
let currentSample = 1;
const lastSample = kSplineTableSize - 1;
for (; currentSample !== lastSample && sampleValues[currentSample] <= aX; ++currentSample) {
intervalStart += kSampleStepSize;
}
--currentSample;
const dist = (aX - sampleValues[currentSample]) /
(sampleValues[currentSample + 1] - sampleValues[currentSample]);
const guessForT = intervalStart + dist * kSampleStepSize;
const initialSlope = getSlope(guessForT, mX1, mX2);
if (initialSlope >= newtonMinSlope) {
return newtonRaphsonIterate(aX, guessForT, mX1, mX2);
}
else if (initialSlope === 0.0) {
return guessForT;
}
else {
return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, mX1, mX2);
}
}
return (t) => t === 0 || t === 1 ? t : calcBezier(getTForX(t), mY1, mY2);
}
//# sourceMappingURL=cubic-bezier.js.map
+15
View File
@@ -0,0 +1,15 @@
import { Easing } from "./types";
export declare const linear: Easing;
export declare const easeIn: Easing;
export declare const easeOut: Easing;
export declare const easeInOut: Easing;
export declare const circIn: Easing;
export declare const circOut: Easing;
export declare const circInOut: Easing;
export declare const backIn: Easing;
export declare const backOut: Easing;
export declare const backInOut: Easing;
export declare const anticipate: Easing;
export declare const bounceOut: (p: number) => number;
export declare const bounceIn: Easing;
export declare const bounceInOut: (p: number) => number;
+36
View File
@@ -0,0 +1,36 @@
import { createExpoIn, reverseEasing, mirrorEasing, createBackIn, createAnticipate, } from "./utils";
const DEFAULT_OVERSHOOT_STRENGTH = 1.525;
const BOUNCE_FIRST_THRESHOLD = 4.0 / 11.0;
const BOUNCE_SECOND_THRESHOLD = 8.0 / 11.0;
const BOUNCE_THIRD_THRESHOLD = 9.0 / 10.0;
export const linear = p => p;
export const easeIn = createExpoIn(2);
export const easeOut = reverseEasing(easeIn);
export const easeInOut = mirrorEasing(easeIn);
export const circIn = p => 1 - Math.sin(Math.acos(p));
export const circOut = reverseEasing(circIn);
export const circInOut = mirrorEasing(circOut);
export const backIn = createBackIn(DEFAULT_OVERSHOOT_STRENGTH);
export const backOut = reverseEasing(backIn);
export const backInOut = mirrorEasing(backIn);
export const anticipate = createAnticipate(DEFAULT_OVERSHOOT_STRENGTH);
const ca = 4356.0 / 361.0;
const cb = 35442.0 / 1805.0;
const cc = 16061.0 / 1805.0;
export const bounceOut = (p) => {
if (p === 1 || p === 0)
return p;
const p2 = p * p;
return p < BOUNCE_FIRST_THRESHOLD
? 7.5625 * p2
: p < BOUNCE_SECOND_THRESHOLD
? 9.075 * p2 - 9.9 * p + 3.4
: p < BOUNCE_THIRD_THRESHOLD
? ca * p2 - cb * p + cc
: 10.8 * p * p - 20.52 * p + 10.72;
};
export const bounceIn = reverseEasing(bounceOut);
export const bounceInOut = (p) => p < 0.5
? 0.5 * (1.0 - bounceOut(1.0 - p * 2.0))
: 0.5 * bounceOut(p * 2.0 - 1.0) + 0.5;
//# sourceMappingURL=index.js.map
+3
View File
@@ -0,0 +1,3 @@
import { Easing } from './types';
export declare type Direction = 'start' | 'end';
export declare const steps: (steps: number, direction?: Direction) => Easing;
+9
View File
@@ -0,0 +1,9 @@
import { clamp } from '../utils/clamp';
export const steps = (steps, direction = 'end') => (progress) => {
progress =
direction === 'end' ? Math.min(progress, 0.999) : Math.max(progress, 0.001);
const expanded = progress * steps;
const rounded = direction === 'end' ? Math.floor(expanded) : Math.ceil(expanded);
return clamp(0, 1, rounded / steps);
};
//# sourceMappingURL=steps.js.map
+2
View File
@@ -0,0 +1,2 @@
export declare type Easing = (v: number) => number;
export declare type EasingModifier = (easing: Easing) => Easing;
+2
View File
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=types.js.map
+6
View File
@@ -0,0 +1,6 @@
import { Easing, EasingModifier } from "./types";
export declare const reverseEasing: EasingModifier;
export declare const mirrorEasing: EasingModifier;
export declare const createExpoIn: (power: number) => Easing;
export declare const createBackIn: (power: number) => Easing;
export declare const createAnticipate: (power: number) => Easing;
+11
View File
@@ -0,0 +1,11 @@
export const reverseEasing = easing => p => 1 - easing(1 - p);
export const mirrorEasing = easing => p => p <= 0.5 ? easing(2 * p) / 2 : (2 - easing(2 * (1 - p))) / 2;
export const createExpoIn = (power) => p => Math.pow(p, power);
export const createBackIn = (power) => p => p * p * ((power + 1) * p - power);
export const createAnticipate = (power) => {
const backEasing = createBackIn(power);
return p => (p *= 2) < 1
? 0.5 * backEasing(p)
: 0.5 * (2 - Math.pow(2, -10 * (p - 1)));
};
//# sourceMappingURL=utils.js.map
+34
View File
@@ -0,0 +1,34 @@
export { animate } from "./animations";
export { inertia } from "./animations/inertia";
export { decay } from "./animations/generators/decay";
export { spring } from "./animations/generators/spring";
export { keyframes } from "./animations/generators/keyframes";
export { angle } from "./utils/angle";
export { applyOffset } from "./utils/apply-offset";
export { createAttractor, attract, attractExpo } from "./utils/attract";
export { clamp } from "./utils/clamp";
export { degreesToRadians } from "./utils/degrees-to-radians";
export { distance } from "./utils/distance";
export { interpolate } from "./utils/interpolate";
export { isPoint3D } from "./utils/is-point-3d";
export { isPoint } from "./utils/is-point";
export { mixColor } from "./utils/mix-color";
export { mixComplex } from "./utils/mix-complex";
export { mix } from "./utils/mix";
export { pipe } from "./utils/pipe";
export { pointFromVector } from "./utils/point-from-vector";
export { progress } from "./utils/progress";
export { radiansToDegrees } from "./utils/radians-to-degrees";
export { smoothFrame } from "./utils/smooth-frame";
export { smooth } from "./utils/smooth";
export { snap } from "./utils/snap";
export { toDecimal } from "./utils/to-decimal";
export { velocityPerFrame } from "./utils/velocity-per-frame";
export { velocityPerSecond } from "./utils/velocity-per-second";
export { wrap } from "./utils/wrap";
export { linear, easeIn, easeInOut, easeOut, circIn, circInOut, circOut, backIn, backInOut, backOut, anticipate, bounceIn, bounceInOut, bounceOut, } from "./easing";
export { cubicBezier } from "./easing/cubic-bezier";
export { steps } from "./easing/steps";
export { mirrorEasing, reverseEasing, createExpoIn, createBackIn, createAnticipate, } from "./easing/utils";
export * from "./animations/types";
export * from "./easing/types";
+35
View File
@@ -0,0 +1,35 @@
export { animate } from "./animations";
export { inertia } from "./animations/inertia";
export { decay } from "./animations/generators/decay";
export { spring } from "./animations/generators/spring";
export { keyframes } from "./animations/generators/keyframes";
export { angle } from "./utils/angle";
export { applyOffset } from "./utils/apply-offset";
export { createAttractor, attract, attractExpo } from "./utils/attract";
export { clamp } from "./utils/clamp";
export { degreesToRadians } from "./utils/degrees-to-radians";
export { distance } from "./utils/distance";
export { interpolate } from "./utils/interpolate";
export { isPoint3D } from "./utils/is-point-3d";
export { isPoint } from "./utils/is-point";
export { mixColor } from "./utils/mix-color";
export { mixComplex } from "./utils/mix-complex";
export { mix } from "./utils/mix";
export { pipe } from "./utils/pipe";
export { pointFromVector } from "./utils/point-from-vector";
export { progress } from "./utils/progress";
export { radiansToDegrees } from "./utils/radians-to-degrees";
export { smoothFrame } from "./utils/smooth-frame";
export { smooth } from "./utils/smooth";
export { snap } from "./utils/snap";
export { toDecimal } from "./utils/to-decimal";
export { velocityPerFrame } from "./utils/velocity-per-frame";
export { velocityPerSecond } from "./utils/velocity-per-second";
export { wrap } from "./utils/wrap";
export { linear, easeIn, easeInOut, easeOut, circIn, circInOut, circOut, backIn, backInOut, backOut, anticipate, bounceIn, bounceInOut, bounceOut, } from "./easing";
export { cubicBezier } from "./easing/cubic-bezier";
export { steps } from "./easing/steps";
export { mirrorEasing, reverseEasing, createExpoIn, createBackIn, createAnticipate, } from "./easing/utils";
export * from "./animations/types";
export * from "./easing/types";
//# sourceMappingURL=index.js.map
+8
View File
@@ -0,0 +1,8 @@
export declare type Point2D = {
x: number;
y: number;
};
export declare type Point3D = Point2D & {
z: number;
};
export declare type Point = Point2D | Point3D;
+2
View File
@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=types.js.map
+2
View File
@@ -0,0 +1,2 @@
import { Point } from '../types';
export declare const angle: (a: Point, b?: Point) => number;
+4
View File
@@ -0,0 +1,4 @@
import { radiansToDegrees } from './radians-to-degrees';
import { zeroPoint } from './inc';
export const angle = (a, b = zeroPoint) => radiansToDegrees(Math.atan2(b.y - a.y, b.x - a.x));
//# sourceMappingURL=angle.js.map
+1
View File
@@ -0,0 +1 @@
export declare const applyOffset: (from: number, to?: number) => (v: number) => number;
+18
View File
@@ -0,0 +1,18 @@
export const applyOffset = (from, to) => {
let hasReceivedFrom = true;
if (to === undefined) {
to = from;
hasReceivedFrom = false;
}
return (v) => {
if (hasReceivedFrom) {
return v - from + to;
}
else {
from = v;
hasReceivedFrom = true;
return to;
}
};
};
//# sourceMappingURL=apply-offset.js.map
+3
View File
@@ -0,0 +1,3 @@
export declare const createAttractor: (alterDisplacement?: Function) => (constant: number, origin: number, v: number) => number;
export declare const attract: (constant: number, origin: number, v: number) => number;
export declare const attractExpo: (constant: number, origin: number, v: number) => number;
+11
View File
@@ -0,0 +1,11 @@
const identity = (v) => v;
export const createAttractor = (alterDisplacement = identity) => (constant, origin, v) => {
const displacement = origin - v;
const springModifiedDisplacement = -(0 - constant + 1) * (0 - alterDisplacement(Math.abs(displacement)));
return displacement <= 0
? origin + springModifiedDisplacement
: origin - springModifiedDisplacement;
};
export const attract = createAttractor();
export const attractExpo = createAttractor(Math.sqrt);
//# sourceMappingURL=attract.js.map
+1
View File
@@ -0,0 +1 @@
export declare const clamp: (min: number, max: number, v: number) => number;
+2
View File
@@ -0,0 +1,2 @@
export const clamp = (min, max, v) => Math.min(Math.max(v, min), max);
//# sourceMappingURL=clamp.js.map
@@ -0,0 +1 @@
export declare const degreesToRadians: (degrees: number) => number;
+2
View File
@@ -0,0 +1,2 @@
export const degreesToRadians = (degrees) => (degrees * Math.PI) / 180;
//# sourceMappingURL=degrees-to-radians.js.map
+2
View File
@@ -0,0 +1,2 @@
import { Point } from "../types";
export declare function distance<P extends Point | number>(a: P, b: P): number;
+16
View File
@@ -0,0 +1,16 @@
import { isPoint } from "./is-point";
import { isPoint3D } from "./is-point-3d";
import { isNum } from "./inc";
const distance1D = (a, b) => Math.abs(a - b);
export function distance(a, b) {
if (isNum(a) && isNum(b)) {
return distance1D(a, b);
}
else if (isPoint(a) && isPoint(b)) {
const xDelta = distance1D(a.x, b.x);
const yDelta = distance1D(a.y, b.y);
const zDelta = isPoint3D(a) && isPoint3D(b) ? distance1D(a.z, b.z) : 0;
return Math.sqrt(Math.pow(xDelta, 2) + Math.pow(yDelta, 2) + Math.pow(zDelta, 2));
}
}
//# sourceMappingURL=distance.js.map
+2
View File
@@ -0,0 +1,2 @@
import { HSLA, RGBA } from "style-value-types";
export declare function hslaToRgba({ hue, saturation, lightness, alpha }: HSLA): RGBA;
+40
View File
@@ -0,0 +1,40 @@
function hueToRgb(p, q, t) {
if (t < 0)
t += 1;
if (t > 1)
t -= 1;
if (t < 1 / 6)
return p + (q - p) * 6 * t;
if (t < 1 / 2)
return q;
if (t < 2 / 3)
return p + (q - p) * (2 / 3 - t) * 6;
return p;
}
export function hslaToRgba({ hue, saturation, lightness, alpha }) {
hue /= 360;
saturation /= 100;
lightness /= 100;
let red = 0;
let green = 0;
let blue = 0;
if (!saturation) {
red = green = blue = lightness;
}
else {
const q = lightness < 0.5
? lightness * (1 + saturation)
: lightness + saturation - lightness * saturation;
const p = 2 * lightness - q;
red = hueToRgb(p, q, hue + 1 / 3);
green = hueToRgb(p, q, hue);
blue = hueToRgb(p, q, hue - 1 / 3);
}
return {
red: Math.round(red * 255),
green: Math.round(green * 255),
blue: Math.round(blue * 255),
alpha,
};
}
//# sourceMappingURL=hsla-to-rgba.js.map
+3
View File
@@ -0,0 +1,3 @@
import { Point } from '../types';
export declare const zeroPoint: Point;
export declare const isNum: (v: any) => v is number;
+7
View File
@@ -0,0 +1,7 @@
export const zeroPoint = {
x: 0,
y: 0,
z: 0
};
export const isNum = (v) => typeof v === 'number';
//# sourceMappingURL=inc.js.map
+11
View File
@@ -0,0 +1,11 @@
import { Easing } from '../easing/types';
declare type MixEasing = Easing | Easing[];
declare type InterpolateOptions<T> = {
clamp?: boolean;
ease?: MixEasing;
mixer?: MixerFactory<T>;
};
declare type Mix<T> = (v: number) => T;
export declare type MixerFactory<T> = (from: T, to: T) => Mix<T>;
export declare function interpolate<T>(input: number[], output: T[], { clamp: isClamp, ease, mixer }?: InterpolateOptions<T>): (v: number) => T;
export {};
+90
View File
@@ -0,0 +1,90 @@
import { progress } from './progress';
import { mix } from './mix';
import { mixColor } from './mix-color';
import { mixComplex, mixArray, mixObject } from './mix-complex';
import { color } from 'style-value-types';
import { clamp } from './clamp';
import { pipe } from './pipe';
import { invariant } from 'hey-listen';
const mixNumber = (from, to) => (p) => mix(from, to, p);
function detectMixerFactory(v) {
if (typeof v === 'number') {
return mixNumber;
}
else if (typeof v === 'string') {
if (color.test(v)) {
return mixColor;
}
else {
return mixComplex;
}
}
else if (Array.isArray(v)) {
return mixArray;
}
else if (typeof v === 'object') {
return mixObject;
}
}
function createMixers(output, ease, customMixer) {
const mixers = [];
const mixerFactory = customMixer || detectMixerFactory(output[0]);
const numMixers = output.length - 1;
for (let i = 0; i < numMixers; i++) {
let mixer = mixerFactory(output[i], output[i + 1]);
if (ease) {
const easingFunction = Array.isArray(ease) ? ease[i] : ease;
mixer = pipe(easingFunction, mixer);
}
mixers.push(mixer);
}
return mixers;
}
function fastInterpolate([from, to], [mixer]) {
return (v) => mixer(progress(from, to, v));
}
function slowInterpolate(input, mixers) {
const inputLength = input.length;
const lastInputIndex = inputLength - 1;
return (v) => {
let mixerIndex = 0;
let foundMixerIndex = false;
if (v <= input[0]) {
foundMixerIndex = true;
}
else if (v >= input[lastInputIndex]) {
mixerIndex = lastInputIndex - 1;
foundMixerIndex = true;
}
if (!foundMixerIndex) {
let i = 1;
for (; i < inputLength; i++) {
if (input[i] > v || i === lastInputIndex) {
break;
}
}
mixerIndex = i - 1;
}
const progressInRange = progress(input[mixerIndex], input[mixerIndex + 1], v);
return mixers[mixerIndex](progressInRange);
};
}
export function interpolate(input, output, { clamp: isClamp = true, ease, mixer } = {}) {
const inputLength = input.length;
invariant(inputLength === output.length, 'Both input and output ranges must be the same length');
invariant(!ease || !Array.isArray(ease) || ease.length === inputLength - 1, 'Array of easing functions must be of length `input.length - 1`, as it applies to the transitions **between** the defined values.');
if (input[0] > input[inputLength - 1]) {
input = [].concat(input);
output = [].concat(output);
input.reverse();
output.reverse();
}
const mixers = createMixers(output, ease, mixer);
const interpolator = inputLength === 2
? fastInterpolate(input, mixers)
: slowInterpolate(input, mixers);
return isClamp
? (v) => interpolator(clamp(input[0], input[inputLength - 1], v))
: interpolator;
}
//# sourceMappingURL=interpolate.js.map
+2
View File
@@ -0,0 +1,2 @@
import { Point, Point3D } from '../types';
export declare const isPoint3D: (point: Point) => point is Point3D;
+3
View File
@@ -0,0 +1,3 @@
import { isPoint } from './is-point';
export const isPoint3D = (point) => isPoint(point) && point.hasOwnProperty('z');
//# sourceMappingURL=is-point-3d.js.map

Some files were not shown because too many files have changed in this diff Show More