UNPKG

58.6 kBJavaScriptView Raw
1import * as THREE from 'three/src/Three';
2import { applyProps } from 'react-three-fiber';
3import _extends from '@babel/runtime/helpers/esm/extends';
4import _objectWithoutPropertiesLoose from '@babel/runtime/helpers/esm/objectWithoutPropertiesLoose';
5import React, { useState, useCallback, forwardRef, useRef, useEffect, useImperativeHandle, useMemo } from 'react';
6
7class Animated {
8 constructor() {
9 this.payload = void 0;
10 this.children = [];
11 }
12
13 getAnimatedValue() {
14 return this.getValue();
15 }
16
17 getPayload() {
18 return this.payload || this;
19 }
20
21 attach() {}
22
23 detach() {}
24
25 getChildren() {
26 return this.children;
27 }
28
29 addChild(child) {
30 if (this.children.length === 0) this.attach();
31 this.children.push(child);
32 }
33
34 removeChild(child) {
35 const index = this.children.indexOf(child);
36 this.children.splice(index, 1);
37 if (this.children.length === 0) this.detach();
38 }
39
40}
41class AnimatedArray extends Animated {
42 constructor() {
43 super(...arguments);
44 this.payload = [];
45
46 this.attach = () => this.payload.forEach(p => p instanceof Animated && p.addChild(this));
47
48 this.detach = () => this.payload.forEach(p => p instanceof Animated && p.removeChild(this));
49 }
50
51}
52class AnimatedObject extends Animated {
53 constructor() {
54 super(...arguments);
55 this.payload = {};
56
57 this.attach = () => Object.values(this.payload).forEach(s => s instanceof Animated && s.addChild(this));
58
59 this.detach = () => Object.values(this.payload).forEach(s => s instanceof Animated && s.removeChild(this));
60 }
61
62 getValue(animated) {
63 if (animated === void 0) {
64 animated = false;
65 }
66
67 const payload = {};
68
69 for (const key in this.payload) {
70 const value = this.payload[key];
71 if (animated && !(value instanceof Animated)) continue;
72 payload[key] = value instanceof Animated ? value[animated ? 'getAnimatedValue' : 'getValue']() : value;
73 }
74
75 return payload;
76 }
77
78 getAnimatedValue() {
79 return this.getValue(true);
80 }
81
82}
83
84let applyAnimatedValues;
85function injectApplyAnimatedValues(fn, transform) {
86 applyAnimatedValues = {
87 fn,
88 transform
89 };
90}
91let colorNames;
92function injectColorNames(names) {
93 colorNames = names;
94}
95let requestFrame = cb => typeof window !== 'undefined' ? window.requestAnimationFrame(cb) : -1;
96let cancelFrame = id => {
97 typeof window !== 'undefined' && window.cancelAnimationFrame(id);
98};
99function injectFrame(raf, caf) {
100 requestFrame = raf;
101 cancelFrame = caf;
102}
103let interpolation;
104function injectStringInterpolator(fn) {
105 interpolation = fn;
106}
107let now = () => Date.now();
108function injectNow(nowFn) {
109 now = nowFn;
110}
111let defaultElement;
112function injectDefaultElement(el) {
113 defaultElement = el;
114}
115let animatedApi = node => node.current;
116function injectAnimatedApi(fn) {
117 animatedApi = fn;
118}
119let createAnimatedStyle;
120function injectCreateAnimatedStyle(factory) {
121 createAnimatedStyle = factory;
122}
123let manualFrameloop = false;
124function injectManualFrameloop(manual) {
125 manualFrameloop = manual;
126}
127
128var Globals = /*#__PURE__*/Object.freeze({
129 get applyAnimatedValues () { return applyAnimatedValues; },
130 injectApplyAnimatedValues: injectApplyAnimatedValues,
131 get colorNames () { return colorNames; },
132 injectColorNames: injectColorNames,
133 get requestFrame () { return requestFrame; },
134 get cancelFrame () { return cancelFrame; },
135 injectFrame: injectFrame,
136 get interpolation () { return interpolation; },
137 injectStringInterpolator: injectStringInterpolator,
138 get now () { return now; },
139 injectNow: injectNow,
140 get defaultElement () { return defaultElement; },
141 injectDefaultElement: injectDefaultElement,
142 get animatedApi () { return animatedApi; },
143 injectAnimatedApi: injectAnimatedApi,
144 get createAnimatedStyle () { return createAnimatedStyle; },
145 injectCreateAnimatedStyle: injectCreateAnimatedStyle,
146 get manualFrameloop () { return manualFrameloop; },
147 injectManualFrameloop: injectManualFrameloop
148});
149
150function createInterpolator(range, output, extrapolate) {
151 if (typeof range === 'function') {
152 return range;
153 }
154
155 if (Array.isArray(range)) {
156 return createInterpolator({
157 range,
158 output: output,
159 extrapolate
160 });
161 }
162
163 if (interpolation && typeof range.output[0] === 'string') {
164 return interpolation(range);
165 }
166
167 const config = range;
168 const outputRange = config.output;
169 const inputRange = config.range || [0, 1];
170 const extrapolateLeft = config.extrapolateLeft || config.extrapolate || 'extend';
171 const extrapolateRight = config.extrapolateRight || config.extrapolate || 'extend';
172
173 const easing = config.easing || (t => t);
174
175 return input => {
176 const range = findRange(input, inputRange);
177 return interpolate(input, inputRange[range], inputRange[range + 1], outputRange[range], outputRange[range + 1], easing, extrapolateLeft, extrapolateRight, config.map);
178 };
179}
180
181function interpolate(input, inputMin, inputMax, outputMin, outputMax, easing, extrapolateLeft, extrapolateRight, map) {
182 let result = map ? map(input) : input; // Extrapolate
183
184 if (result < inputMin) {
185 if (extrapolateLeft === 'identity') return result;else if (extrapolateLeft === 'clamp') result = inputMin;
186 }
187
188 if (result > inputMax) {
189 if (extrapolateRight === 'identity') return result;else if (extrapolateRight === 'clamp') result = inputMax;
190 }
191
192 if (outputMin === outputMax) return outputMin;
193 if (inputMin === inputMax) return input <= inputMin ? outputMin : outputMax; // Input Range
194
195 if (inputMin === -Infinity) result = -result;else if (inputMax === Infinity) result = result - inputMin;else result = (result - inputMin) / (inputMax - inputMin); // Easing
196
197 result = easing(result); // Output Range
198
199 if (outputMin === -Infinity) result = -result;else if (outputMax === Infinity) result = result + outputMin;else result = result * (outputMax - outputMin) + outputMin;
200 return result;
201}
202
203function findRange(input, inputRange) {
204 for (var i = 1; i < inputRange.length - 1; ++i) if (inputRange[i] >= input) break;
205
206 return i - 1;
207}
208
209class AnimatedInterpolation extends AnimatedArray {
210 constructor(parents, range, output) {
211 super();
212 this.calc = void 0;
213 this.payload = parents instanceof AnimatedArray && !(parents instanceof AnimatedInterpolation) ? parents.getPayload() : Array.isArray(parents) ? parents : [parents];
214 this.calc = createInterpolator(range, output);
215 }
216
217 getValue() {
218 return this.calc(...this.payload.map(value => value.getValue()));
219 }
220
221 updateConfig(range, output) {
222 this.calc = createInterpolator(range, output);
223 }
224
225 interpolate(range, output) {
226 return new AnimatedInterpolation(this, range, output);
227 }
228
229}
230
231const interpolate$1 = (parents, range, output) => parents && new AnimatedInterpolation(parents, range, output);
232
233const is = {
234 arr: Array.isArray,
235 obj: a => Object.prototype.toString.call(a) === '[object Object]',
236 fun: a => typeof a === 'function',
237 str: a => typeof a === 'string',
238 num: a => typeof a === 'number',
239 und: a => a === void 0,
240 nul: a => a === null,
241 set: a => a instanceof Set,
242 map: a => a instanceof Map,
243
244 equ(a, b) {
245 if (typeof a !== typeof b) return false;
246 if (is.str(a) || is.num(a)) return a === b;
247 if (is.obj(a) && is.obj(b) && Object.keys(a).length + Object.keys(b).length === 0) return true;
248 let i;
249
250 for (i in a) if (!(i in b)) return false;
251
252 for (i in b) if (a[i] !== b[i]) return false;
253
254 return is.und(i) ? a === b : true;
255 }
256
257};
258function merge(target, lowercase) {
259 if (lowercase === void 0) {
260 lowercase = true;
261 }
262
263 return object => (is.arr(object) ? object : Object.keys(object)).reduce((acc, element) => {
264 const key = lowercase ? element[0].toLowerCase() + element.substring(1) : element;
265 acc[key] = target(key);
266 return acc;
267 }, target);
268}
269function useForceUpdate() {
270 const _useState = useState(false),
271 f = _useState[1];
272
273 const forceUpdate = useCallback(() => f(v => !v), []);
274 return forceUpdate;
275}
276function withDefault(value, defaultValue) {
277 return is.und(value) || is.nul(value) ? defaultValue : value;
278}
279function toArray(a) {
280 return !is.und(a) ? is.arr(a) ? a : [a] : [];
281}
282function callProp(obj) {
283 for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
284 args[_key - 1] = arguments[_key];
285 }
286
287 return is.fun(obj) ? obj(...args) : obj;
288}
289
290function getForwardProps(props) {
291 const to = props.to,
292 from = props.from,
293 config = props.config,
294 onStart = props.onStart,
295 onRest = props.onRest,
296 onFrame = props.onFrame,
297 children = props.children,
298 reset = props.reset,
299 reverse = props.reverse,
300 force = props.force,
301 immediate = props.immediate,
302 delay = props.delay,
303 attach = props.attach,
304 destroyed = props.destroyed,
305 interpolateTo = props.interpolateTo,
306 ref = props.ref,
307 lazy = props.lazy,
308 forward = _objectWithoutPropertiesLoose(props, ["to", "from", "config", "onStart", "onRest", "onFrame", "children", "reset", "reverse", "force", "immediate", "delay", "attach", "destroyed", "interpolateTo", "ref", "lazy"]);
309
310 return forward;
311}
312
313function interpolateTo(props) {
314 const forward = getForwardProps(props);
315 if (is.und(forward)) return _extends({
316 to: forward
317 }, props);
318 const rest = Object.keys(props).reduce((a, k) => !is.und(forward[k]) ? a : _extends({}, a, {
319 [k]: props[k]
320 }), {});
321 return _extends({
322 to: forward
323 }, rest);
324}
325function handleRef(ref, forward) {
326 if (forward) {
327 // If it's a function, assume it's a ref callback
328 if (is.fun(forward)) forward(ref);else if (is.obj(forward)) {
329 forward.current = ref;
330 }
331 }
332
333 return ref;
334}
335
336/**
337 * Wraps the `style` property with `AnimatedStyle`.
338 */
339
340class AnimatedProps extends AnimatedObject {
341 constructor(props, callback) {
342 super();
343 this.update = void 0;
344 this.payload = !props.style ? props : _extends({}, props, {
345 style: createAnimatedStyle(props.style)
346 });
347 this.update = callback;
348 this.attach();
349 }
350
351}
352
353const createAnimatedComponent = Component => {
354 const AnimatedComponent = forwardRef((props, _ref) => {
355 const forceUpdate = useForceUpdate();
356 const mounted = useRef(true);
357 const propsAnimated = useRef(null);
358 const node = useRef(null);
359 const attachProps = useCallback(props => {
360 const oldPropsAnimated = propsAnimated.current;
361
362 const callback = () => {
363 if (node.current) {
364 const didUpdate = applyAnimatedValues.fn(node.current, propsAnimated.current.getAnimatedValue());
365 if (didUpdate === false) forceUpdate();
366 }
367 };
368
369 propsAnimated.current = new AnimatedProps(props, callback);
370 oldPropsAnimated && oldPropsAnimated.detach();
371 }, []);
372 useEffect(() => () => {
373 mounted.current = false;
374 propsAnimated.current && propsAnimated.current.detach();
375 }, []);
376 useImperativeHandle(_ref, () => animatedApi(node, mounted, forceUpdate));
377 attachProps(props);
378
379 const _getValue = propsAnimated.current.getValue(),
380 scrollTop = _getValue.scrollTop,
381 scrollLeft = _getValue.scrollLeft,
382 animatedProps = _objectWithoutPropertiesLoose(_getValue, ["scrollTop", "scrollLeft"]);
383
384 return React.createElement(Component, _extends({}, animatedProps, {
385 ref: childRef => node.current = handleRef(childRef, _ref)
386 }));
387 });
388 return AnimatedComponent;
389};
390
391// http://www.w3.org/TR/css3-color/#svg-color
392const colors = {
393 transparent: 0x00000000,
394 aliceblue: 0xf0f8ffff,
395 antiquewhite: 0xfaebd7ff,
396 aqua: 0x00ffffff,
397 aquamarine: 0x7fffd4ff,
398 azure: 0xf0ffffff,
399 beige: 0xf5f5dcff,
400 bisque: 0xffe4c4ff,
401 black: 0x000000ff,
402 blanchedalmond: 0xffebcdff,
403 blue: 0x0000ffff,
404 blueviolet: 0x8a2be2ff,
405 brown: 0xa52a2aff,
406 burlywood: 0xdeb887ff,
407 burntsienna: 0xea7e5dff,
408 cadetblue: 0x5f9ea0ff,
409 chartreuse: 0x7fff00ff,
410 chocolate: 0xd2691eff,
411 coral: 0xff7f50ff,
412 cornflowerblue: 0x6495edff,
413 cornsilk: 0xfff8dcff,
414 crimson: 0xdc143cff,
415 cyan: 0x00ffffff,
416 darkblue: 0x00008bff,
417 darkcyan: 0x008b8bff,
418 darkgoldenrod: 0xb8860bff,
419 darkgray: 0xa9a9a9ff,
420 darkgreen: 0x006400ff,
421 darkgrey: 0xa9a9a9ff,
422 darkkhaki: 0xbdb76bff,
423 darkmagenta: 0x8b008bff,
424 darkolivegreen: 0x556b2fff,
425 darkorange: 0xff8c00ff,
426 darkorchid: 0x9932ccff,
427 darkred: 0x8b0000ff,
428 darksalmon: 0xe9967aff,
429 darkseagreen: 0x8fbc8fff,
430 darkslateblue: 0x483d8bff,
431 darkslategray: 0x2f4f4fff,
432 darkslategrey: 0x2f4f4fff,
433 darkturquoise: 0x00ced1ff,
434 darkviolet: 0x9400d3ff,
435 deeppink: 0xff1493ff,
436 deepskyblue: 0x00bfffff,
437 dimgray: 0x696969ff,
438 dimgrey: 0x696969ff,
439 dodgerblue: 0x1e90ffff,
440 firebrick: 0xb22222ff,
441 floralwhite: 0xfffaf0ff,
442 forestgreen: 0x228b22ff,
443 fuchsia: 0xff00ffff,
444 gainsboro: 0xdcdcdcff,
445 ghostwhite: 0xf8f8ffff,
446 gold: 0xffd700ff,
447 goldenrod: 0xdaa520ff,
448 gray: 0x808080ff,
449 green: 0x008000ff,
450 greenyellow: 0xadff2fff,
451 grey: 0x808080ff,
452 honeydew: 0xf0fff0ff,
453 hotpink: 0xff69b4ff,
454 indianred: 0xcd5c5cff,
455 indigo: 0x4b0082ff,
456 ivory: 0xfffff0ff,
457 khaki: 0xf0e68cff,
458 lavender: 0xe6e6faff,
459 lavenderblush: 0xfff0f5ff,
460 lawngreen: 0x7cfc00ff,
461 lemonchiffon: 0xfffacdff,
462 lightblue: 0xadd8e6ff,
463 lightcoral: 0xf08080ff,
464 lightcyan: 0xe0ffffff,
465 lightgoldenrodyellow: 0xfafad2ff,
466 lightgray: 0xd3d3d3ff,
467 lightgreen: 0x90ee90ff,
468 lightgrey: 0xd3d3d3ff,
469 lightpink: 0xffb6c1ff,
470 lightsalmon: 0xffa07aff,
471 lightseagreen: 0x20b2aaff,
472 lightskyblue: 0x87cefaff,
473 lightslategray: 0x778899ff,
474 lightslategrey: 0x778899ff,
475 lightsteelblue: 0xb0c4deff,
476 lightyellow: 0xffffe0ff,
477 lime: 0x00ff00ff,
478 limegreen: 0x32cd32ff,
479 linen: 0xfaf0e6ff,
480 magenta: 0xff00ffff,
481 maroon: 0x800000ff,
482 mediumaquamarine: 0x66cdaaff,
483 mediumblue: 0x0000cdff,
484 mediumorchid: 0xba55d3ff,
485 mediumpurple: 0x9370dbff,
486 mediumseagreen: 0x3cb371ff,
487 mediumslateblue: 0x7b68eeff,
488 mediumspringgreen: 0x00fa9aff,
489 mediumturquoise: 0x48d1ccff,
490 mediumvioletred: 0xc71585ff,
491 midnightblue: 0x191970ff,
492 mintcream: 0xf5fffaff,
493 mistyrose: 0xffe4e1ff,
494 moccasin: 0xffe4b5ff,
495 navajowhite: 0xffdeadff,
496 navy: 0x000080ff,
497 oldlace: 0xfdf5e6ff,
498 olive: 0x808000ff,
499 olivedrab: 0x6b8e23ff,
500 orange: 0xffa500ff,
501 orangered: 0xff4500ff,
502 orchid: 0xda70d6ff,
503 palegoldenrod: 0xeee8aaff,
504 palegreen: 0x98fb98ff,
505 paleturquoise: 0xafeeeeff,
506 palevioletred: 0xdb7093ff,
507 papayawhip: 0xffefd5ff,
508 peachpuff: 0xffdab9ff,
509 peru: 0xcd853fff,
510 pink: 0xffc0cbff,
511 plum: 0xdda0ddff,
512 powderblue: 0xb0e0e6ff,
513 purple: 0x800080ff,
514 rebeccapurple: 0x663399ff,
515 red: 0xff0000ff,
516 rosybrown: 0xbc8f8fff,
517 royalblue: 0x4169e1ff,
518 saddlebrown: 0x8b4513ff,
519 salmon: 0xfa8072ff,
520 sandybrown: 0xf4a460ff,
521 seagreen: 0x2e8b57ff,
522 seashell: 0xfff5eeff,
523 sienna: 0xa0522dff,
524 silver: 0xc0c0c0ff,
525 skyblue: 0x87ceebff,
526 slateblue: 0x6a5acdff,
527 slategray: 0x708090ff,
528 slategrey: 0x708090ff,
529 snow: 0xfffafaff,
530 springgreen: 0x00ff7fff,
531 steelblue: 0x4682b4ff,
532 tan: 0xd2b48cff,
533 teal: 0x008080ff,
534 thistle: 0xd8bfd8ff,
535 tomato: 0xff6347ff,
536 turquoise: 0x40e0d0ff,
537 violet: 0xee82eeff,
538 wheat: 0xf5deb3ff,
539 white: 0xffffffff,
540 whitesmoke: 0xf5f5f5ff,
541 yellow: 0xffff00ff,
542 yellowgreen: 0x9acd32ff
543};
544
545const config = {
546 default: {
547 tension: 170,
548 friction: 26
549 },
550 gentle: {
551 tension: 120,
552 friction: 14
553 },
554 wobbly: {
555 tension: 180,
556 friction: 12
557 },
558 stiff: {
559 tension: 210,
560 friction: 20
561 },
562 slow: {
563 tension: 280,
564 friction: 60
565 },
566 molasses: {
567 tension: 280,
568 friction: 120
569 }
570};
571
572// const INTEGER = '[-+]?\\d+';
573const NUMBER = '[-+]?\\d*\\.?\\d+';
574const PERCENTAGE = NUMBER + '%';
575
576function call() {
577 for (var _len = arguments.length, parts = new Array(_len), _key = 0; _key < _len; _key++) {
578 parts[_key] = arguments[_key];
579 }
580
581 return '\\(\\s*(' + parts.join(')\\s*,\\s*(') + ')\\s*\\)';
582}
583
584const rgb = new RegExp('rgb' + call(NUMBER, NUMBER, NUMBER));
585const rgba = new RegExp('rgba' + call(NUMBER, NUMBER, NUMBER, NUMBER));
586const hsl = new RegExp('hsl' + call(NUMBER, PERCENTAGE, PERCENTAGE));
587const hsla = new RegExp('hsla' + call(NUMBER, PERCENTAGE, PERCENTAGE, NUMBER));
588const hex3 = /^#([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/;
589const hex4 = /^#([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/;
590const hex6 = /^#([0-9a-fA-F]{6})$/;
591const hex8 = /^#([0-9a-fA-F]{8})$/;
592
593/*
594https://github.com/react-community/normalize-css-color
595
596BSD 3-Clause License
597
598Copyright (c) 2016, React Community
599All rights reserved.
600
601Redistribution and use in source and binary forms, with or without
602modification, are permitted provided that the following conditions are met:
603
604* Redistributions of source code must retain the above copyright notice, this
605 list of conditions and the following disclaimer.
606
607* Redistributions in binary form must reproduce the above copyright notice,
608 this list of conditions and the following disclaimer in the documentation
609 and/or other materials provided with the distribution.
610
611* Neither the name of the copyright holder nor the names of its
612 contributors may be used to endorse or promote products derived from
613 this software without specific prior written permission.
614
615THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
616AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
617IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
618DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
619FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
620DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
621SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
622CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
623OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
624OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
625*/
626function normalizeColor(color) {
627 let match;
628
629 if (typeof color === 'number') {
630 return color >>> 0 === color && color >= 0 && color <= 0xffffffff ? color : null;
631 } // Ordered based on occurrences on Facebook codebase
632
633
634 if (match = hex6.exec(color)) return parseInt(match[1] + 'ff', 16) >>> 0;
635 if (colors.hasOwnProperty(color)) return colors[color];
636
637 if (match = rgb.exec(color)) {
638 return (parse255(match[1]) << 24 | // r
639 parse255(match[2]) << 16 | // g
640 parse255(match[3]) << 8 | // b
641 0x000000ff) >>> // a
642 0;
643 }
644
645 if (match = rgba.exec(color)) {
646 return (parse255(match[1]) << 24 | // r
647 parse255(match[2]) << 16 | // g
648 parse255(match[3]) << 8 | // b
649 parse1(match[4])) >>> // a
650 0;
651 }
652
653 if (match = hex3.exec(color)) {
654 return parseInt(match[1] + match[1] + // r
655 match[2] + match[2] + // g
656 match[3] + match[3] + // b
657 'ff', // a
658 16) >>> 0;
659 } // https://drafts.csswg.org/css-color-4/#hex-notation
660
661
662 if (match = hex8.exec(color)) return parseInt(match[1], 16) >>> 0;
663
664 if (match = hex4.exec(color)) {
665 return parseInt(match[1] + match[1] + // r
666 match[2] + match[2] + // g
667 match[3] + match[3] + // b
668 match[4] + match[4], // a
669 16) >>> 0;
670 }
671
672 if (match = hsl.exec(color)) {
673 return (hslToRgb(parse360(match[1]), // h
674 parsePercentage(match[2]), // s
675 parsePercentage(match[3]) // l
676 ) | 0x000000ff) >>> // a
677 0;
678 }
679
680 if (match = hsla.exec(color)) {
681 return (hslToRgb(parse360(match[1]), // h
682 parsePercentage(match[2]), // s
683 parsePercentage(match[3]) // l
684 ) | parse1(match[4])) >>> // a
685 0;
686 }
687
688 return null;
689}
690
691function hue2rgb(p, q, t) {
692 if (t < 0) t += 1;
693 if (t > 1) t -= 1;
694 if (t < 1 / 6) return p + (q - p) * 6 * t;
695 if (t < 1 / 2) return q;
696 if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
697 return p;
698}
699
700function hslToRgb(h, s, l) {
701 const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
702 const p = 2 * l - q;
703 const r = hue2rgb(p, q, h + 1 / 3);
704 const g = hue2rgb(p, q, h);
705 const b = hue2rgb(p, q, h - 1 / 3);
706 return Math.round(r * 255) << 24 | Math.round(g * 255) << 16 | Math.round(b * 255) << 8;
707}
708
709function parse255(str) {
710 const int = parseInt(str, 10);
711 if (int < 0) return 0;
712 if (int > 255) return 255;
713 return int;
714}
715
716function parse360(str) {
717 const int = parseFloat(str);
718 return (int % 360 + 360) % 360 / 360;
719}
720
721function parse1(str) {
722 const num = parseFloat(str);
723 if (num < 0) return 0;
724 if (num > 1) return 255;
725 return Math.round(num * 255);
726}
727
728function parsePercentage(str) {
729 // parseFloat conveniently ignores the final %
730 const int = parseFloat(str);
731 if (int < 0) return 0;
732 if (int > 100) return 1;
733 return int / 100;
734}
735
736function colorToRgba(input) {
737 let int32Color = normalizeColor(input);
738 if (int32Color === null) return input;
739 int32Color = int32Color || 0;
740 let r = (int32Color & 0xff000000) >>> 24;
741 let g = (int32Color & 0x00ff0000) >>> 16;
742 let b = (int32Color & 0x0000ff00) >>> 8;
743 let a = (int32Color & 0x000000ff) / 255;
744 return `rgba(${r}, ${g}, ${b}, ${a})`;
745} // Problem: https://github.com/animatedjs/animated/pull/102
746// Solution: https://stackoverflow.com/questions/638565/parsing-scientific-notation-sensibly/658662
747
748
749const stringShapeRegex = /[+\-]?(?:0|[1-9]\d*)(?:\.\d*)?(?:[eE][+\-]?\d+)?/g; // Covers rgb, rgba, hsl, hsla
750// Taken from https://gist.github.com/olmokramer/82ccce673f86db7cda5e
751
752const colorRegex = /(#(?:[0-9a-f]{2}){2,4}|(#[0-9a-f]{3})|(rgb|hsl)a?\((-?\d+%?[,\s]+){2,3}\s*[\d\.]+%?\))/gi; // Covers color names (transparent, blue, etc.)
753
754const colorNamesRegex = new RegExp(`(${Object.keys(colors).join('|')})`, 'g');
755/**
756 * Supports string shapes by extracting numbers so new values can be computed,
757 * and recombines those values into new strings of the same shape. Supports
758 * things like:
759 *
760 * rgba(123, 42, 99, 0.36) // colors
761 * -45deg // values with units
762 * 0 2px 2px 0px rgba(0, 0, 0, 0.12) // box shadows
763 */
764
765const createStringInterpolator = config => {
766 // Replace colors with rgba
767 const outputRange = config.output.map(rangeValue => rangeValue.replace(colorRegex, colorToRgba)).map(rangeValue => rangeValue.replace(colorNamesRegex, colorToRgba));
768 const outputRanges = outputRange[0].match(stringShapeRegex).map(() => []);
769 outputRange.forEach(value => {
770 value.match(stringShapeRegex).forEach((number, i) => outputRanges[i].push(+number));
771 });
772 const interpolations = outputRange[0].match(stringShapeRegex).map((_value, i) => createInterpolator(_extends({}, config, {
773 output: outputRanges[i]
774 })));
775 return input => {
776 let i = 0;
777 return outputRange[0] // 'rgba(0, 100, 200, 0)'
778 // ->
779 // 'rgba(${interpolations[0](input)}, ${interpolations[1](input)}, ...'
780 .replace(stringShapeRegex, () => interpolations[i++](input)) // rgba requires that the r,g,b are integers.... so we want to round them, but we *dont* want to
781 // round the opacity (4th column).
782 .replace(/rgba\(([0-9\.-]+), ([0-9\.-]+), ([0-9\.-]+), ([0-9\.-]+)\)/gi, (_, p1, p2, p3, p4) => `rgba(${Math.round(p1)}, ${Math.round(p2)}, ${Math.round(p3)}, ${p4})`);
783 };
784};
785
786/** API
787 * useChain(references, timeSteps, timeFrame)
788 */
789
790function useChain(refs, timeSteps, timeFrame) {
791 if (timeFrame === void 0) {
792 timeFrame = 1000;
793 }
794
795 const previous = useRef();
796 useEffect(() => {
797 if (is.equ(refs, previous.current)) refs.forEach((_ref) => {
798 let current = _ref.current;
799 return current && current.start();
800 });else if (timeSteps) {
801 refs.forEach((_ref2, index) => {
802 let current = _ref2.current;
803
804 if (current) {
805 const ctrls = current.controllers;
806
807 if (ctrls.length) {
808 const t = timeFrame * timeSteps[index];
809 ctrls.forEach(ctrl => {
810 ctrl.queue = ctrl.queue.map(e => _extends({}, e, {
811 delay: e.delay + t
812 }));
813 ctrl.start();
814 });
815 }
816 }
817 });
818 } else refs.reduce((q, _ref3, rI) => {
819 let current = _ref3.current;
820 return q = q.then(() => current.start());
821 }, Promise.resolve());
822 previous.current = refs;
823 });
824}
825
826/**
827 * Animated works by building a directed acyclic graph of dependencies
828 * transparently when you render your Animated components.
829 *
830 * new Animated.Value(0)
831 * .interpolate() .interpolate() new Animated.Value(1)
832 * opacity translateY scale
833 * style transform
834 * View#234 style
835 * View#123
836 *
837 * A) Top Down phase
838 * When an AnimatedValue is updated, we recursively go down through this
839 * graph in order to find leaf nodes: the views that we flag as needing
840 * an update.
841 *
842 * B) Bottom Up phase
843 * When a view is flagged as needing an update, we recursively go back up
844 * in order to build the new value that it needs. The reason why we need
845 * this two-phases process is to deal with composite props such as
846 * transform which can receive values from multiple parents.
847 */
848function addAnimatedStyles(node, styles) {
849 if ('update' in node) {
850 styles.add(node);
851 } else {
852 node.getChildren().forEach(child => addAnimatedStyles(child, styles));
853 }
854}
855
856class AnimatedValue extends Animated {
857 constructor(_value) {
858 var _this;
859
860 super();
861 _this = this;
862 this.animatedStyles = new Set();
863 this.value = void 0;
864 this.startPosition = void 0;
865 this.lastPosition = void 0;
866 this.lastVelocity = void 0;
867 this.startTime = void 0;
868 this.lastTime = void 0;
869 this.done = false;
870
871 this.setValue = function (value, flush) {
872 if (flush === void 0) {
873 flush = true;
874 }
875
876 _this.value = value;
877 if (flush) _this.flush();
878 };
879
880 this.value = _value;
881 this.startPosition = _value;
882 this.lastPosition = _value;
883 }
884
885 flush() {
886 if (this.animatedStyles.size === 0) {
887 addAnimatedStyles(this, this.animatedStyles);
888 }
889
890 this.animatedStyles.forEach(animatedStyle => animatedStyle.update());
891 }
892
893 clearStyles() {
894 this.animatedStyles.clear();
895 }
896
897 getValue() {
898 return this.value;
899 }
900
901 interpolate(range, output) {
902 return new AnimatedInterpolation(this, range, output);
903 }
904
905}
906
907class AnimatedValueArray extends AnimatedArray {
908 constructor(values) {
909 super();
910 this.payload = values.map(n => new AnimatedValue(n));
911 }
912
913 setValue(value, flush) {
914 if (flush === void 0) {
915 flush = true;
916 }
917
918 if (Array.isArray(value)) {
919 if (value.length === this.payload.length) {
920 value.forEach((v, i) => this.payload[i].setValue(v, flush));
921 }
922 } else {
923 this.payload.forEach(p => p.setValue(value, flush));
924 }
925 }
926
927 getValue() {
928 return this.payload.map(v => v.getValue());
929 }
930
931 interpolate(range, output) {
932 return new AnimatedInterpolation(this, range, output);
933 }
934
935}
936
937let active = false;
938const controllers = new Set();
939
940const update = () => {
941 if (!active) return;
942 let time = now();
943
944 for (let controller of controllers) {
945 let isActive = false;
946
947 for (let configIdx = 0; configIdx < controller.configs.length; configIdx++) {
948 let config = controller.configs[configIdx];
949 let endOfAnimation, lastTime;
950
951 for (let valIdx = 0; valIdx < config.animatedValues.length; valIdx++) {
952 let animation = config.animatedValues[valIdx]; // If an animation is done, skip, until all of them conclude
953
954 if (animation.done) continue;
955 let from = config.fromValues[valIdx];
956 let to = config.toValues[valIdx];
957 let position = animation.lastPosition;
958 let isAnimated = to instanceof Animated;
959 let velocity = Array.isArray(config.initialVelocity) ? config.initialVelocity[valIdx] : config.initialVelocity;
960 if (isAnimated) to = to.getValue(); // Conclude animation if it's either immediate, or from-values match end-state
961
962 if (config.immediate) {
963 animation.setValue(to);
964 animation.done = true;
965 continue;
966 } // Break animation when string values are involved
967
968
969 if (typeof from === 'string' || typeof to === 'string') {
970 animation.setValue(to);
971 animation.done = true;
972 continue;
973 }
974
975 if (config.duration !== void 0) {
976 /** Duration easing */
977 position = from + config.easing((time - animation.startTime) / config.duration) * (to - from);
978 endOfAnimation = time >= animation.startTime + config.duration;
979 } else if (config.decay) {
980 /** Decay easing */
981 position = from + velocity / (1 - 0.998) * (1 - Math.exp(-(1 - 0.998) * (time - controller.startTime)));
982 endOfAnimation = Math.abs(animation.lastPosition - position) < 0.1;
983 if (endOfAnimation) to = position;
984 } else {
985 /** Spring easing */
986 lastTime = animation.lastTime !== void 0 ? animation.lastTime : time;
987 velocity = animation.lastVelocity !== void 0 ? animation.lastVelocity : config.initialVelocity; // If we lost a lot of frames just jump to the end.
988
989 if (time > lastTime + 64) lastTime = time; // http://gafferongames.com/game-physics/fix-your-timestep/
990
991 let numSteps = Math.floor(time - lastTime);
992
993 for (let i = 0; i < numSteps; ++i) {
994 let force = -config.tension * (position - to);
995 let damping = -config.friction * velocity;
996 let acceleration = (force + damping) / config.mass;
997 velocity = velocity + acceleration * 1 / 1000;
998 position = position + velocity * 1 / 1000;
999 } // Conditions for stopping the spring animation
1000
1001
1002 let isOvershooting = config.clamp && config.tension !== 0 ? from < to ? position > to : position < to : false;
1003 let isVelocity = Math.abs(velocity) <= config.precision;
1004 let isDisplacement = config.tension !== 0 ? Math.abs(to - position) <= config.precision : true;
1005 endOfAnimation = isOvershooting || isVelocity && isDisplacement;
1006 animation.lastVelocity = velocity;
1007 animation.lastTime = time;
1008 } // Trails aren't done until their parents conclude
1009
1010
1011 if (isAnimated && !config.toValues[valIdx].done) endOfAnimation = false;
1012
1013 if (endOfAnimation) {
1014 // Ensure that we end up with a round value
1015 if (animation.value !== to) position = to;
1016 animation.done = true;
1017 } else isActive = true;
1018
1019 animation.setValue(position);
1020 animation.lastPosition = position;
1021 } // Keep track of updated values only when necessary
1022
1023
1024 if (controller.props.onFrame) controller.values[config.name] = config.interpolation.getValue();
1025 } // Update callbacks in the end of the frame
1026
1027
1028 if (controller.props.onFrame) controller.props.onFrame(controller.values); // Either call onEnd or next frame
1029
1030 if (!isActive) {
1031 controllers.delete(controller);
1032 controller.stop(true);
1033 }
1034 } // Loop over as long as there are controllers ...
1035
1036
1037 if (controllers.size) {
1038 if (!manualFrameloop) requestFrame(update);
1039 } else active = false;
1040};
1041
1042const start = controller => {
1043 if (!controllers.has(controller)) {
1044 controllers.add(controller);
1045 if (!active && !manualFrameloop) requestFrame(update);
1046 active = true;
1047 }
1048};
1049
1050const stop = controller => {
1051 if (controllers.has(controller)) controllers.delete(controller);
1052};
1053
1054let G = 0;
1055
1056class Controller {
1057 constructor() {
1058 this.id = void 0;
1059 this.idle = true;
1060 this.hasChanged = false;
1061 this.guid = 0;
1062 this.local = 0;
1063 this.props = {};
1064 this.merged = {};
1065 this.animations = {};
1066 this.interpolations = {};
1067 this.values = {};
1068 this.configs = [];
1069 this.listeners = [];
1070 this.queue = [];
1071 this.localQueue = void 0;
1072
1073 this.getValues = () => this.interpolations;
1074
1075 this.id = G++;
1076 }
1077 /** update(props)
1078 * This function filters input props and creates an array of tasks which are executed in .start()
1079 * Each task is allowed to carry a delay, which means it can execute asnychroneously */
1080
1081
1082 update(args) {
1083 //this._id = n + this.id
1084 if (!args) return this; // Extract delay and the to-prop from props
1085
1086 const _ref = interpolateTo(args),
1087 _ref$delay = _ref.delay,
1088 delay = _ref$delay === void 0 ? 0 : _ref$delay,
1089 to = _ref.to,
1090 props = _objectWithoutPropertiesLoose(_ref, ["delay", "to"]);
1091
1092 if (is.arr(to) || is.fun(to)) {
1093 // If config is either a function or an array queue it up as is
1094 this.queue.push(_extends({}, props, {
1095 delay,
1096 to
1097 }));
1098 } else if (to) {
1099 // Otherwise go through each key since it could be delayed individually
1100 let merge$$1 = {};
1101 Object.entries(to).forEach((_ref2) => {
1102 let k = _ref2[0],
1103 v = _ref2[1];
1104
1105 // Fetch delay and create an entry, consisting of the to-props, the delay, and basic props
1106 const entry = _extends({
1107 to: {
1108 [k]: v
1109 },
1110 delay: callProp(delay, k)
1111 }, props); // If it doesn't have a delay, merge it, otherwise add it to the queue
1112
1113
1114 if (!entry.delay) merge$$1 = _extends({}, merge$$1, entry, {
1115 to: _extends({}, merge$$1.to, entry.to)
1116 });else this.queue = [...this.queue, entry];
1117 }); // Append merged props, if present
1118
1119 if (Object.keys(merge$$1).length > 0) this.queue = [...this.queue, merge$$1];
1120 } // Sort queue, so that async calls go last
1121
1122
1123 this.queue = this.queue.sort((a, b) => a.delay - b.delay); // Diff the reduced props immediately (they'll contain the from-prop and some config)
1124
1125 this.diff(props);
1126 return this;
1127 }
1128 /** start(onEnd)
1129 * This function either executes a queue, if present, or starts the frameloop, which animates */
1130
1131
1132 start(onEnd) {
1133 // If a queue is present we must excecute it
1134 if (this.queue.length) {
1135 this.idle = false; // Updates can interrupt trailing queues, in that case we just merge values
1136
1137 if (this.localQueue) {
1138 this.localQueue.forEach((_ref3) => {
1139 let _ref3$from = _ref3.from,
1140 from = _ref3$from === void 0 ? {} : _ref3$from,
1141 _ref3$to = _ref3.to,
1142 to = _ref3$to === void 0 ? {} : _ref3$to;
1143 if (is.obj(from)) this.merged = _extends({}, from, this.merged);
1144 if (is.obj(to)) this.merged = _extends({}, this.merged, to);
1145 });
1146 } // The guid helps us tracking frames, a new queue over an old one means an override
1147 // We discard async calls in that caseÍ
1148
1149
1150 const local = this.local = ++this.guid;
1151 const queue = this.localQueue = this.queue;
1152 this.queue = []; // Go through each entry and execute it
1153
1154 queue.forEach((_ref4, index) => {
1155 let delay = _ref4.delay,
1156 props = _objectWithoutPropertiesLoose(_ref4, ["delay"]);
1157
1158 const cb = finished => {
1159 if (index === queue.length - 1 && local === this.guid && finished) {
1160 this.idle = true;
1161 if (this.props.onRest) this.props.onRest(this.merged);
1162 }
1163
1164 if (onEnd) onEnd();
1165 }; // Entries can be delayed, ansyc or immediate
1166
1167
1168 let async = is.arr(props.to) || is.fun(props.to);
1169
1170 if (delay) {
1171 setTimeout(() => {
1172 if (local === this.guid) {
1173 if (async) this.runAsync(props, cb);else this.diff(props).start(cb);
1174 }
1175 }, delay);
1176 } else if (async) this.runAsync(props, cb);else this.diff(props).start(cb);
1177 });
1178 } // Otherwise we kick of the frameloop
1179 else {
1180 if (is.fun(onEnd)) this.listeners.push(onEnd);
1181 if (this.props.onStart) this.props.onStart();
1182 start(this);
1183 }
1184
1185 return this;
1186 }
1187
1188 stop(finished) {
1189 this.listeners.forEach(onEnd => onEnd(finished));
1190 this.listeners = [];
1191 return this;
1192 }
1193 /** Pause sets onEnd listeners free, but also removes the controller from the frameloop */
1194
1195
1196 pause(finished) {
1197 this.stop(true);
1198 if (finished) stop(this);
1199 return this;
1200 }
1201
1202 runAsync(_ref5, onEnd) {
1203 var _this = this;
1204
1205 let delay = _ref5.delay,
1206 props = _objectWithoutPropertiesLoose(_ref5, ["delay"]);
1207
1208 const local = this.local; // If "to" is either a function or an array it will be processed async, therefor "to" should be empty right now
1209 // If the view relies on certain values "from" has to be present
1210
1211 let queue = Promise.resolve(undefined);
1212
1213 if (is.arr(props.to)) {
1214 for (let i = 0; i < props.to.length; i++) {
1215 const index = i;
1216
1217 const fresh = _extends({}, props, {
1218 to: props.to[index]
1219 });
1220
1221 if (is.arr(fresh.config)) fresh.config = fresh.config[index];
1222 queue = queue.then(() => {
1223 //this.stop()
1224 if (local === this.guid) return new Promise(r => this.diff(interpolateTo(fresh)).start(r));
1225 });
1226 }
1227 } else if (is.fun(props.to)) {
1228 let index = 0;
1229 let last;
1230 queue = queue.then(() => props.to( // next(props)
1231 p => {
1232 const fresh = _extends({}, props, interpolateTo(p));
1233
1234 if (is.arr(fresh.config)) fresh.config = fresh.config[index];
1235 index++; //this.stop()
1236
1237 if (local === this.guid) return last = new Promise(r => this.diff(fresh).start(r));
1238 return;
1239 }, // cancel()
1240 function (finished) {
1241 if (finished === void 0) {
1242 finished = true;
1243 }
1244
1245 return _this.stop(finished);
1246 }).then(() => last));
1247 }
1248
1249 queue.then(onEnd);
1250 }
1251
1252 diff(props) {
1253 this.props = _extends({}, this.props, props);
1254 let _this$props = this.props,
1255 _this$props$from = _this$props.from,
1256 from = _this$props$from === void 0 ? {} : _this$props$from,
1257 _this$props$to = _this$props.to,
1258 to = _this$props$to === void 0 ? {} : _this$props$to,
1259 _this$props$config = _this$props.config,
1260 config = _this$props$config === void 0 ? {} : _this$props$config,
1261 reverse = _this$props.reverse,
1262 attach = _this$props.attach,
1263 reset = _this$props.reset,
1264 immediate = _this$props.immediate; // Reverse values when requested
1265
1266 if (reverse) {
1267 var _ref6 = [to, from];
1268 from = _ref6[0];
1269 to = _ref6[1];
1270 } // This will collect all props that were ever set, reset merged props when necessary
1271
1272
1273 this.merged = _extends({}, from, this.merged, to);
1274 this.hasChanged = false; // Attachment handling, trailed springs can "attach" themselves to a previous spring
1275
1276 let target = attach && attach(this); // Reduces input { name: value } pairs into animated values
1277
1278 this.animations = Object.entries(this.merged).reduce((acc, _ref7) => {
1279 let name = _ref7[0],
1280 value = _ref7[1];
1281 // Issue cached entries, except on reset
1282 let entry = acc[name] || {}; // Figure out what the value is supposed to be
1283
1284 const isNumber = is.num(value);
1285 const isString = is.str(value) && !value.startsWith('#') && !/\d/.test(value) && !colorNames[value];
1286 const isArray = is.arr(value);
1287 const isInterpolation = !isNumber && !isArray && !isString;
1288 let fromValue = !is.und(from[name]) ? from[name] : value;
1289 let toValue = isNumber || isArray ? value : isString ? value : 1;
1290 let toConfig = callProp(config, name);
1291 if (target) toValue = target.animations[name].parent;
1292 let parent = entry.parent,
1293 interpolation$$1 = entry.interpolation,
1294 toValues = toArray(target ? toValue.getPayload() : toValue),
1295 animatedValues;
1296 let newValue = value;
1297 if (isInterpolation) newValue = interpolation({
1298 range: [0, 1],
1299 output: [value, value]
1300 })(1);
1301 let currentValue = interpolation$$1 && interpolation$$1.getValue(); // Change detection flags
1302
1303 const isFirst = is.und(parent);
1304 const isActive = !isFirst && entry.animatedValues.some(v => !v.done);
1305 const currentValueDiffersFromGoal = !is.equ(newValue, currentValue);
1306 const hasNewGoal = !is.equ(newValue, entry.previous);
1307 const hasNewConfig = !is.equ(toConfig, entry.config); // Change animation props when props indicate a new goal (new value differs from previous one)
1308 // and current values differ from it. Config changes trigger a new update as well (though probably shouldn't?)
1309
1310 if (reset || hasNewGoal && currentValueDiffersFromGoal || hasNewConfig) {
1311 // Convert regular values into animated values, ALWAYS re-use if possible
1312 if (isNumber || isString) parent = interpolation$$1 = entry.parent || new AnimatedValue(fromValue);else if (isArray) parent = interpolation$$1 = entry.parent || new AnimatedValueArray(fromValue);else if (isInterpolation) {
1313 let prev = entry.interpolation && entry.interpolation.calc(entry.parent.value);
1314 prev = prev !== void 0 && !reset ? prev : fromValue;
1315
1316 if (entry.parent) {
1317 parent = entry.parent;
1318 parent.setValue(0, false);
1319 } else parent = new AnimatedValue(0);
1320
1321 const range = {
1322 output: [prev, value]
1323 };
1324
1325 if (entry.interpolation) {
1326 interpolation$$1 = entry.interpolation;
1327 entry.interpolation.updateConfig(range);
1328 } else interpolation$$1 = parent.interpolate(range);
1329 }
1330 toValues = toArray(target ? toValue.getPayload() : toValue);
1331 animatedValues = toArray(parent.getPayload());
1332 if (reset && !isInterpolation) parent.setValue(fromValue, false);
1333 this.hasChanged = true; // Reset animated values
1334
1335 animatedValues.forEach(value => {
1336 value.startPosition = value.value;
1337 value.lastPosition = value.value;
1338 value.lastVelocity = isActive ? value.lastVelocity : undefined;
1339 value.lastTime = isActive ? value.lastTime : undefined;
1340 value.startTime = now();
1341 value.done = false;
1342 value.animatedStyles.clear();
1343 }); // Set immediate values
1344
1345 if (callProp(immediate, name)) parent.setValue(value, false);
1346 return _extends({}, acc, {
1347 [name]: _extends({}, entry, {
1348 name,
1349 parent,
1350 interpolation: interpolation$$1,
1351 animatedValues,
1352 toValues,
1353 previous: newValue,
1354 config: toConfig,
1355 fromValues: toArray(parent.getValue()),
1356 immediate: callProp(immediate, name),
1357 initialVelocity: withDefault(toConfig.velocity, 0),
1358 clamp: withDefault(toConfig.clamp, false),
1359 precision: withDefault(toConfig.precision, 0.01),
1360 tension: withDefault(toConfig.tension, 170),
1361 friction: withDefault(toConfig.friction, 26),
1362 mass: withDefault(toConfig.mass, 1),
1363 duration: toConfig.duration,
1364 easing: withDefault(toConfig.easing, t => t),
1365 decay: toConfig.decay
1366 })
1367 });
1368 } else {
1369 if (!currentValueDiffersFromGoal) {
1370 // So ... the current target value (newValue) appears to be different from the previous value,
1371 // which normally constitutes an update, but the actual value (currentValue) matches the target!
1372 // In order to resolve this without causing an animation update we silently flag the animation as done,
1373 // which it technically is. Interpolations also needs a config update with their target set to 1.
1374 if (isInterpolation) {
1375 parent.setValue(1, false);
1376 interpolation$$1.updateConfig({
1377 output: [newValue, newValue]
1378 });
1379 }
1380
1381 parent.done = true;
1382 this.hasChanged = true;
1383 return _extends({}, acc, {
1384 [name]: _extends({}, acc[name], {
1385 previous: newValue
1386 })
1387 });
1388 }
1389
1390 return acc;
1391 }
1392 }, this.animations);
1393
1394 if (this.hasChanged) {
1395 // Make animations available to frameloop
1396 this.configs = Object.values(this.animations);
1397 this.values = {};
1398 this.interpolations = {};
1399
1400 for (let key in this.animations) {
1401 this.interpolations[key] = this.animations[key].interpolation;
1402 this.values[key] = this.animations[key].interpolation.getValue();
1403 }
1404 }
1405
1406 return this;
1407 }
1408
1409 destroy() {
1410 this.stop();
1411 this.props = {};
1412 this.merged = {};
1413 this.animations = {};
1414 this.interpolations = {};
1415 this.values = {};
1416 this.configs = [];
1417 this.local = 0;
1418 }
1419
1420}
1421
1422/** API
1423 * const props = useSprings(number, [{ ... }, { ... }, ...])
1424 * const [props, set] = useSprings(number, (i, controller) => ({ ... }))
1425 */
1426
1427const useSprings = (length, props) => {
1428 const mounted = useRef(false);
1429 const ctrl = useRef();
1430 const isFn = is.fun(props); // The controller maintains the animation values, starts and stops animations
1431
1432 const _useMemo = useMemo(() => {
1433 // Remove old controllers
1434 if (ctrl.current) {
1435 ctrl.current.map(c => c.destroy());
1436 ctrl.current = undefined;
1437 }
1438
1439 let ref;
1440 return [new Array(length).fill().map((_, i) => {
1441 const ctrl = new Controller();
1442 const newProps = isFn ? callProp(props, i, ctrl) : props[i];
1443 if (i === 0) ref = newProps.ref;
1444 ctrl.update(newProps);
1445 if (!ref) ctrl.start();
1446 return ctrl;
1447 }), ref];
1448 }, [length]),
1449 controllers = _useMemo[0],
1450 ref = _useMemo[1];
1451
1452 ctrl.current = controllers; // The hooks reference api gets defined here ...
1453
1454 const api = useImperativeHandle(ref, () => ({
1455 start: () => Promise.all(ctrl.current.map(c => new Promise(r => c.start(r)))),
1456 stop: finished => ctrl.current.forEach(c => c.stop(finished)),
1457
1458 get controllers() {
1459 return ctrl.current;
1460 }
1461
1462 })); // This function updates the controllers
1463
1464 const updateCtrl = useMemo(() => updateProps => ctrl.current.map((c, i) => {
1465 c.update(isFn ? callProp(updateProps, i, c) : updateProps[i]);
1466 if (!ref) c.start();
1467 }), [length]); // Update controller if props aren't functional
1468
1469 useEffect(() => {
1470 if (mounted.current) {
1471 if (!isFn) updateCtrl(props);
1472 } else if (!ref) ctrl.current.forEach(c => c.start());
1473 }); // Update mounted flag and destroy controller on unmount
1474
1475 useEffect(() => (mounted.current = true, () => ctrl.current.forEach(c => c.destroy())), []); // Return animated props, or, anim-props + the update-setter above
1476
1477 const propValues = ctrl.current.map(c => c.getValues());
1478 return isFn ? [propValues, updateCtrl, finished => ctrl.current.forEach(c => c.pause(finished))] : propValues;
1479};
1480
1481/** API
1482 * const props = useSpring({ ... })
1483 * const [props, set] = useSpring(() => ({ ... }))
1484 */
1485
1486const useSpring = props => {
1487 const isFn = is.fun(props);
1488
1489 const _useSprings = useSprings(1, isFn ? props : [props]),
1490 result = _useSprings[0],
1491 set = _useSprings[1],
1492 pause = _useSprings[2];
1493
1494 return isFn ? [result[0], set, pause] : result;
1495};
1496
1497/** API
1498 * const trails = useTrail(number, { ... })
1499 * const [trails, set] = useTrail(number, () => ({ ... }))
1500 */
1501
1502const useTrail = (length, props) => {
1503 const mounted = useRef(false);
1504 const isFn = is.fun(props);
1505 const updateProps = callProp(props);
1506 const instances = useRef();
1507
1508 const _useSprings = useSprings(length, (i, ctrl) => {
1509 if (i === 0) instances.current = [];
1510 instances.current.push(ctrl);
1511 return _extends({}, updateProps, {
1512 config: callProp(updateProps.config, i),
1513 attach: i > 0 && (() => instances.current[i - 1])
1514 });
1515 }),
1516 result = _useSprings[0],
1517 set = _useSprings[1],
1518 pause = _useSprings[2]; // Set up function to update controller
1519
1520
1521 const updateCtrl = useMemo(() => props => set((i, ctrl) => {
1522 const last = props.reverse ? i === 0 : length - 1 === i;
1523 const attachIdx = props.reverse ? i + 1 : i - 1;
1524 const attachController = instances.current[attachIdx];
1525 return _extends({}, props, {
1526 config: callProp(props.config || updateProps.config, i),
1527 attach: attachController && (() => attachController)
1528 });
1529 }), [length, updateProps.reverse]); // Update controller if props aren't functional
1530
1531 useEffect(() => void (mounted.current && !isFn && updateCtrl(props))); // Update mounted flag and destroy controller on unmount
1532
1533 useEffect(() => void (mounted.current = true), []);
1534 return isFn ? [result, updateCtrl, pause] : result;
1535};
1536
1537/** API
1538 * const transitions = useTransition(items, itemKeys, { ... })
1539 * const [transitions, update] = useTransition(items, itemKeys, () => ({ ... }))
1540 */
1541
1542let guid = 0;
1543const ENTER = 'enter';
1544const LEAVE = 'leave';
1545const UPDATE = 'update';
1546
1547const mapKeys = (items, keys) => (typeof keys === 'function' ? items.map(keys) : toArray(keys)).map(String);
1548
1549const get = props => {
1550 let items = props.items,
1551 _props$keys = props.keys,
1552 keys = _props$keys === void 0 ? item => item : _props$keys,
1553 rest = _objectWithoutPropertiesLoose(props, ["items", "keys"]);
1554
1555 items = toArray(items !== void 0 ? items : null);
1556 return _extends({
1557 items,
1558 keys: mapKeys(items, keys)
1559 }, rest);
1560};
1561
1562function useTransition(input, keyTransform, config) {
1563 const props = _extends({
1564 items: input,
1565 keys: keyTransform || (i => i)
1566 }, config);
1567
1568 const _get = get(props),
1569 _get$lazy = _get.lazy,
1570 lazy = _get$lazy === void 0 ? false : _get$lazy,
1571 _get$unique = _get.unique,
1572 _get$reset = _get.reset,
1573 reset = _get$reset === void 0 ? false : _get$reset,
1574 enter = _get.enter,
1575 leave = _get.leave,
1576 update = _get.update,
1577 onDestroyed = _get.onDestroyed,
1578 keys = _get.keys,
1579 items = _get.items,
1580 onFrame = _get.onFrame,
1581 _onRest = _get.onRest,
1582 onStart = _get.onStart,
1583 ref = _get.ref,
1584 extra = _objectWithoutPropertiesLoose(_get, ["lazy", "unique", "reset", "enter", "leave", "update", "onDestroyed", "keys", "items", "onFrame", "onRest", "onStart", "ref"]);
1585
1586 const forceUpdate = useForceUpdate();
1587 const mounted = useRef(false);
1588 const state = useRef({
1589 mounted: false,
1590 first: true,
1591 deleted: [],
1592 current: {},
1593 transitions: [],
1594 prevProps: {},
1595 paused: !!props.ref,
1596 instances: !mounted.current && new Map(),
1597 forceUpdate
1598 });
1599 useImperativeHandle(props.ref, () => ({
1600 start: () => Promise.all(Array.from(state.current.instances).map((_ref) => {
1601 let c = _ref[1];
1602 return new Promise(r => c.start(r));
1603 })),
1604 stop: finished => Array.from(state.current.instances).forEach((_ref2) => {
1605 let c = _ref2[1];
1606 return c.stop(finished);
1607 }),
1608
1609 get controllers() {
1610 return Array.from(state.current.instances).map((_ref3) => {
1611 let c = _ref3[1];
1612 return c;
1613 });
1614 }
1615
1616 })); // Update state
1617
1618 state.current = diffItems(state.current, props);
1619
1620 if (state.current.changed) {
1621 // Update state
1622 state.current.transitions.forEach(transition => {
1623 const slot = transition.slot,
1624 from = transition.from,
1625 to = transition.to,
1626 config = transition.config,
1627 trail = transition.trail,
1628 key = transition.key,
1629 item = transition.item;
1630 if (!state.current.instances.has(key)) state.current.instances.set(key, new Controller()); // update the map object
1631
1632 const ctrl = state.current.instances.get(key);
1633
1634 const newProps = _extends({}, extra, {
1635 to,
1636 from,
1637 config,
1638 ref,
1639 onRest: values => {
1640 if (state.current.mounted) {
1641 if (transition.destroyed) {
1642 // If no ref is given delete destroyed items immediately
1643 if (!ref && !lazy) cleanUp(state, key);
1644 if (onDestroyed) onDestroyed(item);
1645 } // A transition comes to rest once all its springs conclude
1646
1647
1648 const curInstances = Array.from(state.current.instances);
1649 const active = curInstances.some((_ref4) => {
1650 let c = _ref4[1];
1651 return !c.idle;
1652 });
1653 if (!active && (ref || lazy) && state.current.deleted.length > 0) cleanUp(state);
1654 if (_onRest) _onRest(item, slot, values);
1655 }
1656 },
1657 onStart: onStart && (() => onStart(item, slot)),
1658 onFrame: onFrame && (values => onFrame(item, slot, values)),
1659 delay: trail,
1660 reset: reset && slot === ENTER // Update controller
1661
1662 });
1663
1664 ctrl.update(newProps);
1665 if (!state.current.paused) ctrl.start();
1666 });
1667 }
1668
1669 useEffect(() => {
1670 state.current.mounted = mounted.current = true;
1671 return () => {
1672 state.current.mounted = mounted.current = false;
1673 Array.from(state.current.instances).map((_ref5) => {
1674 let c = _ref5[1];
1675 return c.destroy();
1676 });
1677 state.current.instances.clear();
1678 };
1679 }, []);
1680 return state.current.transitions.map((_ref6) => {
1681 let item = _ref6.item,
1682 slot = _ref6.slot,
1683 key = _ref6.key;
1684 return {
1685 item,
1686 key,
1687 state: slot,
1688 props: state.current.instances.get(key).getValues()
1689 };
1690 });
1691}
1692
1693function cleanUp(state, filterKey) {
1694 const deleted = state.current.deleted;
1695
1696 for (let _ref7 of deleted) {
1697 let key = _ref7.key;
1698
1699 const filter = t => t.key !== key;
1700
1701 if (is.und(filterKey) || filterKey === key) {
1702 state.current.instances.delete(key);
1703 state.current.transitions = state.current.transitions.filter(filter);
1704 state.current.deleted = state.current.deleted.filter(filter);
1705 }
1706 }
1707
1708 state.current.forceUpdate();
1709}
1710
1711function diffItems(_ref8, props) {
1712 let first = _ref8.first,
1713 prevProps = _ref8.prevProps,
1714 state = _objectWithoutPropertiesLoose(_ref8, ["first", "prevProps"]);
1715
1716 let _get2 = get(props),
1717 items = _get2.items,
1718 keys = _get2.keys,
1719 initial = _get2.initial,
1720 from = _get2.from,
1721 enter = _get2.enter,
1722 leave = _get2.leave,
1723 update = _get2.update,
1724 _get2$trail = _get2.trail,
1725 trail = _get2$trail === void 0 ? 0 : _get2$trail,
1726 unique = _get2.unique,
1727 config = _get2.config,
1728 _get2$order = _get2.order,
1729 order = _get2$order === void 0 ? [ENTER, LEAVE, UPDATE] : _get2$order;
1730
1731 let _get3 = get(prevProps),
1732 _keys = _get3.keys,
1733 _items = _get3.items;
1734
1735 let current = _extends({}, state.current);
1736
1737 let deleted = [...state.deleted]; // Compare next keys with current keys
1738
1739 let currentKeys = Object.keys(current);
1740 let currentSet = new Set(currentKeys);
1741 let nextSet = new Set(keys);
1742 let added = keys.filter(item => !currentSet.has(item));
1743 let removed = state.transitions.filter(item => !item.destroyed && !nextSet.has(item.originalKey)).map(i => i.originalKey);
1744 let updated = keys.filter(item => currentSet.has(item));
1745 let delay = -trail;
1746
1747 while (order.length) {
1748 const changeType = order.shift();
1749
1750 switch (changeType) {
1751 case ENTER:
1752 {
1753 added.forEach((key, index) => {
1754 // In unique mode, remove fading out transitions if their key comes in again
1755 if (unique && deleted.find(d => d.originalKey === key)) deleted = deleted.filter(t => t.originalKey !== key);
1756 const keyIndex = keys.indexOf(key);
1757 const item = items[keyIndex];
1758 const slot = first && initial !== void 0 ? 'initial' : ENTER;
1759 current[key] = {
1760 slot,
1761 originalKey: key,
1762 key: unique ? String(key) : guid++,
1763 item,
1764 trail: delay = delay + trail,
1765 config: callProp(config, item, slot),
1766 from: callProp(first ? initial !== void 0 ? initial || {} : from : from, item),
1767 to: callProp(enter, item)
1768 };
1769 });
1770 break;
1771 }
1772
1773 case LEAVE:
1774 {
1775 removed.forEach(key => {
1776 const keyIndex = _keys.indexOf(key);
1777
1778 const item = _items[keyIndex];
1779 const slot = LEAVE;
1780 deleted.unshift(_extends({}, current[key], {
1781 slot,
1782 destroyed: true,
1783 left: _keys[Math.max(0, keyIndex - 1)],
1784 right: _keys[Math.min(_keys.length, keyIndex + 1)],
1785 trail: delay = delay + trail,
1786 config: callProp(config, item, slot),
1787 to: callProp(leave, item)
1788 }));
1789 delete current[key];
1790 });
1791 break;
1792 }
1793
1794 case UPDATE:
1795 {
1796 updated.forEach(key => {
1797 const keyIndex = keys.indexOf(key);
1798 const item = items[keyIndex];
1799 const slot = UPDATE;
1800 current[key] = _extends({}, current[key], {
1801 item,
1802 slot,
1803 trail: delay = delay + trail,
1804 config: callProp(config, item, slot),
1805 to: callProp(update, item)
1806 });
1807 });
1808 break;
1809 }
1810 }
1811 }
1812
1813 let out = keys.map(key => current[key]); // This tries to restore order for deleted items by finding their last known siblings
1814 // only using the left sibling to keep order placement consistent for all deleted items
1815
1816 deleted.forEach((_ref9) => {
1817 let left = _ref9.left,
1818 right = _ref9.right,
1819 item = _objectWithoutPropertiesLoose(_ref9, ["left", "right"]);
1820
1821 let pos; // Was it the element on the left, if yes, move there ...
1822
1823 if ((pos = out.findIndex(t => t.originalKey === left)) !== -1) pos += 1; // And if nothing else helps, move it to the start ¯\_(ツ)_/¯
1824
1825 pos = Math.max(0, pos);
1826 out = [...out.slice(0, pos), item, ...out.slice(pos)];
1827 });
1828 return _extends({}, state, {
1829 changed: added.length || removed.length || updated.length,
1830 first: first && added.length === 0,
1831 transitions: out,
1832 current,
1833 deleted,
1834 prevProps: props
1835 });
1836}
1837
1838const apply = merge(createAnimatedComponent);
1839const extendedAnimated = apply(THREE); // Set default native-element
1840
1841injectDefaultElement('group'); // Use default interpolation (which includes numbers, strings, colors)
1842
1843injectStringInterpolator(createStringInterpolator); // Inject color names, so that it will be able to deal with things like "peachpuff"
1844
1845injectColorNames(colors); // This is how we teach react-spring to set props "natively", the api is (instance, props) => { ... }
1846
1847injectApplyAnimatedValues(applyProps, style => style);
1848
1849export { apply, config, extendedAnimated as animated, interpolate$1 as interpolate, Globals, useSpring, useTrail, useTransition, useChain, useSprings };