981 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			981 lines
		
	
	
		
			34 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| '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;
 | 
