framer-motion 10.1.0-alpha.3 → 10.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.js +3224 -367
- package/dist/es/animation/waapi/index.mjs +4 -1
- package/dist/es/gestures/drag/VisualElementDragControls.mjs +7 -2
- package/dist/es/gestures/hover.mjs +2 -1
- package/dist/es/gestures/pan/index.mjs +11 -3
- package/dist/es/gestures/press.mjs +22 -10
- package/dist/es/index.mjs +19 -20
- package/dist/es/render/utils/motion-values.mjs +1 -1
- package/dist/es/value/index.mjs +1 -1
- package/dist/framer-motion.dev.js +386 -362
- package/dist/framer-motion.js +1 -1
- package/dist/index.d.ts +887 -899
- package/dist/projection.dev.js +6 -3
- package/dist/size-rollup-dom-animation-assets.js +1 -0
- package/dist/size-rollup-dom-animation-m.js +1 -0
- package/dist/size-rollup-dom-animation.js +1 -0
- package/dist/size-rollup-dom-max-assets.js +1 -0
- package/dist/size-rollup-dom-max.js +1 -0
- package/dist/size-rollup-m.js +1 -0
- package/dist/size-rollup-motion.js +1 -0
- package/dist/size-webpack-dom-animation.js +1 -0
- package/dist/size-webpack-dom-max.js +1 -0
- package/dist/size-webpack-m.js +1 -0
- package/dist/three-entry.d.ts +376 -384
- package/package.json +13 -32
- package/dist/cjs/dom-entry.js +0 -44
- package/dist/cjs/wrap-e6a894d7.js +0 -2902
- package/dist/dom-entry.d.ts +0 -771
- package/dist/es/dom-entry.mjs +0 -21
package/dist/cjs/index.js
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var React = require('react');
|
|
6
|
-
var wrap = require('./wrap-e6a894d7.js');
|
|
7
6
|
|
|
8
7
|
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
9
8
|
|
|
@@ -459,20 +458,49 @@ function addScaleCorrector(correctors) {
|
|
|
459
458
|
Object.assign(scaleCorrectors, correctors);
|
|
460
459
|
}
|
|
461
460
|
|
|
461
|
+
/**
|
|
462
|
+
* Generate a list of every possible transform key.
|
|
463
|
+
*/
|
|
464
|
+
const transformPropOrder = [
|
|
465
|
+
"transformPerspective",
|
|
466
|
+
"x",
|
|
467
|
+
"y",
|
|
468
|
+
"z",
|
|
469
|
+
"translateX",
|
|
470
|
+
"translateY",
|
|
471
|
+
"translateZ",
|
|
472
|
+
"scale",
|
|
473
|
+
"scaleX",
|
|
474
|
+
"scaleY",
|
|
475
|
+
"rotate",
|
|
476
|
+
"rotateX",
|
|
477
|
+
"rotateY",
|
|
478
|
+
"rotateZ",
|
|
479
|
+
"skew",
|
|
480
|
+
"skewX",
|
|
481
|
+
"skewY",
|
|
482
|
+
];
|
|
483
|
+
/**
|
|
484
|
+
* A quick lookup for transform props.
|
|
485
|
+
*/
|
|
486
|
+
const transformProps = new Set(transformPropOrder);
|
|
487
|
+
|
|
462
488
|
function isForcedMotionValue(key, { layout, layoutId }) {
|
|
463
|
-
return (
|
|
489
|
+
return (transformProps.has(key) ||
|
|
464
490
|
key.startsWith("origin") ||
|
|
465
491
|
((layout || layoutId !== undefined) &&
|
|
466
492
|
(!!scaleCorrectors[key] || key === "opacity")));
|
|
467
493
|
}
|
|
468
494
|
|
|
495
|
+
const isMotionValue = (value) => Boolean(value && value.getVelocity);
|
|
496
|
+
|
|
469
497
|
const translateAlias = {
|
|
470
498
|
x: "translateX",
|
|
471
499
|
y: "translateY",
|
|
472
500
|
z: "translateZ",
|
|
473
501
|
transformPerspective: "perspective",
|
|
474
502
|
};
|
|
475
|
-
const numTransforms =
|
|
503
|
+
const numTransforms = transformPropOrder.length;
|
|
476
504
|
/**
|
|
477
505
|
* Build a CSS transform style from individual x/y/scale etc properties.
|
|
478
506
|
*
|
|
@@ -487,7 +515,7 @@ function buildTransform(transform, { enableHardwareAcceleration = true, allowTra
|
|
|
487
515
|
* are present to the transform string.
|
|
488
516
|
*/
|
|
489
517
|
for (let i = 0; i < numTransforms; i++) {
|
|
490
|
-
const key =
|
|
518
|
+
const key = transformPropOrder[i];
|
|
491
519
|
if (transform[key] !== undefined) {
|
|
492
520
|
const transformName = translateAlias[key] || key;
|
|
493
521
|
transformString += `${transformName}(${transform[key]}) `;
|
|
@@ -521,6 +549,124 @@ const getValueAsType = (value, type) => {
|
|
|
521
549
|
: value;
|
|
522
550
|
};
|
|
523
551
|
|
|
552
|
+
const clamp = (min, max, v) => Math.min(Math.max(v, min), max);
|
|
553
|
+
|
|
554
|
+
const number = {
|
|
555
|
+
test: (v) => typeof v === "number",
|
|
556
|
+
parse: parseFloat,
|
|
557
|
+
transform: (v) => v,
|
|
558
|
+
};
|
|
559
|
+
const alpha = {
|
|
560
|
+
...number,
|
|
561
|
+
transform: (v) => clamp(0, 1, v),
|
|
562
|
+
};
|
|
563
|
+
const scale = {
|
|
564
|
+
...number,
|
|
565
|
+
default: 1,
|
|
566
|
+
};
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* TODO: When we move from string as a source of truth to data models
|
|
570
|
+
* everything in this folder should probably be referred to as models vs types
|
|
571
|
+
*/
|
|
572
|
+
// If this number is a decimal, make it just five decimal places
|
|
573
|
+
// to avoid exponents
|
|
574
|
+
const sanitize = (v) => Math.round(v * 100000) / 100000;
|
|
575
|
+
const floatRegex = /(-)?([\d]*\.?[\d])+/g;
|
|
576
|
+
const colorRegex = /(#[0-9a-f]{3,8}|(rgb|hsl)a?\((-?[\d\.]+%?[,\s]+){2}(-?[\d\.]+%?)\s*[\,\/]?\s*[\d\.]*%?\))/gi;
|
|
577
|
+
const singleColorRegex = /^(#[0-9a-f]{3,8}|(rgb|hsl)a?\((-?[\d\.]+%?[,\s]+){2}(-?[\d\.]+%?)\s*[\,\/]?\s*[\d\.]*%?\))$/i;
|
|
578
|
+
function isString(v) {
|
|
579
|
+
return typeof v === "string";
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
const createUnitType = (unit) => ({
|
|
583
|
+
test: (v) => isString(v) && v.endsWith(unit) && v.split(" ").length === 1,
|
|
584
|
+
parse: parseFloat,
|
|
585
|
+
transform: (v) => `${v}${unit}`,
|
|
586
|
+
});
|
|
587
|
+
const degrees = createUnitType("deg");
|
|
588
|
+
const percent = createUnitType("%");
|
|
589
|
+
const px = createUnitType("px");
|
|
590
|
+
const vh = createUnitType("vh");
|
|
591
|
+
const vw = createUnitType("vw");
|
|
592
|
+
const progressPercentage = {
|
|
593
|
+
...percent,
|
|
594
|
+
parse: (v) => percent.parse(v) / 100,
|
|
595
|
+
transform: (v) => percent.transform(v * 100),
|
|
596
|
+
};
|
|
597
|
+
|
|
598
|
+
const int = {
|
|
599
|
+
...number,
|
|
600
|
+
transform: Math.round,
|
|
601
|
+
};
|
|
602
|
+
|
|
603
|
+
const numberValueTypes = {
|
|
604
|
+
// Border props
|
|
605
|
+
borderWidth: px,
|
|
606
|
+
borderTopWidth: px,
|
|
607
|
+
borderRightWidth: px,
|
|
608
|
+
borderBottomWidth: px,
|
|
609
|
+
borderLeftWidth: px,
|
|
610
|
+
borderRadius: px,
|
|
611
|
+
radius: px,
|
|
612
|
+
borderTopLeftRadius: px,
|
|
613
|
+
borderTopRightRadius: px,
|
|
614
|
+
borderBottomRightRadius: px,
|
|
615
|
+
borderBottomLeftRadius: px,
|
|
616
|
+
// Positioning props
|
|
617
|
+
width: px,
|
|
618
|
+
maxWidth: px,
|
|
619
|
+
height: px,
|
|
620
|
+
maxHeight: px,
|
|
621
|
+
size: px,
|
|
622
|
+
top: px,
|
|
623
|
+
right: px,
|
|
624
|
+
bottom: px,
|
|
625
|
+
left: px,
|
|
626
|
+
// Spacing props
|
|
627
|
+
padding: px,
|
|
628
|
+
paddingTop: px,
|
|
629
|
+
paddingRight: px,
|
|
630
|
+
paddingBottom: px,
|
|
631
|
+
paddingLeft: px,
|
|
632
|
+
margin: px,
|
|
633
|
+
marginTop: px,
|
|
634
|
+
marginRight: px,
|
|
635
|
+
marginBottom: px,
|
|
636
|
+
marginLeft: px,
|
|
637
|
+
// Transform props
|
|
638
|
+
rotate: degrees,
|
|
639
|
+
rotateX: degrees,
|
|
640
|
+
rotateY: degrees,
|
|
641
|
+
rotateZ: degrees,
|
|
642
|
+
scale,
|
|
643
|
+
scaleX: scale,
|
|
644
|
+
scaleY: scale,
|
|
645
|
+
scaleZ: scale,
|
|
646
|
+
skew: degrees,
|
|
647
|
+
skewX: degrees,
|
|
648
|
+
skewY: degrees,
|
|
649
|
+
distance: px,
|
|
650
|
+
translateX: px,
|
|
651
|
+
translateY: px,
|
|
652
|
+
translateZ: px,
|
|
653
|
+
x: px,
|
|
654
|
+
y: px,
|
|
655
|
+
z: px,
|
|
656
|
+
perspective: px,
|
|
657
|
+
transformPerspective: px,
|
|
658
|
+
opacity: alpha,
|
|
659
|
+
originX: progressPercentage,
|
|
660
|
+
originY: progressPercentage,
|
|
661
|
+
originZ: px,
|
|
662
|
+
// Misc
|
|
663
|
+
zIndex: int,
|
|
664
|
+
// SVG
|
|
665
|
+
fillOpacity: alpha,
|
|
666
|
+
strokeOpacity: alpha,
|
|
667
|
+
numOctaves: int,
|
|
668
|
+
};
|
|
669
|
+
|
|
524
670
|
function buildHTMLStyles(state, latestValues, options, transformTemplate) {
|
|
525
671
|
const { style, vars, transform, transformOrigin } = state;
|
|
526
672
|
// Track whether we encounter any transform or transformOrigin values.
|
|
@@ -544,9 +690,9 @@ function buildHTMLStyles(state, latestValues, options, transformTemplate) {
|
|
|
544
690
|
continue;
|
|
545
691
|
}
|
|
546
692
|
// Convert the value to its default value type, ie 0 -> "0px"
|
|
547
|
-
const valueType =
|
|
693
|
+
const valueType = numberValueTypes[key];
|
|
548
694
|
const valueAsType = getValueAsType(value, valueType);
|
|
549
|
-
if (
|
|
695
|
+
if (transformProps.has(key)) {
|
|
550
696
|
// If this is a transform, flag to enable further transform processing
|
|
551
697
|
hasTransform = true;
|
|
552
698
|
transform[key] = valueAsType;
|
|
@@ -597,7 +743,7 @@ const createHtmlRenderState = () => ({
|
|
|
597
743
|
|
|
598
744
|
function copyRawValuesOnly(target, source, props) {
|
|
599
745
|
for (const key in source) {
|
|
600
|
-
if (!
|
|
746
|
+
if (!isMotionValue(source[key]) && !isForcedMotionValue(key, props)) {
|
|
601
747
|
target[key] = source[key];
|
|
602
748
|
}
|
|
603
749
|
}
|
|
@@ -760,7 +906,7 @@ function filterProps(props, isDom, forwardMotionProps) {
|
|
|
760
906
|
function calcOrigin$1(origin, offset, size) {
|
|
761
907
|
return typeof origin === "string"
|
|
762
908
|
? origin
|
|
763
|
-
:
|
|
909
|
+
: px.transform(offset + size * origin);
|
|
764
910
|
}
|
|
765
911
|
/**
|
|
766
912
|
* The SVG transform origin defaults are different to CSS and is less intuitive,
|
|
@@ -794,10 +940,10 @@ function buildSVGPath(attrs, length, spacing = 1, offset = 0, useDashCase = true
|
|
|
794
940
|
// when defining props on a React component.
|
|
795
941
|
const keys = useDashCase ? dashKeys : camelKeys;
|
|
796
942
|
// Build the dash offset
|
|
797
|
-
attrs[keys.offset] =
|
|
943
|
+
attrs[keys.offset] = px.transform(-offset);
|
|
798
944
|
// Build the dash array
|
|
799
|
-
const pathLength =
|
|
800
|
-
const pathSpacing =
|
|
945
|
+
const pathLength = px.transform(length);
|
|
946
|
+
const pathSpacing = px.transform(spacing);
|
|
801
947
|
attrs[keys.array] = `${pathLength} ${pathSpacing}`;
|
|
802
948
|
}
|
|
803
949
|
|
|
@@ -888,7 +1034,7 @@ function createUseRender(forwardMotionProps = false) {
|
|
|
888
1034
|
* will be handled by the onChange handler
|
|
889
1035
|
*/
|
|
890
1036
|
const { children } = props;
|
|
891
|
-
const renderedChildren = React.useMemo(() => (
|
|
1037
|
+
const renderedChildren = React.useMemo(() => (isMotionValue(children) ? children.get() : children), [children]);
|
|
892
1038
|
if (projectionId) {
|
|
893
1039
|
elementProps["data-projection-id"] = projectionId;
|
|
894
1040
|
}
|
|
@@ -953,8 +1099,8 @@ function scrapeMotionValuesFromProps$1(props, prevProps) {
|
|
|
953
1099
|
const { style } = props;
|
|
954
1100
|
const newValues = {};
|
|
955
1101
|
for (const key in style) {
|
|
956
|
-
if (
|
|
957
|
-
(prevProps.style &&
|
|
1102
|
+
if (isMotionValue(style[key]) ||
|
|
1103
|
+
(prevProps.style && isMotionValue(prevProps.style[key])) ||
|
|
958
1104
|
isForcedMotionValue(key, props)) {
|
|
959
1105
|
newValues[key] = style[key];
|
|
960
1106
|
}
|
|
@@ -965,7 +1111,7 @@ function scrapeMotionValuesFromProps$1(props, prevProps) {
|
|
|
965
1111
|
function scrapeMotionValuesFromProps(props, prevProps) {
|
|
966
1112
|
const newValues = scrapeMotionValuesFromProps$1(props, prevProps);
|
|
967
1113
|
for (const key in props) {
|
|
968
|
-
if (
|
|
1114
|
+
if (isMotionValue(props[key]) || isMotionValue(prevProps[key])) {
|
|
969
1115
|
const targetKey = key === "x" || key === "y" ? "attr" + key.toUpperCase() : key;
|
|
970
1116
|
newValues[targetKey] = props[key];
|
|
971
1117
|
}
|
|
@@ -1016,7 +1162,7 @@ const resolveFinalValueInKeyframes = (v) => {
|
|
|
1016
1162
|
* TODO: Remove and move to library
|
|
1017
1163
|
*/
|
|
1018
1164
|
function resolveMotionValue(value) {
|
|
1019
|
-
const unwrappedValue =
|
|
1165
|
+
const unwrappedValue = isMotionValue(value) ? value.get() : value;
|
|
1020
1166
|
return isCustomValue(unwrappedValue)
|
|
1021
1167
|
? unwrappedValue.toValue()
|
|
1022
1168
|
: unwrappedValue;
|
|
@@ -1178,6 +1324,16 @@ function addPointerEvent(target, eventName, handler, options) {
|
|
|
1178
1324
|
return addDomEvent(target, eventName, addPointerInfo(handler), options);
|
|
1179
1325
|
}
|
|
1180
1326
|
|
|
1327
|
+
/**
|
|
1328
|
+
* Pipe
|
|
1329
|
+
* Compose other transformers to run linearily
|
|
1330
|
+
* pipe(min(20), max(40))
|
|
1331
|
+
* @param {...functions} transformers
|
|
1332
|
+
* @return {function}
|
|
1333
|
+
*/
|
|
1334
|
+
const combineFunctions = (a, b) => (v) => b(a(v));
|
|
1335
|
+
const pipe = (...transformers) => transformers.reduce(combineFunctions);
|
|
1336
|
+
|
|
1181
1337
|
function createLock(name) {
|
|
1182
1338
|
let lock = null;
|
|
1183
1339
|
return () => {
|
|
@@ -1238,6 +1394,163 @@ class Feature {
|
|
|
1238
1394
|
update() { }
|
|
1239
1395
|
}
|
|
1240
1396
|
|
|
1397
|
+
/*
|
|
1398
|
+
Detect and load appropriate clock setting for the execution environment
|
|
1399
|
+
*/
|
|
1400
|
+
const defaultTimestep = (1 / 60) * 1000;
|
|
1401
|
+
const getCurrentTime = typeof performance !== "undefined"
|
|
1402
|
+
? () => performance.now()
|
|
1403
|
+
: () => Date.now();
|
|
1404
|
+
const onNextFrame = typeof window !== "undefined"
|
|
1405
|
+
? (callback) => window.requestAnimationFrame(callback)
|
|
1406
|
+
: (callback) => setTimeout(() => callback(getCurrentTime()), defaultTimestep);
|
|
1407
|
+
|
|
1408
|
+
function createRenderStep(runNextFrame) {
|
|
1409
|
+
/**
|
|
1410
|
+
* We create and reuse two arrays, one to queue jobs for the current frame
|
|
1411
|
+
* and one for the next. We reuse to avoid triggering GC after x frames.
|
|
1412
|
+
*/
|
|
1413
|
+
let toRun = [];
|
|
1414
|
+
let toRunNextFrame = [];
|
|
1415
|
+
/**
|
|
1416
|
+
*
|
|
1417
|
+
*/
|
|
1418
|
+
let numToRun = 0;
|
|
1419
|
+
/**
|
|
1420
|
+
* Track whether we're currently processing jobs in this step. This way
|
|
1421
|
+
* we can decide whether to schedule new jobs for this frame or next.
|
|
1422
|
+
*/
|
|
1423
|
+
let isProcessing = false;
|
|
1424
|
+
let flushNextFrame = false;
|
|
1425
|
+
/**
|
|
1426
|
+
* A set of processes which were marked keepAlive when scheduled.
|
|
1427
|
+
*/
|
|
1428
|
+
const toKeepAlive = new WeakSet();
|
|
1429
|
+
const step = {
|
|
1430
|
+
/**
|
|
1431
|
+
* Schedule a process to run on the next frame.
|
|
1432
|
+
*/
|
|
1433
|
+
schedule: (callback, keepAlive = false, immediate = false) => {
|
|
1434
|
+
const addToCurrentFrame = immediate && isProcessing;
|
|
1435
|
+
const buffer = addToCurrentFrame ? toRun : toRunNextFrame;
|
|
1436
|
+
if (keepAlive)
|
|
1437
|
+
toKeepAlive.add(callback);
|
|
1438
|
+
// If the buffer doesn't already contain this callback, add it
|
|
1439
|
+
if (buffer.indexOf(callback) === -1) {
|
|
1440
|
+
buffer.push(callback);
|
|
1441
|
+
// If we're adding it to the currently running buffer, update its measured size
|
|
1442
|
+
if (addToCurrentFrame && isProcessing)
|
|
1443
|
+
numToRun = toRun.length;
|
|
1444
|
+
}
|
|
1445
|
+
return callback;
|
|
1446
|
+
},
|
|
1447
|
+
/**
|
|
1448
|
+
* Cancel the provided callback from running on the next frame.
|
|
1449
|
+
*/
|
|
1450
|
+
cancel: (callback) => {
|
|
1451
|
+
const index = toRunNextFrame.indexOf(callback);
|
|
1452
|
+
if (index !== -1)
|
|
1453
|
+
toRunNextFrame.splice(index, 1);
|
|
1454
|
+
toKeepAlive.delete(callback);
|
|
1455
|
+
},
|
|
1456
|
+
/**
|
|
1457
|
+
* Execute all schedule callbacks.
|
|
1458
|
+
*/
|
|
1459
|
+
process: (frameData) => {
|
|
1460
|
+
/**
|
|
1461
|
+
* If we're already processing we've probably been triggered by a flushSync
|
|
1462
|
+
* inside an existing process. Instead of executing, mark flushNextFrame
|
|
1463
|
+
* as true and ensure we flush the following frame at the end of this one.
|
|
1464
|
+
*/
|
|
1465
|
+
if (isProcessing) {
|
|
1466
|
+
flushNextFrame = true;
|
|
1467
|
+
return;
|
|
1468
|
+
}
|
|
1469
|
+
isProcessing = true;
|
|
1470
|
+
[toRun, toRunNextFrame] = [toRunNextFrame, toRun];
|
|
1471
|
+
// Clear the next frame list
|
|
1472
|
+
toRunNextFrame.length = 0;
|
|
1473
|
+
// Execute this frame
|
|
1474
|
+
numToRun = toRun.length;
|
|
1475
|
+
if (numToRun) {
|
|
1476
|
+
for (let i = 0; i < numToRun; i++) {
|
|
1477
|
+
const callback = toRun[i];
|
|
1478
|
+
callback(frameData);
|
|
1479
|
+
if (toKeepAlive.has(callback)) {
|
|
1480
|
+
step.schedule(callback);
|
|
1481
|
+
runNextFrame();
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
}
|
|
1485
|
+
isProcessing = false;
|
|
1486
|
+
if (flushNextFrame) {
|
|
1487
|
+
flushNextFrame = false;
|
|
1488
|
+
step.process(frameData);
|
|
1489
|
+
}
|
|
1490
|
+
},
|
|
1491
|
+
};
|
|
1492
|
+
return step;
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
const frameData = {
|
|
1496
|
+
delta: 0,
|
|
1497
|
+
timestamp: 0,
|
|
1498
|
+
};
|
|
1499
|
+
|
|
1500
|
+
const maxElapsed$1 = 40;
|
|
1501
|
+
let useDefaultElapsed = true;
|
|
1502
|
+
let runNextFrame = false;
|
|
1503
|
+
let isProcessing = false;
|
|
1504
|
+
const stepsOrder = [
|
|
1505
|
+
"read",
|
|
1506
|
+
"update",
|
|
1507
|
+
"preRender",
|
|
1508
|
+
"render",
|
|
1509
|
+
"postRender",
|
|
1510
|
+
];
|
|
1511
|
+
const steps = stepsOrder.reduce((acc, key) => {
|
|
1512
|
+
acc[key] = createRenderStep(() => (runNextFrame = true));
|
|
1513
|
+
return acc;
|
|
1514
|
+
}, {});
|
|
1515
|
+
const sync = stepsOrder.reduce((acc, key) => {
|
|
1516
|
+
const step = steps[key];
|
|
1517
|
+
acc[key] = (process, keepAlive = false, immediate = false) => {
|
|
1518
|
+
if (!runNextFrame)
|
|
1519
|
+
startLoop();
|
|
1520
|
+
return step.schedule(process, keepAlive, immediate);
|
|
1521
|
+
};
|
|
1522
|
+
return acc;
|
|
1523
|
+
}, {});
|
|
1524
|
+
const cancelSync = stepsOrder.reduce((acc, key) => {
|
|
1525
|
+
acc[key] = steps[key].cancel;
|
|
1526
|
+
return acc;
|
|
1527
|
+
}, {});
|
|
1528
|
+
const flushSync = stepsOrder.reduce((acc, key) => {
|
|
1529
|
+
acc[key] = () => steps[key].process(frameData);
|
|
1530
|
+
return acc;
|
|
1531
|
+
}, {});
|
|
1532
|
+
const processStep = (stepId) => steps[stepId].process(frameData);
|
|
1533
|
+
const processFrame = (timestamp) => {
|
|
1534
|
+
runNextFrame = false;
|
|
1535
|
+
frameData.delta = useDefaultElapsed
|
|
1536
|
+
? defaultTimestep
|
|
1537
|
+
: Math.max(Math.min(timestamp - frameData.timestamp, maxElapsed$1), 1);
|
|
1538
|
+
frameData.timestamp = timestamp;
|
|
1539
|
+
isProcessing = true;
|
|
1540
|
+
stepsOrder.forEach(processStep);
|
|
1541
|
+
isProcessing = false;
|
|
1542
|
+
if (runNextFrame) {
|
|
1543
|
+
useDefaultElapsed = false;
|
|
1544
|
+
onNextFrame(processFrame);
|
|
1545
|
+
}
|
|
1546
|
+
};
|
|
1547
|
+
const startLoop = () => {
|
|
1548
|
+
runNextFrame = true;
|
|
1549
|
+
useDefaultElapsed = true;
|
|
1550
|
+
if (!isProcessing)
|
|
1551
|
+
onNextFrame(processFrame);
|
|
1552
|
+
};
|
|
1553
|
+
|
|
1241
1554
|
function addHoverEvent(node, isActive) {
|
|
1242
1555
|
const eventName = "pointer" + (isActive ? "enter" : "leave");
|
|
1243
1556
|
const callbackName = "onHover" + (isActive ? "Start" : "End");
|
|
@@ -1249,7 +1562,7 @@ function addHoverEvent(node, isActive) {
|
|
|
1249
1562
|
node.animationState.setActive("whileHover", isActive);
|
|
1250
1563
|
}
|
|
1251
1564
|
if (props[callbackName]) {
|
|
1252
|
-
props[callbackName](event, info);
|
|
1565
|
+
sync.update(() => props[callbackName](event, info));
|
|
1253
1566
|
}
|
|
1254
1567
|
};
|
|
1255
1568
|
return addPointerEvent(node.current, eventName, handleEvent, {
|
|
@@ -1258,7 +1571,7 @@ function addHoverEvent(node, isActive) {
|
|
|
1258
1571
|
}
|
|
1259
1572
|
class HoverGesture extends Feature {
|
|
1260
1573
|
mount() {
|
|
1261
|
-
this.unmount =
|
|
1574
|
+
this.unmount = pipe(addHoverEvent(this.node, true), addHoverEvent(this.node, false));
|
|
1262
1575
|
}
|
|
1263
1576
|
unmount() { }
|
|
1264
1577
|
}
|
|
@@ -1294,7 +1607,7 @@ class FocusGesture extends Feature {
|
|
|
1294
1607
|
this.isActive = false;
|
|
1295
1608
|
}
|
|
1296
1609
|
mount() {
|
|
1297
|
-
this.unmount =
|
|
1610
|
+
this.unmount = pipe(addDomEvent(this.node.current, "focus", () => this.onFocus()), addDomEvent(this.node.current, "blur", () => this.onBlur()));
|
|
1298
1611
|
}
|
|
1299
1612
|
unmount() { }
|
|
1300
1613
|
}
|
|
@@ -1318,6 +1631,8 @@ const isNodeOrChild = (parent, child) => {
|
|
|
1318
1631
|
}
|
|
1319
1632
|
};
|
|
1320
1633
|
|
|
1634
|
+
const noop = (any) => any;
|
|
1635
|
+
|
|
1321
1636
|
function fireSyntheticPointerEvent(name, handler) {
|
|
1322
1637
|
if (!handler)
|
|
1323
1638
|
return;
|
|
@@ -1327,9 +1642,9 @@ function fireSyntheticPointerEvent(name, handler) {
|
|
|
1327
1642
|
class PressGesture extends Feature {
|
|
1328
1643
|
constructor() {
|
|
1329
1644
|
super(...arguments);
|
|
1330
|
-
this.removeStartListeners =
|
|
1331
|
-
this.removeEndListeners =
|
|
1332
|
-
this.removeAccessibleListeners =
|
|
1645
|
+
this.removeStartListeners = noop;
|
|
1646
|
+
this.removeEndListeners = noop;
|
|
1647
|
+
this.removeAccessibleListeners = noop;
|
|
1333
1648
|
this.startPointerPress = (startEvent, startInfo) => {
|
|
1334
1649
|
this.removeEndListeners();
|
|
1335
1650
|
if (this.isPressing)
|
|
@@ -1339,17 +1654,19 @@ class PressGesture extends Feature {
|
|
|
1339
1654
|
if (!this.checkPressEnd())
|
|
1340
1655
|
return;
|
|
1341
1656
|
const { onTap, onTapCancel } = this.node.getProps();
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1657
|
+
sync.update(() => {
|
|
1658
|
+
/**
|
|
1659
|
+
* We only count this as a tap gesture if the event.target is the same
|
|
1660
|
+
* as, or a child of, this component's element
|
|
1661
|
+
*/
|
|
1662
|
+
!isNodeOrChild(this.node.current, endEvent.target)
|
|
1663
|
+
? onTapCancel && onTapCancel(endEvent, endInfo)
|
|
1664
|
+
: onTap && onTap(endEvent, endInfo);
|
|
1665
|
+
});
|
|
1349
1666
|
};
|
|
1350
1667
|
const removePointerUpListener = addPointerEvent(window, "pointerup", endPointerPress, { passive: !(props.onTap || props["onPointerUp"]) });
|
|
1351
1668
|
const removePointerCancelListener = addPointerEvent(window, "pointercancel", (cancelEvent, cancelInfo) => this.cancelPress(cancelEvent, cancelInfo), { passive: !(props.onTapCancel || props["onPointerCancel"]) });
|
|
1352
|
-
this.removeEndListeners =
|
|
1669
|
+
this.removeEndListeners = pipe(removePointerUpListener, removePointerCancelListener);
|
|
1353
1670
|
this.startPress(startEvent, startInfo);
|
|
1354
1671
|
};
|
|
1355
1672
|
this.startAccessiblePress = () => {
|
|
@@ -1359,7 +1676,12 @@ class PressGesture extends Feature {
|
|
|
1359
1676
|
const handleKeyup = (keyupEvent) => {
|
|
1360
1677
|
if (keyupEvent.key !== "Enter" || !this.checkPressEnd())
|
|
1361
1678
|
return;
|
|
1362
|
-
fireSyntheticPointerEvent("up",
|
|
1679
|
+
fireSyntheticPointerEvent("up", (event, info) => {
|
|
1680
|
+
const { onTap } = this.node.getProps();
|
|
1681
|
+
if (onTap) {
|
|
1682
|
+
sync.update(() => onTap(event, info));
|
|
1683
|
+
}
|
|
1684
|
+
});
|
|
1363
1685
|
};
|
|
1364
1686
|
this.removeEndListeners();
|
|
1365
1687
|
this.removeEndListeners = addDomEvent(this.node.current, "keyup", handleKeyup);
|
|
@@ -1374,7 +1696,7 @@ class PressGesture extends Feature {
|
|
|
1374
1696
|
fireSyntheticPointerEvent("cancel", (cancelEvent, cancelInfo) => this.cancelPress(cancelEvent, cancelInfo));
|
|
1375
1697
|
};
|
|
1376
1698
|
const removeBlurListener = addDomEvent(this.node.current, "blur", handleBlur);
|
|
1377
|
-
this.removeAccessibleListeners =
|
|
1699
|
+
this.removeAccessibleListeners = pipe(removeKeydownListener, removeBlurListener);
|
|
1378
1700
|
};
|
|
1379
1701
|
}
|
|
1380
1702
|
startPress(event, info) {
|
|
@@ -1386,7 +1708,9 @@ class PressGesture extends Feature {
|
|
|
1386
1708
|
if (whileTap && this.node.animationState) {
|
|
1387
1709
|
this.node.animationState.setActive("whileTap", true);
|
|
1388
1710
|
}
|
|
1389
|
-
|
|
1711
|
+
if (onTapStart) {
|
|
1712
|
+
sync.update(() => onTapStart(event, info));
|
|
1713
|
+
}
|
|
1390
1714
|
}
|
|
1391
1715
|
checkPressEnd() {
|
|
1392
1716
|
this.removeEndListeners();
|
|
@@ -1401,13 +1725,15 @@ class PressGesture extends Feature {
|
|
|
1401
1725
|
if (!this.checkPressEnd())
|
|
1402
1726
|
return;
|
|
1403
1727
|
const { onTapCancel } = this.node.getProps();
|
|
1404
|
-
|
|
1728
|
+
if (onTapCancel) {
|
|
1729
|
+
sync.update(() => onTapCancel(event, info));
|
|
1730
|
+
}
|
|
1405
1731
|
}
|
|
1406
1732
|
mount() {
|
|
1407
1733
|
const props = this.node.getProps();
|
|
1408
1734
|
const removePointerListener = addPointerEvent(this.node.current, "pointerdown", this.startPointerPress, { passive: !(props.onTapStart || props["onPointerStart"]) });
|
|
1409
1735
|
const removeFocusListener = addDomEvent(this.node.current, "focus", this.startAccessiblePress);
|
|
1410
|
-
this.removeStartListeners =
|
|
1736
|
+
this.removeStartListeners = pipe(removePointerListener, removeFocusListener);
|
|
1411
1737
|
}
|
|
1412
1738
|
unmount() {
|
|
1413
1739
|
this.removeStartListeners();
|
|
@@ -1570,178 +1896,2165 @@ const isNumericalString = (v) => /^\-?\d*\.?\d+$/.test(v);
|
|
|
1570
1896
|
*/
|
|
1571
1897
|
const isZeroValueString = (v) => /^0[^.\s]+$/.test(v);
|
|
1572
1898
|
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1899
|
+
function addUniqueItem(arr, item) {
|
|
1900
|
+
if (arr.indexOf(item) === -1)
|
|
1901
|
+
arr.push(item);
|
|
1902
|
+
}
|
|
1903
|
+
function removeItem(arr, item) {
|
|
1904
|
+
const index = arr.indexOf(item);
|
|
1905
|
+
if (index > -1)
|
|
1906
|
+
arr.splice(index, 1);
|
|
1907
|
+
}
|
|
1908
|
+
// Adapted from array-move
|
|
1909
|
+
function moveItem([...arr], fromIndex, toIndex) {
|
|
1910
|
+
const startIndex = fromIndex < 0 ? arr.length + fromIndex : fromIndex;
|
|
1911
|
+
if (startIndex >= 0 && startIndex < arr.length) {
|
|
1912
|
+
const endIndex = toIndex < 0 ? arr.length + toIndex : toIndex;
|
|
1913
|
+
const [item] = arr.splice(fromIndex, 1);
|
|
1914
|
+
arr.splice(endIndex, 0, item);
|
|
1915
|
+
}
|
|
1916
|
+
return arr;
|
|
1917
|
+
}
|
|
1585
1918
|
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1919
|
+
class SubscriptionManager {
|
|
1920
|
+
constructor() {
|
|
1921
|
+
this.subscriptions = [];
|
|
1922
|
+
}
|
|
1923
|
+
add(handler) {
|
|
1924
|
+
addUniqueItem(this.subscriptions, handler);
|
|
1925
|
+
return () => removeItem(this.subscriptions, handler);
|
|
1926
|
+
}
|
|
1927
|
+
notify(a, b, c) {
|
|
1928
|
+
const numSubscriptions = this.subscriptions.length;
|
|
1929
|
+
if (!numSubscriptions)
|
|
1930
|
+
return;
|
|
1931
|
+
if (numSubscriptions === 1) {
|
|
1932
|
+
/**
|
|
1933
|
+
* If there's only a single handler we can just call it without invoking a loop.
|
|
1934
|
+
*/
|
|
1935
|
+
this.subscriptions[0](a, b, c);
|
|
1936
|
+
}
|
|
1937
|
+
else {
|
|
1938
|
+
for (let i = 0; i < numSubscriptions; i++) {
|
|
1939
|
+
/**
|
|
1940
|
+
* Check whether the handler exists before firing as it's possible
|
|
1941
|
+
* the subscriptions were modified during this loop running.
|
|
1942
|
+
*/
|
|
1943
|
+
const handler = this.subscriptions[i];
|
|
1944
|
+
handler && handler(a, b, c);
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
getSize() {
|
|
1949
|
+
return this.subscriptions.length;
|
|
1950
|
+
}
|
|
1951
|
+
clear() {
|
|
1952
|
+
this.subscriptions.length = 0;
|
|
1953
|
+
}
|
|
1954
|
+
}
|
|
1594
1955
|
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
*/
|
|
1598
|
-
const valueTypes = [...dimensionValueTypes, wrap.color, wrap.complex];
|
|
1599
|
-
/**
|
|
1600
|
-
* Tests a value against the list of ValueTypes
|
|
1601
|
-
*/
|
|
1602
|
-
const findValueType = (v) => valueTypes.find(testValueType(v));
|
|
1956
|
+
/*
|
|
1957
|
+
Convert velocity into velocity per second
|
|
1603
1958
|
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
function
|
|
1608
|
-
|
|
1609
|
-
visualElement.values.forEach((value, key) => (current[key] = value.get()));
|
|
1610
|
-
return current;
|
|
1611
|
-
}
|
|
1612
|
-
/**
|
|
1613
|
-
* Creates an object containing the latest velocity of every MotionValue on a VisualElement
|
|
1614
|
-
*/
|
|
1615
|
-
function getVelocity$1(visualElement) {
|
|
1616
|
-
const velocity = {};
|
|
1617
|
-
visualElement.values.forEach((value, key) => (velocity[key] = value.getVelocity()));
|
|
1618
|
-
return velocity;
|
|
1959
|
+
@param [number]: Unit per frame
|
|
1960
|
+
@param [number]: Frame duration in ms
|
|
1961
|
+
*/
|
|
1962
|
+
function velocityPerSecond(velocity, frameDuration) {
|
|
1963
|
+
return frameDuration ? velocity * (1000 / frameDuration) : 0;
|
|
1619
1964
|
}
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1965
|
+
|
|
1966
|
+
const warned = new Set();
|
|
1967
|
+
function warnOnce(condition, message, element) {
|
|
1968
|
+
if (condition || warned.has(message))
|
|
1969
|
+
return;
|
|
1970
|
+
console.warn(message);
|
|
1971
|
+
if (element)
|
|
1972
|
+
console.warn(element);
|
|
1973
|
+
warned.add(message);
|
|
1623
1974
|
}
|
|
1624
1975
|
|
|
1976
|
+
const isFloat = (value) => {
|
|
1977
|
+
return !isNaN(parseFloat(value));
|
|
1978
|
+
};
|
|
1625
1979
|
/**
|
|
1626
|
-
*
|
|
1627
|
-
*
|
|
1980
|
+
* `MotionValue` is used to track the state and velocity of motion values.
|
|
1981
|
+
*
|
|
1982
|
+
* @public
|
|
1628
1983
|
*/
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1984
|
+
class MotionValue {
|
|
1985
|
+
/**
|
|
1986
|
+
* @param init - The initiating value
|
|
1987
|
+
* @param config - Optional configuration options
|
|
1988
|
+
*
|
|
1989
|
+
* - `transformer`: A function to transform incoming values with.
|
|
1990
|
+
*
|
|
1991
|
+
* @internal
|
|
1992
|
+
*/
|
|
1993
|
+
constructor(init, options = {}) {
|
|
1994
|
+
/**
|
|
1995
|
+
* This will be replaced by the build step with the latest version number.
|
|
1996
|
+
* When MotionValues are provided to motion components, warn if versions are mixed.
|
|
1997
|
+
*/
|
|
1998
|
+
this.version = "10.1.0";
|
|
1999
|
+
/**
|
|
2000
|
+
* Duration, in milliseconds, since last updating frame.
|
|
2001
|
+
*
|
|
2002
|
+
* @internal
|
|
2003
|
+
*/
|
|
2004
|
+
this.timeDelta = 0;
|
|
2005
|
+
/**
|
|
2006
|
+
* Timestamp of the last time this `MotionValue` was updated.
|
|
2007
|
+
*
|
|
2008
|
+
* @internal
|
|
2009
|
+
*/
|
|
2010
|
+
this.lastUpdated = 0;
|
|
2011
|
+
/**
|
|
2012
|
+
* Tracks whether this value can output a velocity. Currently this is only true
|
|
2013
|
+
* if the value is numerical, but we might be able to widen the scope here and support
|
|
2014
|
+
* other value types.
|
|
2015
|
+
*
|
|
2016
|
+
* @internal
|
|
2017
|
+
*/
|
|
2018
|
+
this.canTrackVelocity = false;
|
|
2019
|
+
/**
|
|
2020
|
+
* An object containing a SubscriptionManager for each active event.
|
|
2021
|
+
*/
|
|
2022
|
+
this.events = {};
|
|
2023
|
+
this.updateAndNotify = (v, render = true) => {
|
|
2024
|
+
this.prev = this.current;
|
|
2025
|
+
this.current = v;
|
|
2026
|
+
// Update timestamp
|
|
2027
|
+
const { delta, timestamp } = frameData;
|
|
2028
|
+
if (this.lastUpdated !== timestamp) {
|
|
2029
|
+
this.timeDelta = delta;
|
|
2030
|
+
this.lastUpdated = timestamp;
|
|
2031
|
+
sync.postRender(this.scheduleVelocityCheck);
|
|
2032
|
+
}
|
|
2033
|
+
// Update update subscribers
|
|
2034
|
+
if (this.prev !== this.current && this.events.change) {
|
|
2035
|
+
this.events.change.notify(this.current);
|
|
2036
|
+
}
|
|
2037
|
+
// Update velocity subscribers
|
|
2038
|
+
if (this.events.velocityChange) {
|
|
2039
|
+
this.events.velocityChange.notify(this.getVelocity());
|
|
2040
|
+
}
|
|
2041
|
+
// Update render subscribers
|
|
2042
|
+
if (render && this.events.renderRequest) {
|
|
2043
|
+
this.events.renderRequest.notify(this.current);
|
|
2044
|
+
}
|
|
2045
|
+
};
|
|
2046
|
+
/**
|
|
2047
|
+
* Schedule a velocity check for the next frame.
|
|
2048
|
+
*
|
|
2049
|
+
* This is an instanced and bound function to prevent generating a new
|
|
2050
|
+
* function once per frame.
|
|
2051
|
+
*
|
|
2052
|
+
* @internal
|
|
2053
|
+
*/
|
|
2054
|
+
this.scheduleVelocityCheck = () => sync.postRender(this.velocityCheck);
|
|
2055
|
+
/**
|
|
2056
|
+
* Updates `prev` with `current` if the value hasn't been updated this frame.
|
|
2057
|
+
* This ensures velocity calculations return `0`.
|
|
2058
|
+
*
|
|
2059
|
+
* This is an instanced and bound function to prevent generating a new
|
|
2060
|
+
* function once per frame.
|
|
2061
|
+
*
|
|
2062
|
+
* @internal
|
|
2063
|
+
*/
|
|
2064
|
+
this.velocityCheck = ({ timestamp }) => {
|
|
2065
|
+
if (timestamp !== this.lastUpdated) {
|
|
2066
|
+
this.prev = this.current;
|
|
2067
|
+
if (this.events.velocityChange) {
|
|
2068
|
+
this.events.velocityChange.notify(this.getVelocity());
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
};
|
|
2072
|
+
this.hasAnimated = false;
|
|
2073
|
+
this.prev = this.current = init;
|
|
2074
|
+
this.canTrackVelocity = isFloat(this.current);
|
|
2075
|
+
this.owner = options.owner;
|
|
1635
2076
|
}
|
|
1636
|
-
|
|
1637
|
-
function
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
2077
|
+
/**
|
|
2078
|
+
* Adds a function that will be notified when the `MotionValue` is updated.
|
|
2079
|
+
*
|
|
2080
|
+
* It returns a function that, when called, will cancel the subscription.
|
|
2081
|
+
*
|
|
2082
|
+
* When calling `onChange` inside a React component, it should be wrapped with the
|
|
2083
|
+
* `useEffect` hook. As it returns an unsubscribe function, this should be returned
|
|
2084
|
+
* from the `useEffect` function to ensure you don't add duplicate subscribers..
|
|
2085
|
+
*
|
|
2086
|
+
* ```jsx
|
|
2087
|
+
* export const MyComponent = () => {
|
|
2088
|
+
* const x = useMotionValue(0)
|
|
2089
|
+
* const y = useMotionValue(0)
|
|
2090
|
+
* const opacity = useMotionValue(1)
|
|
2091
|
+
*
|
|
2092
|
+
* useEffect(() => {
|
|
2093
|
+
* function updateOpacity() {
|
|
2094
|
+
* const maxXY = Math.max(x.get(), y.get())
|
|
2095
|
+
* const newOpacity = transform(maxXY, [0, 100], [1, 0])
|
|
2096
|
+
* opacity.set(newOpacity)
|
|
2097
|
+
* }
|
|
2098
|
+
*
|
|
2099
|
+
* const unsubscribeX = x.on("change", updateOpacity)
|
|
2100
|
+
* const unsubscribeY = y.on("change", updateOpacity)
|
|
2101
|
+
*
|
|
2102
|
+
* return () => {
|
|
2103
|
+
* unsubscribeX()
|
|
2104
|
+
* unsubscribeY()
|
|
2105
|
+
* }
|
|
2106
|
+
* }, [])
|
|
2107
|
+
*
|
|
2108
|
+
* return <motion.div style={{ x }} />
|
|
2109
|
+
* }
|
|
2110
|
+
* ```
|
|
2111
|
+
*
|
|
2112
|
+
* @param subscriber - A function that receives the latest value.
|
|
2113
|
+
* @returns A function that, when called, will cancel this subscription.
|
|
2114
|
+
*
|
|
2115
|
+
* @deprecated
|
|
2116
|
+
*/
|
|
2117
|
+
onChange(subscription) {
|
|
2118
|
+
if (process.env.NODE_ENV !== "production") {
|
|
2119
|
+
warnOnce(false, `value.onChange(callback) is deprecated. Switch to value.on("change", callback).`);
|
|
2120
|
+
}
|
|
2121
|
+
return this.on("change", subscription);
|
|
1644
2122
|
}
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
reversedLabels.forEach((key) => {
|
|
1649
|
-
const variant = visualElement.getVariant(key);
|
|
1650
|
-
variant && setTarget(visualElement, variant);
|
|
1651
|
-
if (visualElement.variantChildren) {
|
|
1652
|
-
visualElement.variantChildren.forEach((child) => {
|
|
1653
|
-
setVariants(child, variantLabels);
|
|
1654
|
-
});
|
|
2123
|
+
on(eventName, callback) {
|
|
2124
|
+
if (!this.events[eventName]) {
|
|
2125
|
+
this.events[eventName] = new SubscriptionManager();
|
|
1655
2126
|
}
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
2127
|
+
const unsubscribe = this.events[eventName].add(callback);
|
|
2128
|
+
if (eventName === "change") {
|
|
2129
|
+
return () => {
|
|
2130
|
+
unsubscribe();
|
|
2131
|
+
/**
|
|
2132
|
+
* If we have no more change listeners by the start
|
|
2133
|
+
* of the next frame, stop active animations.
|
|
2134
|
+
*/
|
|
2135
|
+
sync.read(() => {
|
|
2136
|
+
if (!this.events.change.getSize()) {
|
|
2137
|
+
this.stop();
|
|
2138
|
+
}
|
|
2139
|
+
});
|
|
2140
|
+
};
|
|
2141
|
+
}
|
|
2142
|
+
return unsubscribe;
|
|
1661
2143
|
}
|
|
1662
|
-
|
|
1663
|
-
|
|
2144
|
+
clearListeners() {
|
|
2145
|
+
for (const eventManagers in this.events) {
|
|
2146
|
+
this.events[eventManagers].clear();
|
|
2147
|
+
}
|
|
1664
2148
|
}
|
|
1665
|
-
|
|
1666
|
-
|
|
2149
|
+
/**
|
|
2150
|
+
* Attaches a passive effect to the `MotionValue`.
|
|
2151
|
+
*
|
|
2152
|
+
* @internal
|
|
2153
|
+
*/
|
|
2154
|
+
attach(passiveEffect, stopPassiveEffect) {
|
|
2155
|
+
this.passiveEffect = passiveEffect;
|
|
2156
|
+
this.stopPassiveEffect = stopPassiveEffect;
|
|
1667
2157
|
}
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
2158
|
+
/**
|
|
2159
|
+
* Sets the state of the `MotionValue`.
|
|
2160
|
+
*
|
|
2161
|
+
* @remarks
|
|
2162
|
+
*
|
|
2163
|
+
* ```jsx
|
|
2164
|
+
* const x = useMotionValue(0)
|
|
2165
|
+
* x.set(10)
|
|
2166
|
+
* ```
|
|
2167
|
+
*
|
|
2168
|
+
* @param latest - Latest value to set.
|
|
2169
|
+
* @param render - Whether to notify render subscribers. Defaults to `true`
|
|
2170
|
+
*
|
|
2171
|
+
* @public
|
|
2172
|
+
*/
|
|
2173
|
+
set(v, render = true) {
|
|
2174
|
+
if (!render || !this.passiveEffect) {
|
|
2175
|
+
this.updateAndNotify(v, render);
|
|
2176
|
+
}
|
|
2177
|
+
else {
|
|
2178
|
+
this.passiveEffect(v, this.updateAndNotify);
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
setWithVelocity(prev, current, delta) {
|
|
2182
|
+
this.set(current);
|
|
2183
|
+
this.prev = prev;
|
|
2184
|
+
this.timeDelta = delta;
|
|
2185
|
+
}
|
|
2186
|
+
/**
|
|
2187
|
+
* Set the state of the `MotionValue`, stopping any active animations,
|
|
2188
|
+
* effects, and resets velocity to `0`.
|
|
2189
|
+
*/
|
|
2190
|
+
jump(v) {
|
|
2191
|
+
this.updateAndNotify(v);
|
|
2192
|
+
this.prev = v;
|
|
2193
|
+
this.stop();
|
|
2194
|
+
if (this.stopPassiveEffect)
|
|
2195
|
+
this.stopPassiveEffect();
|
|
2196
|
+
}
|
|
2197
|
+
/**
|
|
2198
|
+
* Returns the latest state of `MotionValue`
|
|
2199
|
+
*
|
|
2200
|
+
* @returns - The latest state of `MotionValue`
|
|
2201
|
+
*
|
|
2202
|
+
* @public
|
|
2203
|
+
*/
|
|
2204
|
+
get() {
|
|
2205
|
+
return this.current;
|
|
2206
|
+
}
|
|
2207
|
+
/**
|
|
2208
|
+
* @public
|
|
2209
|
+
*/
|
|
2210
|
+
getPrevious() {
|
|
2211
|
+
return this.prev;
|
|
2212
|
+
}
|
|
2213
|
+
/**
|
|
2214
|
+
* Returns the latest velocity of `MotionValue`
|
|
2215
|
+
*
|
|
2216
|
+
* @returns - The latest velocity of `MotionValue`. Returns `0` if the state is non-numerical.
|
|
2217
|
+
*
|
|
2218
|
+
* @public
|
|
2219
|
+
*/
|
|
2220
|
+
getVelocity() {
|
|
2221
|
+
// This could be isFloat(this.prev) && isFloat(this.current), but that would be wasteful
|
|
2222
|
+
return this.canTrackVelocity
|
|
2223
|
+
? // These casts could be avoided if parseFloat would be typed better
|
|
2224
|
+
velocityPerSecond(parseFloat(this.current) -
|
|
2225
|
+
parseFloat(this.prev), this.timeDelta)
|
|
2226
|
+
: 0;
|
|
2227
|
+
}
|
|
2228
|
+
/**
|
|
2229
|
+
* Registers a new animation to control this `MotionValue`. Only one
|
|
2230
|
+
* animation can drive a `MotionValue` at one time.
|
|
2231
|
+
*
|
|
2232
|
+
* ```jsx
|
|
2233
|
+
* value.start()
|
|
2234
|
+
* ```
|
|
2235
|
+
*
|
|
2236
|
+
* @param animation - A function that starts the provided animation
|
|
2237
|
+
*
|
|
2238
|
+
* @internal
|
|
2239
|
+
*/
|
|
2240
|
+
start(startAnimation) {
|
|
2241
|
+
this.stop();
|
|
2242
|
+
return new Promise((resolve) => {
|
|
2243
|
+
this.hasAnimated = true;
|
|
2244
|
+
this.animation = startAnimation(resolve) || null;
|
|
2245
|
+
if (this.events.animationStart) {
|
|
2246
|
+
this.events.animationStart.notify();
|
|
2247
|
+
}
|
|
2248
|
+
}).then(() => {
|
|
2249
|
+
if (this.events.animationComplete) {
|
|
2250
|
+
this.events.animationComplete.notify();
|
|
2251
|
+
}
|
|
2252
|
+
this.clearAnimation();
|
|
2253
|
+
});
|
|
2254
|
+
}
|
|
2255
|
+
/**
|
|
2256
|
+
* Stop the currently active animation.
|
|
2257
|
+
*
|
|
2258
|
+
* @public
|
|
2259
|
+
*/
|
|
2260
|
+
stop() {
|
|
2261
|
+
if (this.animation) {
|
|
2262
|
+
this.animation.stop();
|
|
2263
|
+
if (this.events.animationCancel) {
|
|
2264
|
+
this.events.animationCancel.notify();
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
this.clearAnimation();
|
|
2268
|
+
}
|
|
2269
|
+
/**
|
|
2270
|
+
* Returns `true` if this value is currently animating.
|
|
2271
|
+
*
|
|
2272
|
+
* @public
|
|
2273
|
+
*/
|
|
2274
|
+
isAnimating() {
|
|
2275
|
+
return !!this.animation;
|
|
2276
|
+
}
|
|
2277
|
+
clearAnimation() {
|
|
2278
|
+
this.animation = null;
|
|
2279
|
+
}
|
|
2280
|
+
/**
|
|
2281
|
+
* Destroy and clean up subscribers to this `MotionValue`.
|
|
2282
|
+
*
|
|
2283
|
+
* The `MotionValue` hooks like `useMotionValue` and `useTransform` automatically
|
|
2284
|
+
* handle the lifecycle of the returned `MotionValue`, so this method is only necessary if you've manually
|
|
2285
|
+
* created a `MotionValue` via the `motionValue` function.
|
|
2286
|
+
*
|
|
2287
|
+
* @public
|
|
2288
|
+
*/
|
|
2289
|
+
destroy() {
|
|
2290
|
+
this.clearListeners();
|
|
2291
|
+
this.stop();
|
|
2292
|
+
if (this.stopPassiveEffect) {
|
|
2293
|
+
this.stopPassiveEffect();
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
}
|
|
2297
|
+
function motionValue(init, options) {
|
|
2298
|
+
return new MotionValue(init, options);
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
/**
|
|
2302
|
+
* Returns true if the provided string is a color, ie rgba(0,0,0,0) or #000,
|
|
2303
|
+
* but false if a number or multiple colors
|
|
2304
|
+
*/
|
|
2305
|
+
const isColorString = (type, testProp) => (v) => {
|
|
2306
|
+
return Boolean((isString(v) && singleColorRegex.test(v) && v.startsWith(type)) ||
|
|
2307
|
+
(testProp && Object.prototype.hasOwnProperty.call(v, testProp)));
|
|
2308
|
+
};
|
|
2309
|
+
const splitColor = (aName, bName, cName) => (v) => {
|
|
2310
|
+
if (!isString(v))
|
|
2311
|
+
return v;
|
|
2312
|
+
const [a, b, c, alpha] = v.match(floatRegex);
|
|
2313
|
+
return {
|
|
2314
|
+
[aName]: parseFloat(a),
|
|
2315
|
+
[bName]: parseFloat(b),
|
|
2316
|
+
[cName]: parseFloat(c),
|
|
2317
|
+
alpha: alpha !== undefined ? parseFloat(alpha) : 1,
|
|
2318
|
+
};
|
|
2319
|
+
};
|
|
2320
|
+
|
|
2321
|
+
const clampRgbUnit = (v) => clamp(0, 255, v);
|
|
2322
|
+
const rgbUnit = {
|
|
2323
|
+
...number,
|
|
2324
|
+
transform: (v) => Math.round(clampRgbUnit(v)),
|
|
2325
|
+
};
|
|
2326
|
+
const rgba = {
|
|
2327
|
+
test: isColorString("rgb", "red"),
|
|
2328
|
+
parse: splitColor("red", "green", "blue"),
|
|
2329
|
+
transform: ({ red, green, blue, alpha: alpha$1 = 1 }) => "rgba(" +
|
|
2330
|
+
rgbUnit.transform(red) +
|
|
2331
|
+
", " +
|
|
2332
|
+
rgbUnit.transform(green) +
|
|
2333
|
+
", " +
|
|
2334
|
+
rgbUnit.transform(blue) +
|
|
2335
|
+
", " +
|
|
2336
|
+
sanitize(alpha.transform(alpha$1)) +
|
|
2337
|
+
")",
|
|
2338
|
+
};
|
|
2339
|
+
|
|
2340
|
+
function parseHex(v) {
|
|
2341
|
+
let r = "";
|
|
2342
|
+
let g = "";
|
|
2343
|
+
let b = "";
|
|
2344
|
+
let a = "";
|
|
2345
|
+
// If we have 6 characters, ie #FF0000
|
|
2346
|
+
if (v.length > 5) {
|
|
2347
|
+
r = v.substring(1, 3);
|
|
2348
|
+
g = v.substring(3, 5);
|
|
2349
|
+
b = v.substring(5, 7);
|
|
2350
|
+
a = v.substring(7, 9);
|
|
2351
|
+
// Or we have 3 characters, ie #F00
|
|
2352
|
+
}
|
|
2353
|
+
else {
|
|
2354
|
+
r = v.substring(1, 2);
|
|
2355
|
+
g = v.substring(2, 3);
|
|
2356
|
+
b = v.substring(3, 4);
|
|
2357
|
+
a = v.substring(4, 5);
|
|
2358
|
+
r += r;
|
|
2359
|
+
g += g;
|
|
2360
|
+
b += b;
|
|
2361
|
+
a += a;
|
|
2362
|
+
}
|
|
2363
|
+
return {
|
|
2364
|
+
red: parseInt(r, 16),
|
|
2365
|
+
green: parseInt(g, 16),
|
|
2366
|
+
blue: parseInt(b, 16),
|
|
2367
|
+
alpha: a ? parseInt(a, 16) / 255 : 1,
|
|
2368
|
+
};
|
|
2369
|
+
}
|
|
2370
|
+
const hex = {
|
|
2371
|
+
test: isColorString("#"),
|
|
2372
|
+
parse: parseHex,
|
|
2373
|
+
transform: rgba.transform,
|
|
2374
|
+
};
|
|
2375
|
+
|
|
2376
|
+
const hsla = {
|
|
2377
|
+
test: isColorString("hsl", "hue"),
|
|
2378
|
+
parse: splitColor("hue", "saturation", "lightness"),
|
|
2379
|
+
transform: ({ hue, saturation, lightness, alpha: alpha$1 = 1 }) => {
|
|
2380
|
+
return ("hsla(" +
|
|
2381
|
+
Math.round(hue) +
|
|
2382
|
+
", " +
|
|
2383
|
+
percent.transform(sanitize(saturation)) +
|
|
2384
|
+
", " +
|
|
2385
|
+
percent.transform(sanitize(lightness)) +
|
|
2386
|
+
", " +
|
|
2387
|
+
sanitize(alpha.transform(alpha$1)) +
|
|
2388
|
+
")");
|
|
2389
|
+
},
|
|
2390
|
+
};
|
|
2391
|
+
|
|
2392
|
+
const color = {
|
|
2393
|
+
test: (v) => rgba.test(v) || hex.test(v) || hsla.test(v),
|
|
2394
|
+
parse: (v) => {
|
|
2395
|
+
if (rgba.test(v)) {
|
|
2396
|
+
return rgba.parse(v);
|
|
2397
|
+
}
|
|
2398
|
+
else if (hsla.test(v)) {
|
|
2399
|
+
return hsla.parse(v);
|
|
2400
|
+
}
|
|
2401
|
+
else {
|
|
2402
|
+
return hex.parse(v);
|
|
2403
|
+
}
|
|
2404
|
+
},
|
|
2405
|
+
transform: (v) => {
|
|
2406
|
+
return isString(v)
|
|
2407
|
+
? v
|
|
2408
|
+
: v.hasOwnProperty("red")
|
|
2409
|
+
? rgba.transform(v)
|
|
2410
|
+
: hsla.transform(v);
|
|
2411
|
+
},
|
|
2412
|
+
};
|
|
2413
|
+
|
|
2414
|
+
const colorToken = "${c}";
|
|
2415
|
+
const numberToken = "${n}";
|
|
2416
|
+
function test(v) {
|
|
2417
|
+
var _a, _b;
|
|
2418
|
+
return (isNaN(v) &&
|
|
2419
|
+
isString(v) &&
|
|
2420
|
+
(((_a = v.match(floatRegex)) === null || _a === void 0 ? void 0 : _a.length) || 0) +
|
|
2421
|
+
(((_b = v.match(colorRegex)) === null || _b === void 0 ? void 0 : _b.length) || 0) >
|
|
2422
|
+
0);
|
|
2423
|
+
}
|
|
2424
|
+
function analyseComplexValue(v) {
|
|
2425
|
+
if (typeof v === "number")
|
|
2426
|
+
v = `${v}`;
|
|
2427
|
+
const values = [];
|
|
2428
|
+
let numColors = 0;
|
|
2429
|
+
let numNumbers = 0;
|
|
2430
|
+
const colors = v.match(colorRegex);
|
|
2431
|
+
if (colors) {
|
|
2432
|
+
numColors = colors.length;
|
|
2433
|
+
// Strip colors from input so they're not picked up by number regex.
|
|
2434
|
+
// There's a better way to combine these regex searches, but its beyond my regex skills
|
|
2435
|
+
v = v.replace(colorRegex, colorToken);
|
|
2436
|
+
values.push(...colors.map(color.parse));
|
|
2437
|
+
}
|
|
2438
|
+
const numbers = v.match(floatRegex);
|
|
2439
|
+
if (numbers) {
|
|
2440
|
+
numNumbers = numbers.length;
|
|
2441
|
+
v = v.replace(floatRegex, numberToken);
|
|
2442
|
+
values.push(...numbers.map(number.parse));
|
|
2443
|
+
}
|
|
2444
|
+
return { values, numColors, numNumbers, tokenised: v };
|
|
2445
|
+
}
|
|
2446
|
+
function parse(v) {
|
|
2447
|
+
return analyseComplexValue(v).values;
|
|
2448
|
+
}
|
|
2449
|
+
function createTransformer(source) {
|
|
2450
|
+
const { values, numColors, tokenised } = analyseComplexValue(source);
|
|
2451
|
+
const numValues = values.length;
|
|
2452
|
+
return (v) => {
|
|
2453
|
+
let output = tokenised;
|
|
2454
|
+
for (let i = 0; i < numValues; i++) {
|
|
2455
|
+
output = output.replace(i < numColors ? colorToken : numberToken, i < numColors
|
|
2456
|
+
? color.transform(v[i])
|
|
2457
|
+
: sanitize(v[i]));
|
|
2458
|
+
}
|
|
2459
|
+
return output;
|
|
2460
|
+
};
|
|
2461
|
+
}
|
|
2462
|
+
const convertNumbersToZero = (v) => typeof v === "number" ? 0 : v;
|
|
2463
|
+
function getAnimatableNone$1(v) {
|
|
2464
|
+
const parsed = parse(v);
|
|
2465
|
+
const transformer = createTransformer(v);
|
|
2466
|
+
return transformer(parsed.map(convertNumbersToZero));
|
|
2467
|
+
}
|
|
2468
|
+
const complex = { test, parse, createTransformer, getAnimatableNone: getAnimatableNone$1 };
|
|
2469
|
+
|
|
2470
|
+
/**
|
|
2471
|
+
* Properties that should default to 1 or 100%
|
|
2472
|
+
*/
|
|
2473
|
+
const maxDefaults = new Set(["brightness", "contrast", "saturate", "opacity"]);
|
|
2474
|
+
function applyDefaultFilter(v) {
|
|
2475
|
+
const [name, value] = v.slice(0, -1).split("(");
|
|
2476
|
+
if (name === "drop-shadow")
|
|
2477
|
+
return v;
|
|
2478
|
+
const [number] = value.match(floatRegex) || [];
|
|
2479
|
+
if (!number)
|
|
2480
|
+
return v;
|
|
2481
|
+
const unit = value.replace(number, "");
|
|
2482
|
+
let defaultValue = maxDefaults.has(name) ? 1 : 0;
|
|
2483
|
+
if (number !== value)
|
|
2484
|
+
defaultValue *= 100;
|
|
2485
|
+
return name + "(" + defaultValue + unit + ")";
|
|
2486
|
+
}
|
|
2487
|
+
const functionRegex = /([a-z-]*)\(.*?\)/g;
|
|
2488
|
+
const filter = {
|
|
2489
|
+
...complex,
|
|
2490
|
+
getAnimatableNone: (v) => {
|
|
2491
|
+
const functions = v.match(functionRegex);
|
|
2492
|
+
return functions ? functions.map(applyDefaultFilter).join(" ") : v;
|
|
2493
|
+
},
|
|
2494
|
+
};
|
|
2495
|
+
|
|
2496
|
+
/**
|
|
2497
|
+
* A map of default value types for common values
|
|
2498
|
+
*/
|
|
2499
|
+
const defaultValueTypes = {
|
|
2500
|
+
...numberValueTypes,
|
|
2501
|
+
// Color props
|
|
2502
|
+
color,
|
|
2503
|
+
backgroundColor: color,
|
|
2504
|
+
outlineColor: color,
|
|
2505
|
+
fill: color,
|
|
2506
|
+
stroke: color,
|
|
2507
|
+
// Border props
|
|
2508
|
+
borderColor: color,
|
|
2509
|
+
borderTopColor: color,
|
|
2510
|
+
borderRightColor: color,
|
|
2511
|
+
borderBottomColor: color,
|
|
2512
|
+
borderLeftColor: color,
|
|
2513
|
+
filter,
|
|
2514
|
+
WebkitFilter: filter,
|
|
2515
|
+
};
|
|
2516
|
+
/**
|
|
2517
|
+
* Gets the default ValueType for the provided value key
|
|
2518
|
+
*/
|
|
2519
|
+
const getDefaultValueType = (key) => defaultValueTypes[key];
|
|
2520
|
+
|
|
2521
|
+
function getAnimatableNone(key, value) {
|
|
2522
|
+
let defaultValueType = getDefaultValueType(key);
|
|
2523
|
+
if (defaultValueType !== filter)
|
|
2524
|
+
defaultValueType = complex;
|
|
2525
|
+
// If value is not recognised as animatable, ie "none", create an animatable version origin based on the target
|
|
2526
|
+
return defaultValueType.getAnimatableNone
|
|
2527
|
+
? defaultValueType.getAnimatableNone(value)
|
|
2528
|
+
: undefined;
|
|
2529
|
+
}
|
|
2530
|
+
|
|
2531
|
+
/**
|
|
2532
|
+
* Tests a provided value against a ValueType
|
|
2533
|
+
*/
|
|
2534
|
+
const testValueType = (v) => (type) => type.test(v);
|
|
2535
|
+
|
|
2536
|
+
/**
|
|
2537
|
+
* ValueType for "auto"
|
|
2538
|
+
*/
|
|
2539
|
+
const auto = {
|
|
2540
|
+
test: (v) => v === "auto",
|
|
2541
|
+
parse: (v) => v,
|
|
2542
|
+
};
|
|
2543
|
+
|
|
2544
|
+
/**
|
|
2545
|
+
* A list of value types commonly used for dimensions
|
|
2546
|
+
*/
|
|
2547
|
+
const dimensionValueTypes = [number, px, percent, degrees, vw, vh, auto];
|
|
2548
|
+
/**
|
|
2549
|
+
* Tests a dimensional value against the list of dimension ValueTypes
|
|
2550
|
+
*/
|
|
2551
|
+
const findDimensionValueType = (v) => dimensionValueTypes.find(testValueType(v));
|
|
2552
|
+
|
|
2553
|
+
/**
|
|
2554
|
+
* A list of all ValueTypes
|
|
2555
|
+
*/
|
|
2556
|
+
const valueTypes = [...dimensionValueTypes, color, complex];
|
|
2557
|
+
/**
|
|
2558
|
+
* Tests a value against the list of ValueTypes
|
|
2559
|
+
*/
|
|
2560
|
+
const findValueType = (v) => valueTypes.find(testValueType(v));
|
|
2561
|
+
|
|
2562
|
+
/**
|
|
2563
|
+
* Creates an object containing the latest state of every MotionValue on a VisualElement
|
|
2564
|
+
*/
|
|
2565
|
+
function getCurrent(visualElement) {
|
|
2566
|
+
const current = {};
|
|
2567
|
+
visualElement.values.forEach((value, key) => (current[key] = value.get()));
|
|
2568
|
+
return current;
|
|
2569
|
+
}
|
|
2570
|
+
/**
|
|
2571
|
+
* Creates an object containing the latest velocity of every MotionValue on a VisualElement
|
|
2572
|
+
*/
|
|
2573
|
+
function getVelocity$1(visualElement) {
|
|
2574
|
+
const velocity = {};
|
|
2575
|
+
visualElement.values.forEach((value, key) => (velocity[key] = value.getVelocity()));
|
|
2576
|
+
return velocity;
|
|
2577
|
+
}
|
|
2578
|
+
function resolveVariant(visualElement, definition, custom) {
|
|
2579
|
+
const props = visualElement.getProps();
|
|
2580
|
+
return resolveVariantFromProps(props, definition, custom !== undefined ? custom : props.custom, getCurrent(visualElement), getVelocity$1(visualElement));
|
|
2581
|
+
}
|
|
2582
|
+
|
|
2583
|
+
/**
|
|
2584
|
+
* Set VisualElement's MotionValue, creating a new MotionValue for it if
|
|
2585
|
+
* it doesn't exist.
|
|
2586
|
+
*/
|
|
2587
|
+
function setMotionValue(visualElement, key, value) {
|
|
2588
|
+
if (visualElement.hasValue(key)) {
|
|
2589
|
+
visualElement.getValue(key).set(value);
|
|
2590
|
+
}
|
|
2591
|
+
else {
|
|
2592
|
+
visualElement.addValue(key, motionValue(value));
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2595
|
+
function setTarget(visualElement, definition) {
|
|
2596
|
+
const resolved = resolveVariant(visualElement, definition);
|
|
2597
|
+
let { transitionEnd = {}, transition = {}, ...target } = resolved ? visualElement.makeTargetAnimatable(resolved, false) : {};
|
|
2598
|
+
target = { ...target, ...transitionEnd };
|
|
2599
|
+
for (const key in target) {
|
|
2600
|
+
const value = resolveFinalValueInKeyframes(target[key]);
|
|
2601
|
+
setMotionValue(visualElement, key, value);
|
|
2602
|
+
}
|
|
2603
|
+
}
|
|
2604
|
+
function setVariants(visualElement, variantLabels) {
|
|
2605
|
+
const reversedLabels = [...variantLabels].reverse();
|
|
2606
|
+
reversedLabels.forEach((key) => {
|
|
2607
|
+
const variant = visualElement.getVariant(key);
|
|
2608
|
+
variant && setTarget(visualElement, variant);
|
|
2609
|
+
if (visualElement.variantChildren) {
|
|
2610
|
+
visualElement.variantChildren.forEach((child) => {
|
|
2611
|
+
setVariants(child, variantLabels);
|
|
2612
|
+
});
|
|
2613
|
+
}
|
|
2614
|
+
});
|
|
2615
|
+
}
|
|
2616
|
+
function setValues(visualElement, definition) {
|
|
2617
|
+
if (Array.isArray(definition)) {
|
|
2618
|
+
return setVariants(visualElement, definition);
|
|
2619
|
+
}
|
|
2620
|
+
else if (typeof definition === "string") {
|
|
2621
|
+
return setVariants(visualElement, [definition]);
|
|
2622
|
+
}
|
|
2623
|
+
else {
|
|
2624
|
+
setTarget(visualElement, definition);
|
|
2625
|
+
}
|
|
2626
|
+
}
|
|
2627
|
+
function checkTargetForNewValues(visualElement, target, origin) {
|
|
2628
|
+
var _a, _b;
|
|
2629
|
+
const newValueKeys = Object.keys(target).filter((key) => !visualElement.hasValue(key));
|
|
2630
|
+
const numNewValues = newValueKeys.length;
|
|
2631
|
+
if (!numNewValues)
|
|
2632
|
+
return;
|
|
2633
|
+
for (let i = 0; i < numNewValues; i++) {
|
|
2634
|
+
const key = newValueKeys[i];
|
|
2635
|
+
const targetValue = target[key];
|
|
1678
2636
|
let value = null;
|
|
1679
2637
|
/**
|
|
1680
|
-
* If the target is a series of keyframes, we can use the first value
|
|
1681
|
-
* in the array. If this first value is null, we'll still need to read from the DOM.
|
|
2638
|
+
* If the target is a series of keyframes, we can use the first value
|
|
2639
|
+
* in the array. If this first value is null, we'll still need to read from the DOM.
|
|
2640
|
+
*/
|
|
2641
|
+
if (Array.isArray(targetValue)) {
|
|
2642
|
+
value = targetValue[0];
|
|
2643
|
+
}
|
|
2644
|
+
/**
|
|
2645
|
+
* If the target isn't keyframes, or the first keyframe was null, we need to
|
|
2646
|
+
* first check if an origin value was explicitly defined in the transition as "from",
|
|
2647
|
+
* if not read the value from the DOM. As an absolute fallback, take the defined target value.
|
|
2648
|
+
*/
|
|
2649
|
+
if (value === null) {
|
|
2650
|
+
value = (_b = (_a = origin[key]) !== null && _a !== void 0 ? _a : visualElement.readValue(key)) !== null && _b !== void 0 ? _b : target[key];
|
|
2651
|
+
}
|
|
2652
|
+
/**
|
|
2653
|
+
* If value is still undefined or null, ignore it. Preferably this would throw,
|
|
2654
|
+
* but this was causing issues in Framer.
|
|
2655
|
+
*/
|
|
2656
|
+
if (value === undefined || value === null)
|
|
2657
|
+
continue;
|
|
2658
|
+
if (typeof value === "string" &&
|
|
2659
|
+
(isNumericalString(value) || isZeroValueString(value))) {
|
|
2660
|
+
// If this is a number read as a string, ie "0" or "200", convert it to a number
|
|
2661
|
+
value = parseFloat(value);
|
|
2662
|
+
}
|
|
2663
|
+
else if (!findValueType(value) && complex.test(targetValue)) {
|
|
2664
|
+
value = getAnimatableNone(key, targetValue);
|
|
2665
|
+
}
|
|
2666
|
+
visualElement.addValue(key, motionValue(value, { owner: visualElement }));
|
|
2667
|
+
if (origin[key] === undefined) {
|
|
2668
|
+
origin[key] = value;
|
|
2669
|
+
}
|
|
2670
|
+
if (value !== null)
|
|
2671
|
+
visualElement.setBaseTarget(key, value);
|
|
2672
|
+
}
|
|
2673
|
+
}
|
|
2674
|
+
function getOriginFromTransition(key, transition) {
|
|
2675
|
+
if (!transition)
|
|
2676
|
+
return;
|
|
2677
|
+
const valueTransition = transition[key] || transition["default"] || transition;
|
|
2678
|
+
return valueTransition.from;
|
|
2679
|
+
}
|
|
2680
|
+
function getOrigin(target, transition, visualElement) {
|
|
2681
|
+
const origin = {};
|
|
2682
|
+
for (const key in target) {
|
|
2683
|
+
const transitionOrigin = getOriginFromTransition(key, transition);
|
|
2684
|
+
if (transitionOrigin !== undefined) {
|
|
2685
|
+
origin[key] = transitionOrigin;
|
|
2686
|
+
}
|
|
2687
|
+
else {
|
|
2688
|
+
const value = visualElement.getValue(key);
|
|
2689
|
+
if (value) {
|
|
2690
|
+
origin[key] = value.get();
|
|
2691
|
+
}
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2694
|
+
return origin;
|
|
2695
|
+
}
|
|
2696
|
+
|
|
2697
|
+
function isWillChangeMotionValue(value) {
|
|
2698
|
+
return Boolean(isMotionValue(value) && value.add);
|
|
2699
|
+
}
|
|
2700
|
+
|
|
2701
|
+
const optimizedAppearDataId = "framerAppearId";
|
|
2702
|
+
const optimizedAppearDataAttribute = "data-" + camelToDash(optimizedAppearDataId);
|
|
2703
|
+
|
|
2704
|
+
exports.warning = noop;
|
|
2705
|
+
exports.invariant = noop;
|
|
2706
|
+
if (process.env.NODE_ENV !== "production") {
|
|
2707
|
+
exports.warning = (check, message) => {
|
|
2708
|
+
if (!check && typeof console !== "undefined") {
|
|
2709
|
+
console.warn(message);
|
|
2710
|
+
}
|
|
2711
|
+
};
|
|
2712
|
+
exports.invariant = (check, message) => {
|
|
2713
|
+
if (!check) {
|
|
2714
|
+
throw new Error(message);
|
|
2715
|
+
}
|
|
2716
|
+
};
|
|
2717
|
+
}
|
|
2718
|
+
|
|
2719
|
+
/**
|
|
2720
|
+
* Converts seconds to milliseconds
|
|
2721
|
+
*
|
|
2722
|
+
* @param seconds - Time in seconds.
|
|
2723
|
+
* @return milliseconds - Converted time in milliseconds.
|
|
2724
|
+
*/
|
|
2725
|
+
const secondsToMilliseconds = (seconds) => seconds * 1000;
|
|
2726
|
+
|
|
2727
|
+
const instantAnimationState = {
|
|
2728
|
+
current: false,
|
|
2729
|
+
};
|
|
2730
|
+
|
|
2731
|
+
// Accepts an easing function and returns a new one that outputs mirrored values for
|
|
2732
|
+
// the second half of the animation. Turns easeIn into easeInOut.
|
|
2733
|
+
const mirrorEasing = (easing) => (p) => p <= 0.5 ? easing(2 * p) / 2 : (2 - easing(2 * (1 - p))) / 2;
|
|
2734
|
+
|
|
2735
|
+
// Accepts an easing function and returns a new one that outputs reversed values.
|
|
2736
|
+
// Turns easeIn into easeOut.
|
|
2737
|
+
const reverseEasing = (easing) => (p) => 1 - easing(1 - p);
|
|
2738
|
+
|
|
2739
|
+
const easeIn = (p) => p * p;
|
|
2740
|
+
const easeOut = reverseEasing(easeIn);
|
|
2741
|
+
const easeInOut = mirrorEasing(easeIn);
|
|
2742
|
+
|
|
2743
|
+
/*
|
|
2744
|
+
Value in range from progress
|
|
2745
|
+
|
|
2746
|
+
Given a lower limit and an upper limit, we return the value within
|
|
2747
|
+
that range as expressed by progress (usually a number from 0 to 1)
|
|
2748
|
+
|
|
2749
|
+
So progress = 0.5 would change
|
|
2750
|
+
|
|
2751
|
+
from -------- to
|
|
2752
|
+
|
|
2753
|
+
to
|
|
2754
|
+
|
|
2755
|
+
from ---- to
|
|
2756
|
+
|
|
2757
|
+
E.g. from = 10, to = 20, progress = 0.5 => 15
|
|
2758
|
+
|
|
2759
|
+
@param [number]: Lower limit of range
|
|
2760
|
+
@param [number]: Upper limit of range
|
|
2761
|
+
@param [number]: The progress between lower and upper limits expressed 0-1
|
|
2762
|
+
@return [number]: Value as calculated from progress within range (not limited within range)
|
|
2763
|
+
*/
|
|
2764
|
+
const mix = (from, to, progress) => -progress * from + progress * to + from;
|
|
2765
|
+
|
|
2766
|
+
// Adapted from https://gist.github.com/mjackson/5311256
|
|
2767
|
+
function hueToRgb(p, q, t) {
|
|
2768
|
+
if (t < 0)
|
|
2769
|
+
t += 1;
|
|
2770
|
+
if (t > 1)
|
|
2771
|
+
t -= 1;
|
|
2772
|
+
if (t < 1 / 6)
|
|
2773
|
+
return p + (q - p) * 6 * t;
|
|
2774
|
+
if (t < 1 / 2)
|
|
2775
|
+
return q;
|
|
2776
|
+
if (t < 2 / 3)
|
|
2777
|
+
return p + (q - p) * (2 / 3 - t) * 6;
|
|
2778
|
+
return p;
|
|
2779
|
+
}
|
|
2780
|
+
function hslaToRgba({ hue, saturation, lightness, alpha }) {
|
|
2781
|
+
hue /= 360;
|
|
2782
|
+
saturation /= 100;
|
|
2783
|
+
lightness /= 100;
|
|
2784
|
+
let red = 0;
|
|
2785
|
+
let green = 0;
|
|
2786
|
+
let blue = 0;
|
|
2787
|
+
if (!saturation) {
|
|
2788
|
+
red = green = blue = lightness;
|
|
2789
|
+
}
|
|
2790
|
+
else {
|
|
2791
|
+
const q = lightness < 0.5
|
|
2792
|
+
? lightness * (1 + saturation)
|
|
2793
|
+
: lightness + saturation - lightness * saturation;
|
|
2794
|
+
const p = 2 * lightness - q;
|
|
2795
|
+
red = hueToRgb(p, q, hue + 1 / 3);
|
|
2796
|
+
green = hueToRgb(p, q, hue);
|
|
2797
|
+
blue = hueToRgb(p, q, hue - 1 / 3);
|
|
2798
|
+
}
|
|
2799
|
+
return {
|
|
2800
|
+
red: Math.round(red * 255),
|
|
2801
|
+
green: Math.round(green * 255),
|
|
2802
|
+
blue: Math.round(blue * 255),
|
|
2803
|
+
alpha,
|
|
2804
|
+
};
|
|
2805
|
+
}
|
|
2806
|
+
|
|
2807
|
+
// Linear color space blending
|
|
2808
|
+
// Explained https://www.youtube.com/watch?v=LKnqECcg6Gw
|
|
2809
|
+
// Demonstrated http://codepen.io/osublake/pen/xGVVaN
|
|
2810
|
+
const mixLinearColor = (from, to, v) => {
|
|
2811
|
+
const fromExpo = from * from;
|
|
2812
|
+
return Math.sqrt(Math.max(0, v * (to * to - fromExpo) + fromExpo));
|
|
2813
|
+
};
|
|
2814
|
+
const colorTypes = [hex, rgba, hsla];
|
|
2815
|
+
const getColorType = (v) => colorTypes.find((type) => type.test(v));
|
|
2816
|
+
function asRGBA(color) {
|
|
2817
|
+
const type = getColorType(color);
|
|
2818
|
+
exports.invariant(Boolean(type), `'${color}' is not an animatable color. Use the equivalent color code instead.`);
|
|
2819
|
+
let model = type.parse(color);
|
|
2820
|
+
if (type === hsla) {
|
|
2821
|
+
// TODO Remove this cast - needed since Framer Motion's stricter typing
|
|
2822
|
+
model = hslaToRgba(model);
|
|
2823
|
+
}
|
|
2824
|
+
return model;
|
|
2825
|
+
}
|
|
2826
|
+
const mixColor = (from, to) => {
|
|
2827
|
+
const fromRGBA = asRGBA(from);
|
|
2828
|
+
const toRGBA = asRGBA(to);
|
|
2829
|
+
const blended = { ...fromRGBA };
|
|
2830
|
+
return (v) => {
|
|
2831
|
+
blended.red = mixLinearColor(fromRGBA.red, toRGBA.red, v);
|
|
2832
|
+
blended.green = mixLinearColor(fromRGBA.green, toRGBA.green, v);
|
|
2833
|
+
blended.blue = mixLinearColor(fromRGBA.blue, toRGBA.blue, v);
|
|
2834
|
+
blended.alpha = mix(fromRGBA.alpha, toRGBA.alpha, v);
|
|
2835
|
+
return rgba.transform(blended);
|
|
2836
|
+
};
|
|
2837
|
+
};
|
|
2838
|
+
|
|
2839
|
+
function getMixer$1(origin, target) {
|
|
2840
|
+
if (typeof origin === "number") {
|
|
2841
|
+
return (v) => mix(origin, target, v);
|
|
2842
|
+
}
|
|
2843
|
+
else if (color.test(origin)) {
|
|
2844
|
+
return mixColor(origin, target);
|
|
2845
|
+
}
|
|
2846
|
+
else {
|
|
2847
|
+
return mixComplex(origin, target);
|
|
2848
|
+
}
|
|
2849
|
+
}
|
|
2850
|
+
const mixArray = (from, to) => {
|
|
2851
|
+
const output = [...from];
|
|
2852
|
+
const numValues = output.length;
|
|
2853
|
+
const blendValue = from.map((fromThis, i) => getMixer$1(fromThis, to[i]));
|
|
2854
|
+
return (v) => {
|
|
2855
|
+
for (let i = 0; i < numValues; i++) {
|
|
2856
|
+
output[i] = blendValue[i](v);
|
|
2857
|
+
}
|
|
2858
|
+
return output;
|
|
2859
|
+
};
|
|
2860
|
+
};
|
|
2861
|
+
const mixObject = (origin, target) => {
|
|
2862
|
+
const output = { ...origin, ...target };
|
|
2863
|
+
const blendValue = {};
|
|
2864
|
+
for (const key in output) {
|
|
2865
|
+
if (origin[key] !== undefined && target[key] !== undefined) {
|
|
2866
|
+
blendValue[key] = getMixer$1(origin[key], target[key]);
|
|
2867
|
+
}
|
|
2868
|
+
}
|
|
2869
|
+
return (v) => {
|
|
2870
|
+
for (const key in blendValue) {
|
|
2871
|
+
output[key] = blendValue[key](v);
|
|
2872
|
+
}
|
|
2873
|
+
return output;
|
|
2874
|
+
};
|
|
2875
|
+
};
|
|
2876
|
+
const mixComplex = (origin, target) => {
|
|
2877
|
+
const template = complex.createTransformer(target);
|
|
2878
|
+
const originStats = analyseComplexValue(origin);
|
|
2879
|
+
const targetStats = analyseComplexValue(target);
|
|
2880
|
+
const canInterpolate = originStats.numColors === targetStats.numColors &&
|
|
2881
|
+
originStats.numNumbers >= targetStats.numNumbers;
|
|
2882
|
+
if (canInterpolate) {
|
|
2883
|
+
return pipe(mixArray(originStats.values, targetStats.values), template);
|
|
2884
|
+
}
|
|
2885
|
+
else {
|
|
2886
|
+
exports.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.`);
|
|
2887
|
+
return (p) => `${p > 0 ? target : origin}`;
|
|
2888
|
+
}
|
|
2889
|
+
};
|
|
2890
|
+
|
|
2891
|
+
/*
|
|
2892
|
+
Progress within given range
|
|
2893
|
+
|
|
2894
|
+
Given a lower limit and an upper limit, we return the progress
|
|
2895
|
+
(expressed as a number 0-1) represented by the given value, and
|
|
2896
|
+
limit that progress to within 0-1.
|
|
2897
|
+
|
|
2898
|
+
@param [number]: Lower limit
|
|
2899
|
+
@param [number]: Upper limit
|
|
2900
|
+
@param [number]: Value to find progress within given range
|
|
2901
|
+
@return [number]: Progress of value within range as expressed 0-1
|
|
2902
|
+
*/
|
|
2903
|
+
const progress = (from, to, value) => {
|
|
2904
|
+
const toFromDifference = to - from;
|
|
2905
|
+
return toFromDifference === 0 ? 1 : (value - from) / toFromDifference;
|
|
2906
|
+
};
|
|
2907
|
+
|
|
2908
|
+
const mixNumber = (from, to) => (p) => mix(from, to, p);
|
|
2909
|
+
function detectMixerFactory(v) {
|
|
2910
|
+
if (typeof v === "number") {
|
|
2911
|
+
return mixNumber;
|
|
2912
|
+
}
|
|
2913
|
+
else if (typeof v === "string") {
|
|
2914
|
+
if (color.test(v)) {
|
|
2915
|
+
return mixColor;
|
|
2916
|
+
}
|
|
2917
|
+
else {
|
|
2918
|
+
return mixComplex;
|
|
2919
|
+
}
|
|
2920
|
+
}
|
|
2921
|
+
else if (Array.isArray(v)) {
|
|
2922
|
+
return mixArray;
|
|
2923
|
+
}
|
|
2924
|
+
else if (typeof v === "object") {
|
|
2925
|
+
return mixObject;
|
|
2926
|
+
}
|
|
2927
|
+
return mixNumber;
|
|
2928
|
+
}
|
|
2929
|
+
function createMixers(output, ease, customMixer) {
|
|
2930
|
+
const mixers = [];
|
|
2931
|
+
const mixerFactory = customMixer || detectMixerFactory(output[0]);
|
|
2932
|
+
const numMixers = output.length - 1;
|
|
2933
|
+
for (let i = 0; i < numMixers; i++) {
|
|
2934
|
+
let mixer = mixerFactory(output[i], output[i + 1]);
|
|
2935
|
+
if (ease) {
|
|
2936
|
+
const easingFunction = Array.isArray(ease) ? ease[i] : ease;
|
|
2937
|
+
mixer = pipe(easingFunction, mixer);
|
|
2938
|
+
}
|
|
2939
|
+
mixers.push(mixer);
|
|
2940
|
+
}
|
|
2941
|
+
return mixers;
|
|
2942
|
+
}
|
|
2943
|
+
/**
|
|
2944
|
+
* Create a function that maps from a numerical input array to a generic output array.
|
|
2945
|
+
*
|
|
2946
|
+
* Accepts:
|
|
2947
|
+
* - Numbers
|
|
2948
|
+
* - Colors (hex, hsl, hsla, rgb, rgba)
|
|
2949
|
+
* - Complex (combinations of one or more numbers or strings)
|
|
2950
|
+
*
|
|
2951
|
+
* ```jsx
|
|
2952
|
+
* const mixColor = interpolate([0, 1], ['#fff', '#000'])
|
|
2953
|
+
*
|
|
2954
|
+
* mixColor(0.5) // 'rgba(128, 128, 128, 1)'
|
|
2955
|
+
* ```
|
|
2956
|
+
*
|
|
2957
|
+
* TODO Revist this approach once we've moved to data models for values,
|
|
2958
|
+
* probably not needed to pregenerate mixer functions.
|
|
2959
|
+
*
|
|
2960
|
+
* @public
|
|
2961
|
+
*/
|
|
2962
|
+
function interpolate(input, output, { clamp: isClamp = true, ease, mixer } = {}) {
|
|
2963
|
+
const inputLength = input.length;
|
|
2964
|
+
exports.invariant(inputLength === output.length, "Both input and output ranges must be the same length");
|
|
2965
|
+
exports.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.");
|
|
2966
|
+
// If input runs highest -> lowest, reverse both arrays
|
|
2967
|
+
if (input[0] > input[inputLength - 1]) {
|
|
2968
|
+
input = [...input].reverse();
|
|
2969
|
+
output = [...output].reverse();
|
|
2970
|
+
}
|
|
2971
|
+
const mixers = createMixers(output, ease, mixer);
|
|
2972
|
+
const numMixers = mixers.length;
|
|
2973
|
+
const interpolator = (v) => {
|
|
2974
|
+
let i = 0;
|
|
2975
|
+
if (numMixers > 1) {
|
|
2976
|
+
for (; i < input.length - 2; i++) {
|
|
2977
|
+
if (v < input[i + 1])
|
|
2978
|
+
break;
|
|
2979
|
+
}
|
|
2980
|
+
}
|
|
2981
|
+
const progressInRange = progress(input[i], input[i + 1], v);
|
|
2982
|
+
return mixers[i](progressInRange);
|
|
2983
|
+
};
|
|
2984
|
+
return isClamp
|
|
2985
|
+
? (v) => interpolator(clamp(input[0], input[inputLength - 1], v))
|
|
2986
|
+
: interpolator;
|
|
2987
|
+
}
|
|
2988
|
+
|
|
2989
|
+
function fillOffset(offset, remaining) {
|
|
2990
|
+
const min = offset[offset.length - 1];
|
|
2991
|
+
for (let i = 1; i <= remaining; i++) {
|
|
2992
|
+
const offsetProgress = progress(0, remaining, i);
|
|
2993
|
+
offset.push(mix(min, 1, offsetProgress));
|
|
2994
|
+
}
|
|
2995
|
+
}
|
|
2996
|
+
|
|
2997
|
+
function defaultOffset$1(arr) {
|
|
2998
|
+
const offset = [0];
|
|
2999
|
+
fillOffset(offset, arr.length - 1);
|
|
3000
|
+
return offset;
|
|
3001
|
+
}
|
|
3002
|
+
|
|
3003
|
+
function convertOffsetToTimes(offset, duration) {
|
|
3004
|
+
return offset.map((o) => o * duration);
|
|
3005
|
+
}
|
|
3006
|
+
|
|
3007
|
+
/*
|
|
3008
|
+
Bezier function generator
|
|
3009
|
+
This has been modified from Gaëtan Renaudeau's BezierEasing
|
|
3010
|
+
https://github.com/gre/bezier-easing/blob/master/src/index.js
|
|
3011
|
+
https://github.com/gre/bezier-easing/blob/master/LICENSE
|
|
3012
|
+
|
|
3013
|
+
I've removed the newtonRaphsonIterate algo because in benchmarking it
|
|
3014
|
+
wasn't noticiably faster than binarySubdivision, indeed removing it
|
|
3015
|
+
usually improved times, depending on the curve.
|
|
3016
|
+
I also removed the lookup table, as for the added bundle size and loop we're
|
|
3017
|
+
only cutting ~4 or so subdivision iterations. I bumped the max iterations up
|
|
3018
|
+
to 12 to compensate and this still tended to be faster for no perceivable
|
|
3019
|
+
loss in accuracy.
|
|
3020
|
+
Usage
|
|
3021
|
+
const easeOut = cubicBezier(.17,.67,.83,.67);
|
|
3022
|
+
const x = easeOut(0.5); // returns 0.627...
|
|
3023
|
+
*/
|
|
3024
|
+
// Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2.
|
|
3025
|
+
const calcBezier = (t, a1, a2) => (((1.0 - 3.0 * a2 + 3.0 * a1) * t + (3.0 * a2 - 6.0 * a1)) * t + 3.0 * a1) *
|
|
3026
|
+
t;
|
|
3027
|
+
const subdivisionPrecision = 0.0000001;
|
|
3028
|
+
const subdivisionMaxIterations = 12;
|
|
3029
|
+
function binarySubdivide(x, lowerBound, upperBound, mX1, mX2) {
|
|
3030
|
+
let currentX;
|
|
3031
|
+
let currentT;
|
|
3032
|
+
let i = 0;
|
|
3033
|
+
do {
|
|
3034
|
+
currentT = lowerBound + (upperBound - lowerBound) / 2.0;
|
|
3035
|
+
currentX = calcBezier(currentT, mX1, mX2) - x;
|
|
3036
|
+
if (currentX > 0.0) {
|
|
3037
|
+
upperBound = currentT;
|
|
3038
|
+
}
|
|
3039
|
+
else {
|
|
3040
|
+
lowerBound = currentT;
|
|
3041
|
+
}
|
|
3042
|
+
} while (Math.abs(currentX) > subdivisionPrecision &&
|
|
3043
|
+
++i < subdivisionMaxIterations);
|
|
3044
|
+
return currentT;
|
|
3045
|
+
}
|
|
3046
|
+
function cubicBezier(mX1, mY1, mX2, mY2) {
|
|
3047
|
+
// If this is a linear gradient, return linear easing
|
|
3048
|
+
if (mX1 === mY1 && mX2 === mY2)
|
|
3049
|
+
return noop;
|
|
3050
|
+
const getTForX = (aX) => binarySubdivide(aX, 0, 1, mX1, mX2);
|
|
3051
|
+
// If animation is at start/end, return t without easing
|
|
3052
|
+
return (t) => t === 0 || t === 1 ? t : calcBezier(getTForX(t), mY1, mY2);
|
|
3053
|
+
}
|
|
3054
|
+
|
|
3055
|
+
const circIn = (p) => 1 - Math.sin(Math.acos(p));
|
|
3056
|
+
const circOut = reverseEasing(circIn);
|
|
3057
|
+
const circInOut = mirrorEasing(circOut);
|
|
3058
|
+
|
|
3059
|
+
const backOut = cubicBezier(0.33, 1.53, 0.69, 0.99);
|
|
3060
|
+
const backIn = reverseEasing(backOut);
|
|
3061
|
+
const backInOut = mirrorEasing(backIn);
|
|
3062
|
+
|
|
3063
|
+
const anticipate = (p) => (p *= 2) < 1 ? 0.5 * backIn(p) : 0.5 * (2 - Math.pow(2, -10 * (p - 1)));
|
|
3064
|
+
|
|
3065
|
+
const easingLookup = {
|
|
3066
|
+
linear: noop,
|
|
3067
|
+
easeIn,
|
|
3068
|
+
easeInOut,
|
|
3069
|
+
easeOut,
|
|
3070
|
+
circIn,
|
|
3071
|
+
circInOut,
|
|
3072
|
+
circOut,
|
|
3073
|
+
backIn,
|
|
3074
|
+
backInOut,
|
|
3075
|
+
backOut,
|
|
3076
|
+
anticipate,
|
|
3077
|
+
};
|
|
3078
|
+
const easingDefinitionToFunction = (definition) => {
|
|
3079
|
+
if (Array.isArray(definition)) {
|
|
3080
|
+
// If cubic bezier definition, create bezier curve
|
|
3081
|
+
exports.invariant(definition.length === 4, `Cubic bezier arrays must contain four numerical values.`);
|
|
3082
|
+
const [x1, y1, x2, y2] = definition;
|
|
3083
|
+
return cubicBezier(x1, y1, x2, y2);
|
|
3084
|
+
}
|
|
3085
|
+
else if (typeof definition === "string") {
|
|
3086
|
+
// Else lookup from table
|
|
3087
|
+
exports.invariant(easingLookup[definition] !== undefined, `Invalid easing type '${definition}'`);
|
|
3088
|
+
return easingLookup[definition];
|
|
3089
|
+
}
|
|
3090
|
+
return definition;
|
|
3091
|
+
};
|
|
3092
|
+
const isEasingArray = (ease) => {
|
|
3093
|
+
return Array.isArray(ease) && typeof ease[0] !== "number";
|
|
3094
|
+
};
|
|
3095
|
+
|
|
3096
|
+
function defaultEasing(values, easing) {
|
|
3097
|
+
return values.map(() => easing || easeInOut).splice(0, values.length - 1);
|
|
3098
|
+
}
|
|
3099
|
+
function keyframes({ keyframes: keyframeValues, ease = easeInOut, times, duration = 300, }) {
|
|
3100
|
+
keyframeValues = [...keyframeValues];
|
|
3101
|
+
/**
|
|
3102
|
+
* Easing functions can be externally defined as strings. Here we convert them
|
|
3103
|
+
* into actual functions.
|
|
3104
|
+
*/
|
|
3105
|
+
const easingFunctions = isEasingArray(ease)
|
|
3106
|
+
? ease.map(easingDefinitionToFunction)
|
|
3107
|
+
: easingDefinitionToFunction(ease);
|
|
3108
|
+
/**
|
|
3109
|
+
* This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
|
|
3110
|
+
* to reduce GC during animation.
|
|
3111
|
+
*/
|
|
3112
|
+
const state = {
|
|
3113
|
+
done: false,
|
|
3114
|
+
value: keyframeValues[0],
|
|
3115
|
+
};
|
|
3116
|
+
/**
|
|
3117
|
+
* Create a times array based on the provided 0-1 offsets
|
|
3118
|
+
*/
|
|
3119
|
+
const absoluteTimes = convertOffsetToTimes(
|
|
3120
|
+
// Only use the provided offsets if they're the correct length
|
|
3121
|
+
// TODO Maybe we should warn here if there's a length mismatch
|
|
3122
|
+
times && times.length === keyframeValues.length
|
|
3123
|
+
? times
|
|
3124
|
+
: defaultOffset$1(keyframeValues), duration);
|
|
3125
|
+
function createInterpolator() {
|
|
3126
|
+
return interpolate(absoluteTimes, keyframeValues, {
|
|
3127
|
+
ease: Array.isArray(easingFunctions)
|
|
3128
|
+
? easingFunctions
|
|
3129
|
+
: defaultEasing(keyframeValues, easingFunctions),
|
|
3130
|
+
});
|
|
3131
|
+
}
|
|
3132
|
+
let interpolator = createInterpolator();
|
|
3133
|
+
return {
|
|
3134
|
+
next: (t) => {
|
|
3135
|
+
state.value = interpolator(t);
|
|
3136
|
+
state.done = t >= duration;
|
|
3137
|
+
return state;
|
|
3138
|
+
},
|
|
3139
|
+
flipTarget: () => {
|
|
3140
|
+
keyframeValues.reverse();
|
|
3141
|
+
interpolator = createInterpolator();
|
|
3142
|
+
},
|
|
3143
|
+
};
|
|
3144
|
+
}
|
|
3145
|
+
|
|
3146
|
+
const safeMin = 0.001;
|
|
3147
|
+
const minDuration = 0.01;
|
|
3148
|
+
const maxDuration = 10.0;
|
|
3149
|
+
const minDamping = 0.05;
|
|
3150
|
+
const maxDamping = 1;
|
|
3151
|
+
function findSpring({ duration = 800, bounce = 0.25, velocity = 0, mass = 1, }) {
|
|
3152
|
+
let envelope;
|
|
3153
|
+
let derivative;
|
|
3154
|
+
exports.warning(duration <= maxDuration * 1000, "Spring duration must be 10 seconds or less");
|
|
3155
|
+
let dampingRatio = 1 - bounce;
|
|
3156
|
+
/**
|
|
3157
|
+
* Restrict dampingRatio and duration to within acceptable ranges.
|
|
3158
|
+
*/
|
|
3159
|
+
dampingRatio = clamp(minDamping, maxDamping, dampingRatio);
|
|
3160
|
+
duration = clamp(minDuration, maxDuration, duration / 1000);
|
|
3161
|
+
if (dampingRatio < 1) {
|
|
3162
|
+
/**
|
|
3163
|
+
* Underdamped spring
|
|
3164
|
+
*/
|
|
3165
|
+
envelope = (undampedFreq) => {
|
|
3166
|
+
const exponentialDecay = undampedFreq * dampingRatio;
|
|
3167
|
+
const delta = exponentialDecay * duration;
|
|
3168
|
+
const a = exponentialDecay - velocity;
|
|
3169
|
+
const b = calcAngularFreq(undampedFreq, dampingRatio);
|
|
3170
|
+
const c = Math.exp(-delta);
|
|
3171
|
+
return safeMin - (a / b) * c;
|
|
3172
|
+
};
|
|
3173
|
+
derivative = (undampedFreq) => {
|
|
3174
|
+
const exponentialDecay = undampedFreq * dampingRatio;
|
|
3175
|
+
const delta = exponentialDecay * duration;
|
|
3176
|
+
const d = delta * velocity + velocity;
|
|
3177
|
+
const e = Math.pow(dampingRatio, 2) * Math.pow(undampedFreq, 2) * duration;
|
|
3178
|
+
const f = Math.exp(-delta);
|
|
3179
|
+
const g = calcAngularFreq(Math.pow(undampedFreq, 2), dampingRatio);
|
|
3180
|
+
const factor = -envelope(undampedFreq) + safeMin > 0 ? -1 : 1;
|
|
3181
|
+
return (factor * ((d - e) * f)) / g;
|
|
3182
|
+
};
|
|
3183
|
+
}
|
|
3184
|
+
else {
|
|
3185
|
+
/**
|
|
3186
|
+
* Critically-damped spring
|
|
3187
|
+
*/
|
|
3188
|
+
envelope = (undampedFreq) => {
|
|
3189
|
+
const a = Math.exp(-undampedFreq * duration);
|
|
3190
|
+
const b = (undampedFreq - velocity) * duration + 1;
|
|
3191
|
+
return -safeMin + a * b;
|
|
3192
|
+
};
|
|
3193
|
+
derivative = (undampedFreq) => {
|
|
3194
|
+
const a = Math.exp(-undampedFreq * duration);
|
|
3195
|
+
const b = (velocity - undampedFreq) * (duration * duration);
|
|
3196
|
+
return a * b;
|
|
3197
|
+
};
|
|
3198
|
+
}
|
|
3199
|
+
const initialGuess = 5 / duration;
|
|
3200
|
+
const undampedFreq = approximateRoot(envelope, derivative, initialGuess);
|
|
3201
|
+
duration = duration * 1000;
|
|
3202
|
+
if (isNaN(undampedFreq)) {
|
|
3203
|
+
return {
|
|
3204
|
+
stiffness: 100,
|
|
3205
|
+
damping: 10,
|
|
3206
|
+
duration,
|
|
3207
|
+
};
|
|
3208
|
+
}
|
|
3209
|
+
else {
|
|
3210
|
+
const stiffness = Math.pow(undampedFreq, 2) * mass;
|
|
3211
|
+
return {
|
|
3212
|
+
stiffness,
|
|
3213
|
+
damping: dampingRatio * 2 * Math.sqrt(mass * stiffness),
|
|
3214
|
+
duration,
|
|
3215
|
+
};
|
|
3216
|
+
}
|
|
3217
|
+
}
|
|
3218
|
+
const rootIterations = 12;
|
|
3219
|
+
function approximateRoot(envelope, derivative, initialGuess) {
|
|
3220
|
+
let result = initialGuess;
|
|
3221
|
+
for (let i = 1; i < rootIterations; i++) {
|
|
3222
|
+
result = result - envelope(result) / derivative(result);
|
|
3223
|
+
}
|
|
3224
|
+
return result;
|
|
3225
|
+
}
|
|
3226
|
+
function calcAngularFreq(undampedFreq, dampingRatio) {
|
|
3227
|
+
return undampedFreq * Math.sqrt(1 - dampingRatio * dampingRatio);
|
|
3228
|
+
}
|
|
3229
|
+
|
|
3230
|
+
const durationKeys = ["duration", "bounce"];
|
|
3231
|
+
const physicsKeys = ["stiffness", "damping", "mass"];
|
|
3232
|
+
function isSpringType(options, keys) {
|
|
3233
|
+
return keys.some((key) => options[key] !== undefined);
|
|
3234
|
+
}
|
|
3235
|
+
function getSpringOptions(options) {
|
|
3236
|
+
let springOptions = {
|
|
3237
|
+
velocity: 0.0,
|
|
3238
|
+
stiffness: 100,
|
|
3239
|
+
damping: 10,
|
|
3240
|
+
mass: 1.0,
|
|
3241
|
+
isResolvedFromDuration: false,
|
|
3242
|
+
...options,
|
|
3243
|
+
};
|
|
3244
|
+
// stiffness/damping/mass overrides duration/bounce
|
|
3245
|
+
if (!isSpringType(options, physicsKeys) &&
|
|
3246
|
+
isSpringType(options, durationKeys)) {
|
|
3247
|
+
const derived = findSpring(options);
|
|
3248
|
+
springOptions = {
|
|
3249
|
+
...springOptions,
|
|
3250
|
+
...derived,
|
|
3251
|
+
velocity: 0.0,
|
|
3252
|
+
mass: 1.0,
|
|
3253
|
+
};
|
|
3254
|
+
springOptions.isResolvedFromDuration = true;
|
|
3255
|
+
}
|
|
3256
|
+
return springOptions;
|
|
3257
|
+
}
|
|
3258
|
+
const velocitySampleDuration = 5;
|
|
3259
|
+
/**
|
|
3260
|
+
* This is based on the spring implementation of Wobble https://github.com/skevy/wobble
|
|
3261
|
+
*/
|
|
3262
|
+
function spring({ keyframes, restDelta, restSpeed, ...options }) {
|
|
3263
|
+
let origin = keyframes[0];
|
|
3264
|
+
let target = keyframes[keyframes.length - 1];
|
|
3265
|
+
/**
|
|
3266
|
+
* This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
|
|
3267
|
+
* to reduce GC during animation.
|
|
3268
|
+
*/
|
|
3269
|
+
const state = { done: false, value: origin };
|
|
3270
|
+
const { stiffness, damping, mass, velocity, duration, isResolvedFromDuration, } = getSpringOptions(options);
|
|
3271
|
+
let resolveSpring = zero;
|
|
3272
|
+
let initialVelocity = velocity ? -(velocity / 1000) : 0.0;
|
|
3273
|
+
const dampingRatio = damping / (2 * Math.sqrt(stiffness * mass));
|
|
3274
|
+
function createSpring() {
|
|
3275
|
+
const initialDelta = target - origin;
|
|
3276
|
+
const undampedAngularFreq = Math.sqrt(stiffness / mass) / 1000;
|
|
3277
|
+
/**
|
|
3278
|
+
* If we're working on a granular scale, use smaller defaults for determining
|
|
3279
|
+
* when the spring is finished.
|
|
3280
|
+
*
|
|
3281
|
+
* These defaults have been selected emprically based on what strikes a good
|
|
3282
|
+
* ratio between feeling good and finishing as soon as changes are imperceptible.
|
|
3283
|
+
*/
|
|
3284
|
+
const isGranularScale = Math.abs(initialDelta) < 5;
|
|
3285
|
+
restSpeed || (restSpeed = isGranularScale ? 0.01 : 2);
|
|
3286
|
+
restDelta || (restDelta = isGranularScale ? 0.005 : 0.5);
|
|
3287
|
+
if (dampingRatio < 1) {
|
|
3288
|
+
const angularFreq = calcAngularFreq(undampedAngularFreq, dampingRatio);
|
|
3289
|
+
// Underdamped spring
|
|
3290
|
+
resolveSpring = (t) => {
|
|
3291
|
+
const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
|
|
3292
|
+
return (target -
|
|
3293
|
+
envelope *
|
|
3294
|
+
(((initialVelocity +
|
|
3295
|
+
dampingRatio * undampedAngularFreq * initialDelta) /
|
|
3296
|
+
angularFreq) *
|
|
3297
|
+
Math.sin(angularFreq * t) +
|
|
3298
|
+
initialDelta * Math.cos(angularFreq * t)));
|
|
3299
|
+
};
|
|
3300
|
+
}
|
|
3301
|
+
else if (dampingRatio === 1) {
|
|
3302
|
+
// Critically damped spring
|
|
3303
|
+
resolveSpring = (t) => target -
|
|
3304
|
+
Math.exp(-undampedAngularFreq * t) *
|
|
3305
|
+
(initialDelta +
|
|
3306
|
+
(initialVelocity + undampedAngularFreq * initialDelta) *
|
|
3307
|
+
t);
|
|
3308
|
+
}
|
|
3309
|
+
else {
|
|
3310
|
+
// Overdamped spring
|
|
3311
|
+
const dampedAngularFreq = undampedAngularFreq * Math.sqrt(dampingRatio * dampingRatio - 1);
|
|
3312
|
+
resolveSpring = (t) => {
|
|
3313
|
+
const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
|
|
3314
|
+
// When performing sinh or cosh values can hit Infinity so we cap them here
|
|
3315
|
+
const freqForT = Math.min(dampedAngularFreq * t, 300);
|
|
3316
|
+
return (target -
|
|
3317
|
+
(envelope *
|
|
3318
|
+
((initialVelocity +
|
|
3319
|
+
dampingRatio * undampedAngularFreq * initialDelta) *
|
|
3320
|
+
Math.sinh(freqForT) +
|
|
3321
|
+
dampedAngularFreq *
|
|
3322
|
+
initialDelta *
|
|
3323
|
+
Math.cosh(freqForT))) /
|
|
3324
|
+
dampedAngularFreq);
|
|
3325
|
+
};
|
|
3326
|
+
}
|
|
3327
|
+
}
|
|
3328
|
+
createSpring();
|
|
3329
|
+
return {
|
|
3330
|
+
next: (t) => {
|
|
3331
|
+
const current = resolveSpring(t);
|
|
3332
|
+
if (!isResolvedFromDuration) {
|
|
3333
|
+
let currentVelocity = initialVelocity;
|
|
3334
|
+
if (t !== 0) {
|
|
3335
|
+
/**
|
|
3336
|
+
* We only need to calculate velocity for under-damped springs
|
|
3337
|
+
* as over- and critically-damped springs can't overshoot, so
|
|
3338
|
+
* checking only for displacement is enough.
|
|
3339
|
+
*/
|
|
3340
|
+
if (dampingRatio < 1) {
|
|
3341
|
+
const prevT = Math.max(0, t - velocitySampleDuration);
|
|
3342
|
+
currentVelocity = velocityPerSecond(current - resolveSpring(prevT), t - prevT);
|
|
3343
|
+
}
|
|
3344
|
+
else {
|
|
3345
|
+
currentVelocity = 0;
|
|
3346
|
+
}
|
|
3347
|
+
}
|
|
3348
|
+
const isBelowVelocityThreshold = Math.abs(currentVelocity) <= restSpeed;
|
|
3349
|
+
const isBelowDisplacementThreshold = Math.abs(target - current) <= restDelta;
|
|
3350
|
+
state.done =
|
|
3351
|
+
isBelowVelocityThreshold && isBelowDisplacementThreshold;
|
|
3352
|
+
}
|
|
3353
|
+
else {
|
|
3354
|
+
state.done = t >= duration;
|
|
3355
|
+
}
|
|
3356
|
+
state.value = state.done ? target : current;
|
|
3357
|
+
return state;
|
|
3358
|
+
},
|
|
3359
|
+
flipTarget: () => {
|
|
3360
|
+
initialVelocity = -initialVelocity;
|
|
3361
|
+
[origin, target] = [target, origin];
|
|
3362
|
+
createSpring();
|
|
3363
|
+
},
|
|
3364
|
+
};
|
|
3365
|
+
}
|
|
3366
|
+
spring.needsInterpolation = (a, b) => typeof a === "string" || typeof b === "string";
|
|
3367
|
+
const zero = (_t) => 0;
|
|
3368
|
+
|
|
3369
|
+
function decay({
|
|
3370
|
+
/**
|
|
3371
|
+
* The decay animation dynamically calculates an end of the animation
|
|
3372
|
+
* based on the initial keyframe, so we only need to define a single keyframe
|
|
3373
|
+
* as default.
|
|
3374
|
+
*/
|
|
3375
|
+
keyframes = [0], velocity = 0, power = 0.8, timeConstant = 350, restDelta = 0.5, modifyTarget, }) {
|
|
3376
|
+
const origin = keyframes[0];
|
|
3377
|
+
/**
|
|
3378
|
+
* This is the Iterator-spec return value. We ensure it's mutable rather than using a generator
|
|
3379
|
+
* to reduce GC during animation.
|
|
3380
|
+
*/
|
|
3381
|
+
const state = { done: false, value: origin };
|
|
3382
|
+
let amplitude = power * velocity;
|
|
3383
|
+
const ideal = origin + amplitude;
|
|
3384
|
+
const target = modifyTarget === undefined ? ideal : modifyTarget(ideal);
|
|
3385
|
+
/**
|
|
3386
|
+
* If the target has changed we need to re-calculate the amplitude, otherwise
|
|
3387
|
+
* the animation will start from the wrong position.
|
|
3388
|
+
*/
|
|
3389
|
+
if (target !== ideal)
|
|
3390
|
+
amplitude = target - origin;
|
|
3391
|
+
return {
|
|
3392
|
+
next: (t) => {
|
|
3393
|
+
const delta = -amplitude * Math.exp(-t / timeConstant);
|
|
3394
|
+
state.done = !(delta > restDelta || delta < -restDelta);
|
|
3395
|
+
state.value = state.done ? target : target + delta;
|
|
3396
|
+
return state;
|
|
3397
|
+
},
|
|
3398
|
+
flipTarget: () => { },
|
|
3399
|
+
};
|
|
3400
|
+
}
|
|
3401
|
+
|
|
3402
|
+
const types = {
|
|
3403
|
+
decay,
|
|
3404
|
+
keyframes: keyframes,
|
|
3405
|
+
tween: keyframes,
|
|
3406
|
+
spring,
|
|
3407
|
+
};
|
|
3408
|
+
function loopElapsed(elapsed, duration, delay = 0) {
|
|
3409
|
+
return elapsed - duration - delay;
|
|
3410
|
+
}
|
|
3411
|
+
function reverseElapsed(elapsed, duration = 0, delay = 0, isForwardPlayback = true) {
|
|
3412
|
+
return isForwardPlayback
|
|
3413
|
+
? loopElapsed(duration + -elapsed, duration, delay)
|
|
3414
|
+
: duration - (elapsed - duration) + delay;
|
|
3415
|
+
}
|
|
3416
|
+
function hasRepeatDelayElapsed(elapsed, duration, delay, isForwardPlayback) {
|
|
3417
|
+
return isForwardPlayback ? elapsed >= duration + delay : elapsed <= -delay;
|
|
3418
|
+
}
|
|
3419
|
+
const framesync = (update) => {
|
|
3420
|
+
const passTimestamp = ({ delta }) => update(delta);
|
|
3421
|
+
return {
|
|
3422
|
+
start: () => sync.update(passTimestamp, true),
|
|
3423
|
+
stop: () => cancelSync.update(passTimestamp),
|
|
3424
|
+
};
|
|
3425
|
+
};
|
|
3426
|
+
function animateValue({ duration, driver = framesync, elapsed = 0, repeat: repeatMax = 0, repeatType = "loop", repeatDelay = 0, keyframes: keyframes$1, autoplay = true, onPlay, onStop, onComplete, onRepeat, onUpdate, type = "keyframes", ...options }) {
|
|
3427
|
+
const initialElapsed = elapsed;
|
|
3428
|
+
let driverControls;
|
|
3429
|
+
let repeatCount = 0;
|
|
3430
|
+
let computedDuration = duration;
|
|
3431
|
+
let isComplete = false;
|
|
3432
|
+
let isForwardPlayback = true;
|
|
3433
|
+
let interpolateFromNumber;
|
|
3434
|
+
const animator = types[keyframes$1.length > 2 ? "keyframes" : type] || keyframes;
|
|
3435
|
+
const origin = keyframes$1[0];
|
|
3436
|
+
const target = keyframes$1[keyframes$1.length - 1];
|
|
3437
|
+
let state = { done: false, value: origin };
|
|
3438
|
+
/**
|
|
3439
|
+
* If this value needs interpolation (ie is non-numerical), set up an interpolator.
|
|
3440
|
+
* TODO: Keyframes animation also performs this step. This could be removed so it only happens here.
|
|
3441
|
+
*/
|
|
3442
|
+
const { needsInterpolation } = animator;
|
|
3443
|
+
if (needsInterpolation && needsInterpolation(origin, target)) {
|
|
3444
|
+
interpolateFromNumber = interpolate([0, 100], [origin, target], {
|
|
3445
|
+
clamp: false,
|
|
3446
|
+
});
|
|
3447
|
+
keyframes$1 = [0, 100];
|
|
3448
|
+
}
|
|
3449
|
+
const animation = animator({
|
|
3450
|
+
...options,
|
|
3451
|
+
duration,
|
|
3452
|
+
keyframes: keyframes$1,
|
|
3453
|
+
});
|
|
3454
|
+
function repeat() {
|
|
3455
|
+
repeatCount++;
|
|
3456
|
+
if (repeatType === "reverse") {
|
|
3457
|
+
isForwardPlayback = repeatCount % 2 === 0;
|
|
3458
|
+
elapsed = reverseElapsed(elapsed, computedDuration, repeatDelay, isForwardPlayback);
|
|
3459
|
+
}
|
|
3460
|
+
else {
|
|
3461
|
+
elapsed = loopElapsed(elapsed, computedDuration, repeatDelay);
|
|
3462
|
+
if (repeatType === "mirror")
|
|
3463
|
+
animation.flipTarget();
|
|
3464
|
+
}
|
|
3465
|
+
isComplete = false;
|
|
3466
|
+
onRepeat && onRepeat();
|
|
3467
|
+
}
|
|
3468
|
+
function complete() {
|
|
3469
|
+
driverControls && driverControls.stop();
|
|
3470
|
+
onComplete && onComplete();
|
|
3471
|
+
}
|
|
3472
|
+
function update(delta) {
|
|
3473
|
+
if (!isForwardPlayback)
|
|
3474
|
+
delta = -delta;
|
|
3475
|
+
elapsed += delta;
|
|
3476
|
+
if (!isComplete) {
|
|
3477
|
+
state = animation.next(Math.max(0, elapsed));
|
|
3478
|
+
if (interpolateFromNumber)
|
|
3479
|
+
state.value = interpolateFromNumber(state.value);
|
|
3480
|
+
isComplete = isForwardPlayback ? state.done : elapsed <= 0;
|
|
3481
|
+
}
|
|
3482
|
+
onUpdate && onUpdate(state.value);
|
|
3483
|
+
if (isComplete) {
|
|
3484
|
+
if (repeatCount === 0) {
|
|
3485
|
+
computedDuration =
|
|
3486
|
+
computedDuration !== undefined ? computedDuration : elapsed;
|
|
3487
|
+
}
|
|
3488
|
+
if (repeatCount < repeatMax) {
|
|
3489
|
+
hasRepeatDelayElapsed(elapsed, computedDuration, repeatDelay, isForwardPlayback) && repeat();
|
|
3490
|
+
}
|
|
3491
|
+
else {
|
|
3492
|
+
complete();
|
|
3493
|
+
}
|
|
3494
|
+
}
|
|
3495
|
+
}
|
|
3496
|
+
function play() {
|
|
3497
|
+
onPlay && onPlay();
|
|
3498
|
+
driverControls = driver(update);
|
|
3499
|
+
driverControls.start();
|
|
3500
|
+
}
|
|
3501
|
+
autoplay && play();
|
|
3502
|
+
return {
|
|
3503
|
+
stop: () => {
|
|
3504
|
+
onStop && onStop();
|
|
3505
|
+
driverControls && driverControls.stop();
|
|
3506
|
+
},
|
|
3507
|
+
/**
|
|
3508
|
+
* Set the current time of the animation. This is purposefully
|
|
3509
|
+
* mirroring the WAAPI animation API to make them interchanagable.
|
|
3510
|
+
* Going forward this file should be ported more towards
|
|
3511
|
+
* https://github.com/motiondivision/motionone/blob/main/packages/animation/src/Animation.ts
|
|
3512
|
+
* Which behaviourally adheres to WAAPI as far as possible.
|
|
3513
|
+
*
|
|
3514
|
+
* WARNING: This is not safe to use for most animations. We currently
|
|
3515
|
+
* only use it for handoff from WAAPI within Framer.
|
|
3516
|
+
*
|
|
3517
|
+
* This animation function consumes time every frame rather than being sampled for time.
|
|
3518
|
+
* So the sample() method performs some headless frames to ensure
|
|
3519
|
+
* repeats are handled correctly. Ideally in the future we will replace
|
|
3520
|
+
* that method with this, once repeat calculations are pure.
|
|
3521
|
+
*/
|
|
3522
|
+
set currentTime(t) {
|
|
3523
|
+
elapsed = initialElapsed;
|
|
3524
|
+
update(t);
|
|
3525
|
+
},
|
|
3526
|
+
/**
|
|
3527
|
+
* animate() can't yet be sampled for time, instead it
|
|
3528
|
+
* consumes time. So to sample it we have to run a low
|
|
3529
|
+
* temporal-resolution version.
|
|
3530
|
+
*
|
|
3531
|
+
* isControlled should be set to true if sample is being run within
|
|
3532
|
+
* a loop. This indicates that we're not arbitrarily sampling
|
|
3533
|
+
* the animation but running it one step after another. Therefore
|
|
3534
|
+
* we don't need to run a low-res version here. This is a stop-gap
|
|
3535
|
+
* until a rewrite can sample for time.
|
|
3536
|
+
*/
|
|
3537
|
+
sample: (t, isControlled = false) => {
|
|
3538
|
+
elapsed = initialElapsed;
|
|
3539
|
+
if (isControlled) {
|
|
3540
|
+
update(t);
|
|
3541
|
+
return state;
|
|
3542
|
+
}
|
|
3543
|
+
const sampleResolution = duration && typeof duration === "number"
|
|
3544
|
+
? Math.max(duration * 0.5, 50)
|
|
3545
|
+
: 50;
|
|
3546
|
+
let sampleElapsed = 0;
|
|
3547
|
+
update(0);
|
|
3548
|
+
while (sampleElapsed <= t) {
|
|
3549
|
+
const remaining = t - sampleElapsed;
|
|
3550
|
+
update(Math.min(remaining, sampleResolution));
|
|
3551
|
+
sampleElapsed += sampleResolution;
|
|
3552
|
+
}
|
|
3553
|
+
return state;
|
|
3554
|
+
},
|
|
3555
|
+
};
|
|
3556
|
+
}
|
|
3557
|
+
|
|
3558
|
+
function isWaapiSupportedEasing(easing) {
|
|
3559
|
+
return (!easing || // Default easing
|
|
3560
|
+
Array.isArray(easing) || // Bezier curve
|
|
3561
|
+
(typeof easing === "string" && supportedWaapiEasing[easing]));
|
|
3562
|
+
}
|
|
3563
|
+
const cubicBezierAsString = ([a, b, c, d]) => `cubic-bezier(${a}, ${b}, ${c}, ${d})`;
|
|
3564
|
+
const supportedWaapiEasing = {
|
|
3565
|
+
linear: "linear",
|
|
3566
|
+
ease: "ease",
|
|
3567
|
+
easeIn: "ease-in",
|
|
3568
|
+
easeOut: "ease-out",
|
|
3569
|
+
easeInOut: "ease-in-out",
|
|
3570
|
+
circIn: cubicBezierAsString([0, 0.65, 0.55, 1]),
|
|
3571
|
+
circOut: cubicBezierAsString([0.55, 0, 1, 0.45]),
|
|
3572
|
+
backIn: cubicBezierAsString([0.31, 0.01, 0.66, -0.59]),
|
|
3573
|
+
backOut: cubicBezierAsString([0.33, 1.53, 0.69, 0.99]),
|
|
3574
|
+
};
|
|
3575
|
+
function mapEasingToNativeEasing(easing) {
|
|
3576
|
+
if (!easing)
|
|
3577
|
+
return undefined;
|
|
3578
|
+
return Array.isArray(easing)
|
|
3579
|
+
? cubicBezierAsString(easing)
|
|
3580
|
+
: supportedWaapiEasing[easing];
|
|
3581
|
+
}
|
|
3582
|
+
|
|
3583
|
+
function animateStyle(element, valueName, keyframes, { delay = 0, duration, repeat = 0, repeatType = "loop", ease, times, } = {}) {
|
|
3584
|
+
const keyframeOptions = { [valueName]: keyframes };
|
|
3585
|
+
if (times)
|
|
3586
|
+
keyframeOptions.offset = times;
|
|
3587
|
+
return element.animate(keyframeOptions, {
|
|
3588
|
+
delay,
|
|
3589
|
+
duration,
|
|
3590
|
+
easing: mapEasingToNativeEasing(ease),
|
|
3591
|
+
fill: "both",
|
|
3592
|
+
iterations: repeat + 1,
|
|
3593
|
+
direction: repeatType === "reverse" ? "alternate" : "normal",
|
|
3594
|
+
});
|
|
3595
|
+
}
|
|
3596
|
+
|
|
3597
|
+
const featureTests = {
|
|
3598
|
+
waapi: () => Object.hasOwnProperty.call(Element.prototype, "animate"),
|
|
3599
|
+
};
|
|
3600
|
+
const results = {};
|
|
3601
|
+
const supports = {};
|
|
3602
|
+
/**
|
|
3603
|
+
* Generate features tests that cache their results.
|
|
3604
|
+
*/
|
|
3605
|
+
for (const key in featureTests) {
|
|
3606
|
+
supports[key] = () => {
|
|
3607
|
+
if (results[key] === undefined)
|
|
3608
|
+
results[key] = featureTests[key]();
|
|
3609
|
+
return results[key];
|
|
3610
|
+
};
|
|
3611
|
+
}
|
|
3612
|
+
|
|
3613
|
+
function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }) {
|
|
3614
|
+
const index = repeat && repeatType !== "loop" && repeat % 2 === 1
|
|
3615
|
+
? 0
|
|
3616
|
+
: keyframes.length - 1;
|
|
3617
|
+
return keyframes[index];
|
|
3618
|
+
}
|
|
3619
|
+
|
|
3620
|
+
/**
|
|
3621
|
+
* A list of values that can be hardware-accelerated.
|
|
3622
|
+
*/
|
|
3623
|
+
const acceleratedValues = new Set([
|
|
3624
|
+
"opacity",
|
|
3625
|
+
"clipPath",
|
|
3626
|
+
"filter",
|
|
3627
|
+
"transform",
|
|
3628
|
+
"backgroundColor",
|
|
3629
|
+
]);
|
|
3630
|
+
/**
|
|
3631
|
+
* 10ms is chosen here as it strikes a balance between smooth
|
|
3632
|
+
* results (more than one keyframe per frame at 60fps) and
|
|
3633
|
+
* keyframe quantity.
|
|
3634
|
+
*/
|
|
3635
|
+
const sampleDelta = 10; //ms
|
|
3636
|
+
const requiresPregeneratedKeyframes = (valueName, options) => options.type === "spring" ||
|
|
3637
|
+
valueName === "backgroundColor" ||
|
|
3638
|
+
!isWaapiSupportedEasing(options.ease);
|
|
3639
|
+
function createAcceleratedAnimation(value, valueName, { onUpdate, onComplete, ...options }) {
|
|
3640
|
+
const canAccelerateAnimation = supports.waapi() &&
|
|
3641
|
+
acceleratedValues.has(valueName) &&
|
|
3642
|
+
!options.repeatDelay &&
|
|
3643
|
+
options.repeatType !== "mirror" &&
|
|
3644
|
+
options.damping !== 0;
|
|
3645
|
+
if (!canAccelerateAnimation)
|
|
3646
|
+
return false;
|
|
3647
|
+
let { keyframes, duration = 300, elapsed = 0, ease } = options;
|
|
3648
|
+
/**
|
|
3649
|
+
* If this animation needs pre-generated keyframes then generate.
|
|
3650
|
+
*/
|
|
3651
|
+
if (requiresPregeneratedKeyframes(valueName, options)) {
|
|
3652
|
+
const sampleAnimation = animateValue({
|
|
3653
|
+
...options,
|
|
3654
|
+
repeat: 0,
|
|
3655
|
+
elapsed: 0,
|
|
3656
|
+
});
|
|
3657
|
+
let state = { done: false, value: keyframes[0] };
|
|
3658
|
+
const pregeneratedKeyframes = [];
|
|
3659
|
+
/**
|
|
3660
|
+
* Bail after 20 seconds of pre-generated keyframes as it's likely
|
|
3661
|
+
* we're heading for an infinite loop.
|
|
3662
|
+
*/
|
|
3663
|
+
let t = 0;
|
|
3664
|
+
while (!state.done && t < 20000) {
|
|
3665
|
+
state = sampleAnimation.sample(t, true);
|
|
3666
|
+
pregeneratedKeyframes.push(state.value);
|
|
3667
|
+
t += sampleDelta;
|
|
3668
|
+
}
|
|
3669
|
+
keyframes = pregeneratedKeyframes;
|
|
3670
|
+
duration = t - sampleDelta;
|
|
3671
|
+
ease = "linear";
|
|
3672
|
+
}
|
|
3673
|
+
const animation = animateStyle(value.owner.current, valueName, keyframes, {
|
|
3674
|
+
...options,
|
|
3675
|
+
delay: -elapsed,
|
|
3676
|
+
duration,
|
|
3677
|
+
/**
|
|
3678
|
+
* This function is currently not called if ease is provided
|
|
3679
|
+
* as a function so the cast is safe.
|
|
3680
|
+
*
|
|
3681
|
+
* However it would be possible for a future refinement to port
|
|
3682
|
+
* in easing pregeneration from Motion One for browsers that
|
|
3683
|
+
* support the upcoming `linear()` easing function.
|
|
3684
|
+
*/
|
|
3685
|
+
ease: ease,
|
|
3686
|
+
});
|
|
3687
|
+
/**
|
|
3688
|
+
* Prefer the `onfinish` prop as it's more widely supported than
|
|
3689
|
+
* the `finished` promise.
|
|
3690
|
+
*
|
|
3691
|
+
* Here, we synchronously set the provided MotionValue to the end
|
|
3692
|
+
* keyframe. If we didn't, when the WAAPI animation is finished it would
|
|
3693
|
+
* be removed from the element which would then revert to its old styles.
|
|
3694
|
+
*/
|
|
3695
|
+
animation.onfinish = () => {
|
|
3696
|
+
value.set(getFinalKeyframe(keyframes, options));
|
|
3697
|
+
sync.update(() => animation.cancel());
|
|
3698
|
+
onComplete && onComplete();
|
|
3699
|
+
};
|
|
3700
|
+
/**
|
|
3701
|
+
* Animation interrupt callback.
|
|
3702
|
+
*/
|
|
3703
|
+
return {
|
|
3704
|
+
get currentTime() {
|
|
3705
|
+
return animation.currentTime || 0;
|
|
3706
|
+
},
|
|
3707
|
+
set currentTime(t) {
|
|
3708
|
+
animation.currentTime = t;
|
|
3709
|
+
},
|
|
3710
|
+
stop: () => {
|
|
3711
|
+
/**
|
|
3712
|
+
* WAAPI doesn't natively have any interruption capabilities.
|
|
3713
|
+
*
|
|
3714
|
+
* Rather than read commited styles back out of the DOM, we can
|
|
3715
|
+
* create a renderless JS animation and sample it twice to calculate
|
|
3716
|
+
* its current value, "previous" value, and therefore allow
|
|
3717
|
+
* Motion to calculate velocity for any subsequent animation.
|
|
3718
|
+
*/
|
|
3719
|
+
const { currentTime } = animation;
|
|
3720
|
+
if (currentTime) {
|
|
3721
|
+
const sampleAnimation = animateValue({
|
|
3722
|
+
...options,
|
|
3723
|
+
autoplay: false,
|
|
3724
|
+
});
|
|
3725
|
+
value.setWithVelocity(sampleAnimation.sample(currentTime - sampleDelta).value, sampleAnimation.sample(currentTime).value, sampleDelta);
|
|
3726
|
+
}
|
|
3727
|
+
sync.update(() => animation.cancel());
|
|
3728
|
+
},
|
|
3729
|
+
};
|
|
3730
|
+
}
|
|
3731
|
+
|
|
3732
|
+
/**
|
|
3733
|
+
* Timeout defined in ms
|
|
3734
|
+
*/
|
|
3735
|
+
function delay(callback, timeout) {
|
|
3736
|
+
const start = performance.now();
|
|
3737
|
+
const checkElapsed = ({ timestamp }) => {
|
|
3738
|
+
const elapsed = timestamp - start;
|
|
3739
|
+
if (elapsed >= timeout) {
|
|
3740
|
+
cancelSync.read(checkElapsed);
|
|
3741
|
+
callback(elapsed - timeout);
|
|
3742
|
+
}
|
|
3743
|
+
};
|
|
3744
|
+
sync.read(checkElapsed, true);
|
|
3745
|
+
return () => cancelSync.read(checkElapsed);
|
|
3746
|
+
}
|
|
3747
|
+
|
|
3748
|
+
function createInstantAnimation({ keyframes, elapsed, onUpdate, onComplete, }) {
|
|
3749
|
+
const setValue = () => {
|
|
3750
|
+
onUpdate && onUpdate(keyframes[keyframes.length - 1]);
|
|
3751
|
+
onComplete && onComplete();
|
|
3752
|
+
};
|
|
3753
|
+
return elapsed ? { stop: delay(setValue, -elapsed) } : setValue();
|
|
3754
|
+
}
|
|
3755
|
+
|
|
3756
|
+
function inertia({ keyframes, velocity = 0, min, max, power = 0.8, timeConstant = 750, bounceStiffness = 500, bounceDamping = 10, restDelta = 1, modifyTarget, driver, onUpdate, onComplete, onStop, }) {
|
|
3757
|
+
const origin = keyframes[0];
|
|
3758
|
+
let currentAnimation;
|
|
3759
|
+
function isOutOfBounds(v) {
|
|
3760
|
+
return (min !== undefined && v < min) || (max !== undefined && v > max);
|
|
3761
|
+
}
|
|
3762
|
+
function findNearestBoundary(v) {
|
|
3763
|
+
if (min === undefined)
|
|
3764
|
+
return max;
|
|
3765
|
+
if (max === undefined)
|
|
3766
|
+
return min;
|
|
3767
|
+
return Math.abs(min - v) < Math.abs(max - v) ? min : max;
|
|
3768
|
+
}
|
|
3769
|
+
function startAnimation(options) {
|
|
3770
|
+
currentAnimation && currentAnimation.stop();
|
|
3771
|
+
currentAnimation = animateValue({
|
|
3772
|
+
keyframes: [0, 1],
|
|
3773
|
+
velocity: 0,
|
|
3774
|
+
...options,
|
|
3775
|
+
driver,
|
|
3776
|
+
onUpdate: (v) => {
|
|
3777
|
+
onUpdate && onUpdate(v);
|
|
3778
|
+
options.onUpdate && options.onUpdate(v);
|
|
3779
|
+
},
|
|
3780
|
+
onComplete,
|
|
3781
|
+
onStop,
|
|
3782
|
+
});
|
|
3783
|
+
}
|
|
3784
|
+
function startSpring(options) {
|
|
3785
|
+
startAnimation({
|
|
3786
|
+
type: "spring",
|
|
3787
|
+
stiffness: bounceStiffness,
|
|
3788
|
+
damping: bounceDamping,
|
|
3789
|
+
restDelta,
|
|
3790
|
+
...options,
|
|
3791
|
+
});
|
|
3792
|
+
}
|
|
3793
|
+
if (isOutOfBounds(origin)) {
|
|
3794
|
+
// Start the animation with spring if outside the defined boundaries
|
|
3795
|
+
startSpring({
|
|
3796
|
+
velocity,
|
|
3797
|
+
keyframes: [origin, findNearestBoundary(origin)],
|
|
3798
|
+
});
|
|
3799
|
+
}
|
|
3800
|
+
else {
|
|
3801
|
+
/**
|
|
3802
|
+
* Or if the value is out of bounds, simulate the inertia movement
|
|
3803
|
+
* with the decay animation.
|
|
3804
|
+
*
|
|
3805
|
+
* Pre-calculate the target so we can detect if it's out-of-bounds.
|
|
3806
|
+
* If it is, we want to check per frame when to switch to a spring
|
|
3807
|
+
* animation
|
|
3808
|
+
*/
|
|
3809
|
+
let target = power * velocity + origin;
|
|
3810
|
+
if (typeof modifyTarget !== "undefined")
|
|
3811
|
+
target = modifyTarget(target);
|
|
3812
|
+
const boundary = findNearestBoundary(target);
|
|
3813
|
+
const heading = boundary === min ? -1 : 1;
|
|
3814
|
+
let prev;
|
|
3815
|
+
let current;
|
|
3816
|
+
const checkBoundary = (v) => {
|
|
3817
|
+
prev = current;
|
|
3818
|
+
current = v;
|
|
3819
|
+
velocity = velocityPerSecond(v - prev, frameData.delta);
|
|
3820
|
+
if ((heading === 1 && v > boundary) ||
|
|
3821
|
+
(heading === -1 && v < boundary)) {
|
|
3822
|
+
startSpring({ keyframes: [v, boundary], velocity });
|
|
3823
|
+
}
|
|
3824
|
+
};
|
|
3825
|
+
startAnimation({
|
|
3826
|
+
type: "decay",
|
|
3827
|
+
keyframes: [origin, 0],
|
|
3828
|
+
velocity,
|
|
3829
|
+
timeConstant,
|
|
3830
|
+
power,
|
|
3831
|
+
restDelta,
|
|
3832
|
+
modifyTarget,
|
|
3833
|
+
onUpdate: isOutOfBounds(target) ? checkBoundary : undefined,
|
|
3834
|
+
});
|
|
3835
|
+
}
|
|
3836
|
+
return {
|
|
3837
|
+
stop: () => currentAnimation && currentAnimation.stop(),
|
|
3838
|
+
};
|
|
3839
|
+
}
|
|
3840
|
+
|
|
3841
|
+
const underDampedSpring = {
|
|
3842
|
+
type: "spring",
|
|
3843
|
+
stiffness: 500,
|
|
3844
|
+
damping: 25,
|
|
3845
|
+
restSpeed: 10,
|
|
3846
|
+
};
|
|
3847
|
+
const criticallyDampedSpring = (target) => ({
|
|
3848
|
+
type: "spring",
|
|
3849
|
+
stiffness: 550,
|
|
3850
|
+
damping: target === 0 ? 2 * Math.sqrt(550) : 30,
|
|
3851
|
+
restSpeed: 10,
|
|
3852
|
+
});
|
|
3853
|
+
const keyframesTransition = {
|
|
3854
|
+
type: "keyframes",
|
|
3855
|
+
duration: 0.8,
|
|
3856
|
+
};
|
|
3857
|
+
/**
|
|
3858
|
+
* Default easing curve is a slightly shallower version of
|
|
3859
|
+
* the default browser easing curve.
|
|
3860
|
+
*/
|
|
3861
|
+
const ease = {
|
|
3862
|
+
type: "keyframes",
|
|
3863
|
+
ease: [0.25, 0.1, 0.35, 1],
|
|
3864
|
+
duration: 0.3,
|
|
3865
|
+
};
|
|
3866
|
+
const getDefaultTransition = (valueKey, { keyframes }) => {
|
|
3867
|
+
if (keyframes.length > 2) {
|
|
3868
|
+
return keyframesTransition;
|
|
3869
|
+
}
|
|
3870
|
+
else if (transformProps.has(valueKey)) {
|
|
3871
|
+
return valueKey.startsWith("scale")
|
|
3872
|
+
? criticallyDampedSpring(keyframes[1])
|
|
3873
|
+
: underDampedSpring;
|
|
3874
|
+
}
|
|
3875
|
+
return ease;
|
|
3876
|
+
};
|
|
3877
|
+
|
|
3878
|
+
/**
|
|
3879
|
+
* Check if a value is animatable. Examples:
|
|
3880
|
+
*
|
|
3881
|
+
* ✅: 100, "100px", "#fff"
|
|
3882
|
+
* ❌: "block", "url(2.jpg)"
|
|
3883
|
+
* @param value
|
|
3884
|
+
*
|
|
3885
|
+
* @internal
|
|
3886
|
+
*/
|
|
3887
|
+
const isAnimatable = (key, value) => {
|
|
3888
|
+
// If the list of keys tat might be non-animatable grows, replace with Set
|
|
3889
|
+
if (key === "zIndex")
|
|
3890
|
+
return false;
|
|
3891
|
+
// If it's a number or a keyframes array, we can animate it. We might at some point
|
|
3892
|
+
// need to do a deep isAnimatable check of keyframes, or let Popmotion handle this,
|
|
3893
|
+
// but for now lets leave it like this for performance reasons
|
|
3894
|
+
if (typeof value === "number" || Array.isArray(value))
|
|
3895
|
+
return true;
|
|
3896
|
+
if (typeof value === "string" && // It's animatable if we have a string
|
|
3897
|
+
complex.test(value) && // And it contains numbers and/or colors
|
|
3898
|
+
!value.startsWith("url(") // Unless it starts with "url("
|
|
3899
|
+
) {
|
|
3900
|
+
return true;
|
|
3901
|
+
}
|
|
3902
|
+
return false;
|
|
3903
|
+
};
|
|
3904
|
+
|
|
3905
|
+
/**
|
|
3906
|
+
* Decide whether a transition is defined on a given Transition.
|
|
3907
|
+
* This filters out orchestration options and returns true
|
|
3908
|
+
* if any options are left.
|
|
3909
|
+
*/
|
|
3910
|
+
function isTransitionDefined({ when, delay: _delay, delayChildren, staggerChildren, staggerDirection, repeat, repeatType, repeatDelay, from, elapsed, ...transition }) {
|
|
3911
|
+
return !!Object.keys(transition).length;
|
|
3912
|
+
}
|
|
3913
|
+
function isZero(value) {
|
|
3914
|
+
return (value === 0 ||
|
|
3915
|
+
(typeof value === "string" &&
|
|
3916
|
+
parseFloat(value) === 0 &&
|
|
3917
|
+
value.indexOf(" ") === -1));
|
|
3918
|
+
}
|
|
3919
|
+
function getZeroUnit(potentialUnitType) {
|
|
3920
|
+
return typeof potentialUnitType === "number"
|
|
3921
|
+
? 0
|
|
3922
|
+
: getAnimatableNone("", potentialUnitType);
|
|
3923
|
+
}
|
|
3924
|
+
function getValueTransition(transition, key) {
|
|
3925
|
+
return transition[key] || transition["default"] || transition;
|
|
3926
|
+
}
|
|
3927
|
+
|
|
3928
|
+
function getKeyframes(value, valueName, target, transition) {
|
|
3929
|
+
const isTargetAnimatable = isAnimatable(valueName, target);
|
|
3930
|
+
let origin = transition.from !== undefined ? transition.from : value.get();
|
|
3931
|
+
if (origin === "none" && isTargetAnimatable && typeof target === "string") {
|
|
3932
|
+
/**
|
|
3933
|
+
* If we're trying to animate from "none", try and get an animatable version
|
|
3934
|
+
* of the target. This could be improved to work both ways.
|
|
3935
|
+
*/
|
|
3936
|
+
origin = getAnimatableNone(valueName, target);
|
|
3937
|
+
}
|
|
3938
|
+
else if (isZero(origin) && typeof target === "string") {
|
|
3939
|
+
origin = getZeroUnit(target);
|
|
3940
|
+
}
|
|
3941
|
+
else if (!Array.isArray(target) &&
|
|
3942
|
+
isZero(target) &&
|
|
3943
|
+
typeof origin === "string") {
|
|
3944
|
+
target = getZeroUnit(origin);
|
|
3945
|
+
}
|
|
3946
|
+
/**
|
|
3947
|
+
* If the target has been defined as a series of keyframes
|
|
3948
|
+
*/
|
|
3949
|
+
if (Array.isArray(target)) {
|
|
3950
|
+
/**
|
|
3951
|
+
* Ensure an initial wildcard keyframe is hydrated by the origin.
|
|
3952
|
+
* TODO: Support extra wildcard keyframes i.e [1, null, 0]
|
|
3953
|
+
*/
|
|
3954
|
+
if (target[0] === null) {
|
|
3955
|
+
target[0] = origin;
|
|
3956
|
+
}
|
|
3957
|
+
return target;
|
|
3958
|
+
}
|
|
3959
|
+
else {
|
|
3960
|
+
return [origin, target];
|
|
3961
|
+
}
|
|
3962
|
+
}
|
|
3963
|
+
|
|
3964
|
+
const createMotionValueAnimation = (valueName, value, target, transition = {}) => {
|
|
3965
|
+
return (onComplete) => {
|
|
3966
|
+
const valueTransition = getValueTransition(transition, valueName) || {};
|
|
3967
|
+
/**
|
|
3968
|
+
* Most transition values are currently completely overwritten by value-specific
|
|
3969
|
+
* transitions. In the future it'd be nicer to blend these transitions. But for now
|
|
3970
|
+
* delay actually does inherit from the root transition if not value-specific.
|
|
1682
3971
|
*/
|
|
1683
|
-
|
|
1684
|
-
value = targetValue[0];
|
|
1685
|
-
}
|
|
3972
|
+
const delay = valueTransition.delay || transition.delay || 0;
|
|
1686
3973
|
/**
|
|
1687
|
-
*
|
|
1688
|
-
*
|
|
1689
|
-
* if not read the value from the DOM. As an absolute fallback, take the defined target value.
|
|
3974
|
+
* Elapsed isn't a public transition option but can be passed through from
|
|
3975
|
+
* optimized appear effects in milliseconds.
|
|
1690
3976
|
*/
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
3977
|
+
let { elapsed = 0 } = transition;
|
|
3978
|
+
elapsed = elapsed - secondsToMilliseconds(delay);
|
|
3979
|
+
const keyframes = getKeyframes(value, valueName, target, valueTransition);
|
|
1694
3980
|
/**
|
|
1695
|
-
*
|
|
1696
|
-
*
|
|
3981
|
+
* Check if we're able to animate between the start and end keyframes,
|
|
3982
|
+
* and throw a warning if we're attempting to animate between one that's
|
|
3983
|
+
* animatable and another that isn't.
|
|
1697
3984
|
*/
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
3985
|
+
const originKeyframe = keyframes[0];
|
|
3986
|
+
const targetKeyframe = keyframes[keyframes.length - 1];
|
|
3987
|
+
const isOriginAnimatable = isAnimatable(valueName, originKeyframe);
|
|
3988
|
+
const isTargetAnimatable = isAnimatable(valueName, targetKeyframe);
|
|
3989
|
+
exports.warning(isOriginAnimatable === isTargetAnimatable, `You are trying to animate ${valueName} from "${originKeyframe}" to "${targetKeyframe}". ${originKeyframe} is not an animatable value - to enable this animation set ${originKeyframe} to a value animatable to ${targetKeyframe} via the \`style\` property.`);
|
|
3990
|
+
let options = {
|
|
3991
|
+
keyframes,
|
|
3992
|
+
velocity: value.getVelocity(),
|
|
3993
|
+
...valueTransition,
|
|
3994
|
+
elapsed,
|
|
3995
|
+
onUpdate: (v) => {
|
|
3996
|
+
value.set(v);
|
|
3997
|
+
valueTransition.onUpdate && valueTransition.onUpdate(v);
|
|
3998
|
+
},
|
|
3999
|
+
onComplete: () => {
|
|
4000
|
+
onComplete();
|
|
4001
|
+
valueTransition.onComplete && valueTransition.onComplete();
|
|
4002
|
+
},
|
|
4003
|
+
};
|
|
4004
|
+
if (!isOriginAnimatable ||
|
|
4005
|
+
!isTargetAnimatable ||
|
|
4006
|
+
instantAnimationState.current ||
|
|
4007
|
+
valueTransition.type === false) {
|
|
4008
|
+
/**
|
|
4009
|
+
* If we can't animate this value, or the global instant animation flag is set,
|
|
4010
|
+
* or this is simply defined as an instant transition, return an instant transition.
|
|
4011
|
+
*/
|
|
4012
|
+
return createInstantAnimation(options);
|
|
1704
4013
|
}
|
|
1705
|
-
else if (
|
|
1706
|
-
|
|
4014
|
+
else if (valueTransition.type === "inertia") {
|
|
4015
|
+
/**
|
|
4016
|
+
* If this is an inertia animation, we currently don't support pre-generating
|
|
4017
|
+
* keyframes for this as such it must always run on the main thread.
|
|
4018
|
+
*/
|
|
4019
|
+
return inertia(options);
|
|
1707
4020
|
}
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
4021
|
+
/**
|
|
4022
|
+
* If there's no transition defined for this value, we can generate
|
|
4023
|
+
* unqiue transition settings for this value.
|
|
4024
|
+
*/
|
|
4025
|
+
if (!isTransitionDefined(valueTransition)) {
|
|
4026
|
+
options = {
|
|
4027
|
+
...options,
|
|
4028
|
+
...getDefaultTransition(valueName, options),
|
|
4029
|
+
};
|
|
1711
4030
|
}
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
const valueTransition = transition[key] || transition["default"] || transition;
|
|
1720
|
-
return valueTransition.from;
|
|
1721
|
-
}
|
|
1722
|
-
function getOrigin(target, transition, visualElement) {
|
|
1723
|
-
const origin = {};
|
|
1724
|
-
for (const key in target) {
|
|
1725
|
-
const transitionOrigin = getOriginFromTransition(key, transition);
|
|
1726
|
-
if (transitionOrigin !== undefined) {
|
|
1727
|
-
origin[key] = transitionOrigin;
|
|
4031
|
+
/**
|
|
4032
|
+
* Both WAAPI and our internal animation functions use durations
|
|
4033
|
+
* as defined by milliseconds, while our external API defines them
|
|
4034
|
+
* as seconds.
|
|
4035
|
+
*/
|
|
4036
|
+
if (options.duration) {
|
|
4037
|
+
options.duration = secondsToMilliseconds(options.duration);
|
|
1728
4038
|
}
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
if (value) {
|
|
1732
|
-
origin[key] = value.get();
|
|
1733
|
-
}
|
|
4039
|
+
if (options.repeatDelay) {
|
|
4040
|
+
options.repeatDelay = secondsToMilliseconds(options.repeatDelay);
|
|
1734
4041
|
}
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
4042
|
+
/**
|
|
4043
|
+
* Animate via WAAPI if possible.
|
|
4044
|
+
*/
|
|
4045
|
+
if (value.owner &&
|
|
4046
|
+
value.owner.current instanceof HTMLElement &&
|
|
4047
|
+
!value.owner.getProps().onUpdate) {
|
|
4048
|
+
const acceleratedAnimation = createAcceleratedAnimation(value, valueName, options);
|
|
4049
|
+
if (acceleratedAnimation)
|
|
4050
|
+
return acceleratedAnimation;
|
|
4051
|
+
}
|
|
4052
|
+
/**
|
|
4053
|
+
* If we didn't create an accelerated animation, create a JS animation
|
|
4054
|
+
*/
|
|
4055
|
+
return animateValue(options);
|
|
4056
|
+
};
|
|
4057
|
+
};
|
|
1745
4058
|
|
|
1746
4059
|
function animateVisualElement(visualElement, definition, options = {}) {
|
|
1747
4060
|
visualElement.notify("AnimationStart", definition);
|
|
@@ -1828,10 +4141,10 @@ function animateTarget(visualElement, definition, { delay = 0, transitionOverrid
|
|
|
1828
4141
|
if (window.HandoffAppearAnimations && !value.hasAnimated) {
|
|
1829
4142
|
const appearId = visualElement.getProps()[optimizedAppearDataAttribute];
|
|
1830
4143
|
if (appearId) {
|
|
1831
|
-
valueTransition.elapsed = window.HandoffAppearAnimations(appearId, key, value,
|
|
4144
|
+
valueTransition.elapsed = window.HandoffAppearAnimations(appearId, key, value, sync);
|
|
1832
4145
|
}
|
|
1833
4146
|
}
|
|
1834
|
-
let animation = value.start(
|
|
4147
|
+
let animation = value.start(createMotionValueAnimation(key, value, valueTarget, visualElement.shouldReduceMotion && transformProps.has(key)
|
|
1835
4148
|
? { type: false }
|
|
1836
4149
|
: valueTransition));
|
|
1837
4150
|
if (isWillChangeMotionValue(willChange)) {
|
|
@@ -2259,6 +4572,14 @@ const animations = {
|
|
|
2259
4572
|
},
|
|
2260
4573
|
};
|
|
2261
4574
|
|
|
4575
|
+
const distance = (a, b) => Math.abs(a - b);
|
|
4576
|
+
function distance2D(a, b) {
|
|
4577
|
+
// Multi-dimensional
|
|
4578
|
+
const xDelta = distance(a.x, b.x);
|
|
4579
|
+
const yDelta = distance(a.y, b.y);
|
|
4580
|
+
return Math.sqrt(xDelta ** 2 + yDelta ** 2);
|
|
4581
|
+
}
|
|
4582
|
+
|
|
2262
4583
|
/**
|
|
2263
4584
|
* @internal
|
|
2264
4585
|
*/
|
|
@@ -2288,11 +4609,11 @@ class PanSession {
|
|
|
2288
4609
|
// Only start panning if the offset is larger than 3 pixels. If we make it
|
|
2289
4610
|
// any larger than this we'll want to reset the pointer history
|
|
2290
4611
|
// on the first update to avoid visual snapping to the cursoe.
|
|
2291
|
-
const isDistancePastThreshold =
|
|
4612
|
+
const isDistancePastThreshold = distance2D(info.offset, { x: 0, y: 0 }) >= 3;
|
|
2292
4613
|
if (!isPanStarted && !isDistancePastThreshold)
|
|
2293
4614
|
return;
|
|
2294
4615
|
const { point } = info;
|
|
2295
|
-
const { timestamp } =
|
|
4616
|
+
const { timestamp } = frameData;
|
|
2296
4617
|
this.history.push({ ...point, timestamp });
|
|
2297
4618
|
const { onStart, onMove } = this.handlers;
|
|
2298
4619
|
if (!isPanStarted) {
|
|
@@ -2305,7 +4626,7 @@ class PanSession {
|
|
|
2305
4626
|
this.lastMoveEvent = event;
|
|
2306
4627
|
this.lastMoveEventInfo = transformPoint(info, this.transformPagePoint);
|
|
2307
4628
|
// Throttle mouse move event to once per frame
|
|
2308
|
-
|
|
4629
|
+
sync.update(this.updatePoint, true);
|
|
2309
4630
|
};
|
|
2310
4631
|
this.handlePointerUp = (event, info) => {
|
|
2311
4632
|
this.end();
|
|
@@ -2328,19 +4649,19 @@ class PanSession {
|
|
|
2328
4649
|
const info = extractEventInfo(event);
|
|
2329
4650
|
const initialInfo = transformPoint(info, this.transformPagePoint);
|
|
2330
4651
|
const { point } = initialInfo;
|
|
2331
|
-
const { timestamp } =
|
|
4652
|
+
const { timestamp } = frameData;
|
|
2332
4653
|
this.history = [{ ...point, timestamp }];
|
|
2333
4654
|
const { onSessionStart } = handlers;
|
|
2334
4655
|
onSessionStart &&
|
|
2335
4656
|
onSessionStart(event, getPanInfo(initialInfo, this.history));
|
|
2336
|
-
this.removeListeners =
|
|
4657
|
+
this.removeListeners = pipe(addPointerEvent(window, "pointermove", this.handlePointerMove), addPointerEvent(window, "pointerup", this.handlePointerUp), addPointerEvent(window, "pointercancel", this.handlePointerUp));
|
|
2337
4658
|
}
|
|
2338
4659
|
updateHandlers(handlers) {
|
|
2339
4660
|
this.handlers = handlers;
|
|
2340
4661
|
}
|
|
2341
4662
|
end() {
|
|
2342
4663
|
this.removeListeners && this.removeListeners();
|
|
2343
|
-
|
|
4664
|
+
cancelSync.update(this.updatePoint);
|
|
2344
4665
|
}
|
|
2345
4666
|
}
|
|
2346
4667
|
function transformPoint(info, transformPagePoint) {
|
|
@@ -2373,7 +4694,7 @@ function getVelocity(history, timeDelta) {
|
|
|
2373
4694
|
while (i >= 0) {
|
|
2374
4695
|
timestampedPoint = history[i];
|
|
2375
4696
|
if (lastPoint.timestamp - timestampedPoint.timestamp >
|
|
2376
|
-
|
|
4697
|
+
secondsToMilliseconds(timeDelta)) {
|
|
2377
4698
|
break;
|
|
2378
4699
|
}
|
|
2379
4700
|
i--;
|
|
@@ -2406,12 +4727,12 @@ function isNear(value, target = 0, maxDistance = 0.01) {
|
|
|
2406
4727
|
}
|
|
2407
4728
|
function calcAxisDelta(delta, source, target, origin = 0.5) {
|
|
2408
4729
|
delta.origin = origin;
|
|
2409
|
-
delta.originPoint =
|
|
4730
|
+
delta.originPoint = mix(source.min, source.max, delta.origin);
|
|
2410
4731
|
delta.scale = calcLength(target) / calcLength(source);
|
|
2411
4732
|
if (isNear(delta.scale, 1, 0.0001) || isNaN(delta.scale))
|
|
2412
4733
|
delta.scale = 1;
|
|
2413
4734
|
delta.translate =
|
|
2414
|
-
|
|
4735
|
+
mix(target.min, target.max, delta.origin) - delta.originPoint;
|
|
2415
4736
|
if (isNear(delta.translate) || isNaN(delta.translate))
|
|
2416
4737
|
delta.translate = 0;
|
|
2417
4738
|
}
|
|
@@ -2444,11 +4765,11 @@ function calcRelativePosition(target, layout, parent) {
|
|
|
2444
4765
|
function applyConstraints(point, { min, max }, elastic) {
|
|
2445
4766
|
if (min !== undefined && point < min) {
|
|
2446
4767
|
// If we have a min point defined, and this is outside of that, constrain
|
|
2447
|
-
point = elastic ?
|
|
4768
|
+
point = elastic ? mix(min, point, elastic.min) : Math.max(point, min);
|
|
2448
4769
|
}
|
|
2449
4770
|
else if (max !== undefined && point > max) {
|
|
2450
4771
|
// If we have a max point defined, and this is outside of that, constrain
|
|
2451
|
-
point = elastic ?
|
|
4772
|
+
point = elastic ? mix(max, point, elastic.max) : Math.min(point, max);
|
|
2452
4773
|
}
|
|
2453
4774
|
return point;
|
|
2454
4775
|
}
|
|
@@ -2507,12 +4828,12 @@ function calcOrigin(source, target) {
|
|
|
2507
4828
|
const sourceLength = calcLength(source);
|
|
2508
4829
|
const targetLength = calcLength(target);
|
|
2509
4830
|
if (targetLength > sourceLength) {
|
|
2510
|
-
origin =
|
|
4831
|
+
origin = progress(target.min, target.max - sourceLength, source.min);
|
|
2511
4832
|
}
|
|
2512
4833
|
else if (sourceLength > targetLength) {
|
|
2513
|
-
origin =
|
|
4834
|
+
origin = progress(source.min, source.max - targetLength, target.min);
|
|
2514
4835
|
}
|
|
2515
|
-
return
|
|
4836
|
+
return clamp(0, 1, origin);
|
|
2516
4837
|
}
|
|
2517
4838
|
/**
|
|
2518
4839
|
* Rebase the calculated viewport constraints relative to the layout.min point.
|
|
@@ -2731,7 +5052,7 @@ function translateAxis(axis, distance) {
|
|
|
2731
5052
|
*/
|
|
2732
5053
|
function transformAxis(axis, transforms, [key, scaleKey, originKey]) {
|
|
2733
5054
|
const axisOrigin = transforms[originKey] !== undefined ? transforms[originKey] : 0.5;
|
|
2734
|
-
const originPoint =
|
|
5055
|
+
const originPoint = mix(axis.min, axis.max, axisOrigin);
|
|
2735
5056
|
// Apply the axis delta to the final axis
|
|
2736
5057
|
applyAxisDelta(axis, transforms[key], transforms[scaleKey], originPoint, transforms.scale);
|
|
2737
5058
|
}
|
|
@@ -2827,7 +5148,7 @@ class VisualElementDragControls {
|
|
|
2827
5148
|
/**
|
|
2828
5149
|
* If the MotionValue is a percentage value convert to px
|
|
2829
5150
|
*/
|
|
2830
|
-
if (
|
|
5151
|
+
if (percent.test(current)) {
|
|
2831
5152
|
const { projection } = this.visualElement;
|
|
2832
5153
|
if (projection && projection.layout) {
|
|
2833
5154
|
const measuredAxis = projection.layout.layoutBox[axis];
|
|
@@ -2840,7 +5161,9 @@ class VisualElementDragControls {
|
|
|
2840
5161
|
this.originPoint[axis] = current;
|
|
2841
5162
|
});
|
|
2842
5163
|
// Fire onDragStart event
|
|
2843
|
-
|
|
5164
|
+
if (onDragStart) {
|
|
5165
|
+
sync.update(() => onDragStart(event, info));
|
|
5166
|
+
}
|
|
2844
5167
|
const { animationState } = this.visualElement;
|
|
2845
5168
|
animationState && animationState.setActive("whileDrag", true);
|
|
2846
5169
|
};
|
|
@@ -2892,7 +5215,9 @@ class VisualElementDragControls {
|
|
|
2892
5215
|
const { velocity } = info;
|
|
2893
5216
|
this.startAnimation(velocity);
|
|
2894
5217
|
const { onDragEnd } = this.getProps();
|
|
2895
|
-
|
|
5218
|
+
if (onDragEnd) {
|
|
5219
|
+
sync.update(() => onDragEnd(event, info));
|
|
5220
|
+
}
|
|
2896
5221
|
}
|
|
2897
5222
|
cancel() {
|
|
2898
5223
|
this.isDragging = false;
|
|
@@ -2960,7 +5285,7 @@ class VisualElementDragControls {
|
|
|
2960
5285
|
if (!constraints || !isRefObject(constraints))
|
|
2961
5286
|
return false;
|
|
2962
5287
|
const constraintsElement = constraints.current;
|
|
2963
|
-
|
|
5288
|
+
exports.invariant(constraintsElement !== null, "If `dragConstraints` is set as a React ref, that ref must be passed to another component's `ref` prop.");
|
|
2964
5289
|
const { projection } = this.visualElement;
|
|
2965
5290
|
// TODO
|
|
2966
5291
|
if (!projection || !projection.layout)
|
|
@@ -3019,7 +5344,7 @@ class VisualElementDragControls {
|
|
|
3019
5344
|
}
|
|
3020
5345
|
startAxisValueAnimation(axis, transition) {
|
|
3021
5346
|
const axisValue = this.getAxisMotionValue(axis);
|
|
3022
|
-
return axisValue.start(
|
|
5347
|
+
return axisValue.start(createMotionValueAnimation(axis, axisValue, 0, transition));
|
|
3023
5348
|
}
|
|
3024
5349
|
stopAnimation() {
|
|
3025
5350
|
eachAxis((axis) => this.getAxisMotionValue(axis).stop());
|
|
@@ -3048,7 +5373,7 @@ class VisualElementDragControls {
|
|
|
3048
5373
|
const axisValue = this.getAxisMotionValue(axis);
|
|
3049
5374
|
if (projection && projection.layout) {
|
|
3050
5375
|
const { min, max } = projection.layout.layoutBox[axis];
|
|
3051
|
-
axisValue.set(point[axis] -
|
|
5376
|
+
axisValue.set(point[axis] - mix(min, max, 0.5));
|
|
3052
5377
|
}
|
|
3053
5378
|
});
|
|
3054
5379
|
}
|
|
@@ -3103,7 +5428,7 @@ class VisualElementDragControls {
|
|
|
3103
5428
|
*/
|
|
3104
5429
|
const axisValue = this.getAxisMotionValue(axis);
|
|
3105
5430
|
const { min, max } = this.constraints[axis];
|
|
3106
|
-
axisValue.set(
|
|
5431
|
+
axisValue.set(mix(min, max, boxProgress[axis]));
|
|
3107
5432
|
});
|
|
3108
5433
|
}
|
|
3109
5434
|
addListeners() {
|
|
@@ -3198,8 +5523,8 @@ function getCurrentDirection(offset, lockThreshold = 10) {
|
|
|
3198
5523
|
class DragGesture extends Feature {
|
|
3199
5524
|
constructor(node) {
|
|
3200
5525
|
super(node);
|
|
3201
|
-
this.removeGroupControls =
|
|
3202
|
-
this.removeListeners =
|
|
5526
|
+
this.removeGroupControls = noop;
|
|
5527
|
+
this.removeListeners = noop;
|
|
3203
5528
|
this.controls = new VisualElementDragControls(node);
|
|
3204
5529
|
}
|
|
3205
5530
|
mount() {
|
|
@@ -3209,7 +5534,7 @@ class DragGesture extends Feature {
|
|
|
3209
5534
|
if (dragControls) {
|
|
3210
5535
|
this.removeGroupControls = dragControls.subscribe(this.controls);
|
|
3211
5536
|
}
|
|
3212
|
-
this.removeListeners = this.controls.addListeners() ||
|
|
5537
|
+
this.removeListeners = this.controls.addListeners() || noop;
|
|
3213
5538
|
}
|
|
3214
5539
|
unmount() {
|
|
3215
5540
|
this.removeGroupControls();
|
|
@@ -3217,10 +5542,15 @@ class DragGesture extends Feature {
|
|
|
3217
5542
|
}
|
|
3218
5543
|
}
|
|
3219
5544
|
|
|
5545
|
+
const asyncHandler = (handler) => (event, info) => {
|
|
5546
|
+
if (handler) {
|
|
5547
|
+
sync.update(() => handler(event, info));
|
|
5548
|
+
}
|
|
5549
|
+
};
|
|
3220
5550
|
class PanGesture extends Feature {
|
|
3221
5551
|
constructor() {
|
|
3222
5552
|
super(...arguments);
|
|
3223
|
-
this.removePointerDownListener =
|
|
5553
|
+
this.removePointerDownListener = noop;
|
|
3224
5554
|
}
|
|
3225
5555
|
onPointerDown(pointerDownEvent) {
|
|
3226
5556
|
this.session = new PanSession(pointerDownEvent, this.createPanHandlers(), { transformPagePoint: this.node.getTransformPagePoint() });
|
|
@@ -3228,12 +5558,14 @@ class PanGesture extends Feature {
|
|
|
3228
5558
|
createPanHandlers() {
|
|
3229
5559
|
const { onPanSessionStart, onPanStart, onPan, onPanEnd } = this.node.getProps();
|
|
3230
5560
|
return {
|
|
3231
|
-
onSessionStart: onPanSessionStart,
|
|
3232
|
-
onStart: onPanStart,
|
|
5561
|
+
onSessionStart: asyncHandler(onPanSessionStart),
|
|
5562
|
+
onStart: asyncHandler(onPanStart),
|
|
3233
5563
|
onMove: onPan,
|
|
3234
5564
|
onEnd: (event, info) => {
|
|
3235
5565
|
delete this.session;
|
|
3236
|
-
|
|
5566
|
+
if (onPanEnd) {
|
|
5567
|
+
sync.update(() => onPanEnd(event, info));
|
|
5568
|
+
}
|
|
3237
5569
|
},
|
|
3238
5570
|
};
|
|
3239
5571
|
}
|
|
@@ -3249,19 +5581,55 @@ class PanGesture extends Feature {
|
|
|
3249
5581
|
}
|
|
3250
5582
|
}
|
|
3251
5583
|
|
|
5584
|
+
/**
|
|
5585
|
+
* Animate a single value or a `MotionValue`.
|
|
5586
|
+
*
|
|
5587
|
+
* The first argument is either a `MotionValue` to animate, or an initial animation value.
|
|
5588
|
+
*
|
|
5589
|
+
* The second is either a value to animate to, or an array of keyframes to animate through.
|
|
5590
|
+
*
|
|
5591
|
+
* The third argument can be either tween or spring options, and optional lifecycle methods: `onUpdate`, `onPlay`, `onComplete`, `onRepeat` and `onStop`.
|
|
5592
|
+
*
|
|
5593
|
+
* Returns `AnimationPlaybackControls`, currently just a `stop` method.
|
|
5594
|
+
*
|
|
5595
|
+
* ```javascript
|
|
5596
|
+
* const x = useMotionValue(0)
|
|
5597
|
+
*
|
|
5598
|
+
* useEffect(() => {
|
|
5599
|
+
* const controls = animate(x, 100, {
|
|
5600
|
+
* type: "spring",
|
|
5601
|
+
* stiffness: 2000,
|
|
5602
|
+
* onComplete: v => {}
|
|
5603
|
+
* })
|
|
5604
|
+
*
|
|
5605
|
+
* return controls.stop
|
|
5606
|
+
* })
|
|
5607
|
+
* ```
|
|
5608
|
+
*
|
|
5609
|
+
* @public
|
|
5610
|
+
*/
|
|
5611
|
+
function animate(from, to, transition = {}) {
|
|
5612
|
+
const value = isMotionValue(from) ? from : motionValue(from);
|
|
5613
|
+
value.start(createMotionValueAnimation("", value, to, transition));
|
|
5614
|
+
return {
|
|
5615
|
+
stop: () => value.stop(),
|
|
5616
|
+
isAnimating: () => value.isAnimating(),
|
|
5617
|
+
};
|
|
5618
|
+
}
|
|
5619
|
+
|
|
3252
5620
|
const borders = ["TopLeft", "TopRight", "BottomLeft", "BottomRight"];
|
|
3253
5621
|
const numBorders = borders.length;
|
|
3254
5622
|
const asNumber = (value) => typeof value === "string" ? parseFloat(value) : value;
|
|
3255
|
-
const isPx = (value) => typeof value === "number" ||
|
|
5623
|
+
const isPx = (value) => typeof value === "number" || px.test(value);
|
|
3256
5624
|
function mixValues(target, follow, lead, progress, shouldCrossfadeOpacity, isOnlyMember) {
|
|
3257
5625
|
if (shouldCrossfadeOpacity) {
|
|
3258
|
-
target.opacity =
|
|
5626
|
+
target.opacity = mix(0,
|
|
3259
5627
|
// TODO Reinstate this if only child
|
|
3260
5628
|
lead.opacity !== undefined ? lead.opacity : 1, easeCrossfadeIn(progress));
|
|
3261
|
-
target.opacityExit =
|
|
5629
|
+
target.opacityExit = mix(follow.opacity !== undefined ? follow.opacity : 1, 0, easeCrossfadeOut(progress));
|
|
3262
5630
|
}
|
|
3263
5631
|
else if (isOnlyMember) {
|
|
3264
|
-
target.opacity =
|
|
5632
|
+
target.opacity = mix(follow.opacity !== undefined ? follow.opacity : 1, lead.opacity !== undefined ? lead.opacity : 1, progress);
|
|
3265
5633
|
}
|
|
3266
5634
|
/**
|
|
3267
5635
|
* Mix border radius
|
|
@@ -3278,8 +5646,8 @@ function mixValues(target, follow, lead, progress, shouldCrossfadeOpacity, isOnl
|
|
|
3278
5646
|
leadRadius === 0 ||
|
|
3279
5647
|
isPx(followRadius) === isPx(leadRadius);
|
|
3280
5648
|
if (canMix) {
|
|
3281
|
-
target[borderLabel] = Math.max(
|
|
3282
|
-
if (
|
|
5649
|
+
target[borderLabel] = Math.max(mix(asNumber(followRadius), asNumber(leadRadius), progress), 0);
|
|
5650
|
+
if (percent.test(leadRadius) || percent.test(followRadius)) {
|
|
3283
5651
|
target[borderLabel] += "%";
|
|
3284
5652
|
}
|
|
3285
5653
|
}
|
|
@@ -3291,7 +5659,7 @@ function mixValues(target, follow, lead, progress, shouldCrossfadeOpacity, isOnl
|
|
|
3291
5659
|
* Mix rotation
|
|
3292
5660
|
*/
|
|
3293
5661
|
if (follow.rotate || lead.rotate) {
|
|
3294
|
-
target.rotate =
|
|
5662
|
+
target.rotate = mix(follow.rotate || 0, lead.rotate || 0, progress);
|
|
3295
5663
|
}
|
|
3296
5664
|
}
|
|
3297
5665
|
function getRadius(values, radiusName) {
|
|
@@ -3322,8 +5690,8 @@ function getRadius(values, radiusName) {
|
|
|
3322
5690
|
// latestLeadValues.backgroundColor as string
|
|
3323
5691
|
// )(p)
|
|
3324
5692
|
// }
|
|
3325
|
-
const easeCrossfadeIn = compress(0, 0.5,
|
|
3326
|
-
const easeCrossfadeOut = compress(0.5, 0.95,
|
|
5693
|
+
const easeCrossfadeIn = compress(0, 0.5, circOut);
|
|
5694
|
+
const easeCrossfadeOut = compress(0.5, 0.95, noop);
|
|
3327
5695
|
function compress(min, max, easing) {
|
|
3328
5696
|
return (p) => {
|
|
3329
5697
|
// Could replace ifs with clamp
|
|
@@ -3331,7 +5699,7 @@ function compress(min, max, easing) {
|
|
|
3331
5699
|
return 0;
|
|
3332
5700
|
if (p > max)
|
|
3333
5701
|
return 1;
|
|
3334
|
-
return easing(
|
|
5702
|
+
return easing(progress(min, max, p));
|
|
3335
5703
|
};
|
|
3336
5704
|
}
|
|
3337
5705
|
|
|
@@ -3369,14 +5737,14 @@ function removePointDelta(point, translate, scale, originPoint, boxScale) {
|
|
|
3369
5737
|
* Remove a delta from an axis. This is essentially the steps of applyAxisDelta in reverse
|
|
3370
5738
|
*/
|
|
3371
5739
|
function removeAxisDelta(axis, translate = 0, scale = 1, origin = 0.5, boxScale, originAxis = axis, sourceAxis = axis) {
|
|
3372
|
-
if (
|
|
5740
|
+
if (percent.test(translate)) {
|
|
3373
5741
|
translate = parseFloat(translate);
|
|
3374
|
-
const relativeProgress =
|
|
5742
|
+
const relativeProgress = mix(sourceAxis.min, sourceAxis.max, translate / 100);
|
|
3375
5743
|
translate = relativeProgress - sourceAxis.min;
|
|
3376
5744
|
}
|
|
3377
5745
|
if (typeof translate !== "number")
|
|
3378
5746
|
return;
|
|
3379
|
-
let originPoint =
|
|
5747
|
+
let originPoint = mix(originAxis.min, originAxis.max, origin);
|
|
3380
5748
|
if (axis === originAxis)
|
|
3381
5749
|
originPoint -= translate;
|
|
3382
5750
|
axis.min = removePointDelta(axis.min, translate, scale, originPoint, boxScale);
|
|
@@ -3424,11 +5792,11 @@ class NodeStack {
|
|
|
3424
5792
|
this.members = [];
|
|
3425
5793
|
}
|
|
3426
5794
|
add(node) {
|
|
3427
|
-
|
|
5795
|
+
addUniqueItem(this.members, node);
|
|
3428
5796
|
node.scheduleRender();
|
|
3429
5797
|
}
|
|
3430
5798
|
remove(node) {
|
|
3431
|
-
|
|
5799
|
+
removeItem(this.members, node);
|
|
3432
5800
|
if (node === this.prevLead) {
|
|
3433
5801
|
this.prevLead = undefined;
|
|
3434
5802
|
}
|
|
@@ -3577,11 +5945,11 @@ class FlatTree {
|
|
|
3577
5945
|
this.isDirty = false;
|
|
3578
5946
|
}
|
|
3579
5947
|
add(child) {
|
|
3580
|
-
|
|
5948
|
+
addUniqueItem(this.children, child);
|
|
3581
5949
|
this.isDirty = true;
|
|
3582
5950
|
}
|
|
3583
5951
|
remove(child) {
|
|
3584
|
-
|
|
5952
|
+
removeItem(this.children, child);
|
|
3585
5953
|
this.isDirty = true;
|
|
3586
5954
|
}
|
|
3587
5955
|
forEach(callback) {
|
|
@@ -3753,7 +6121,7 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
|
|
|
3753
6121
|
}
|
|
3754
6122
|
addEventListener(name, handler) {
|
|
3755
6123
|
if (!this.eventHandlers.has(name)) {
|
|
3756
|
-
this.eventHandlers.set(name, new
|
|
6124
|
+
this.eventHandlers.set(name, new SubscriptionManager());
|
|
3757
6125
|
}
|
|
3758
6126
|
return this.eventHandlers.get(name).add(handler);
|
|
3759
6127
|
}
|
|
@@ -3792,7 +6160,7 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
|
|
|
3792
6160
|
attachResizeListener(instance, () => {
|
|
3793
6161
|
this.root.updateBlockedByResize = true;
|
|
3794
6162
|
cancelDelay && cancelDelay();
|
|
3795
|
-
cancelDelay =
|
|
6163
|
+
cancelDelay = delay(resizeUnblockUpdate, 250);
|
|
3796
6164
|
if (globalProjectionState.hasAnimatedSinceResize) {
|
|
3797
6165
|
globalProjectionState.hasAnimatedSinceResize = false;
|
|
3798
6166
|
this.nodes.forEach(finishAnimation);
|
|
@@ -3841,7 +6209,7 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
|
|
|
3841
6209
|
}
|
|
3842
6210
|
this.setAnimationOrigin(delta, hasOnlyRelativeTargetChanged);
|
|
3843
6211
|
const animationOptions = {
|
|
3844
|
-
...
|
|
6212
|
+
...getValueTransition(layoutTransition, "layout"),
|
|
3845
6213
|
onPlay: onLayoutAnimationStart,
|
|
3846
6214
|
onComplete: onLayoutAnimationComplete,
|
|
3847
6215
|
};
|
|
@@ -3877,7 +6245,7 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
|
|
|
3877
6245
|
stack && stack.remove(this);
|
|
3878
6246
|
this.parent && this.parent.children.delete(this);
|
|
3879
6247
|
this.instance = undefined;
|
|
3880
|
-
|
|
6248
|
+
cancelSync.preRender(this.updateProjection);
|
|
3881
6249
|
}
|
|
3882
6250
|
// only on the root
|
|
3883
6251
|
blockUpdate() {
|
|
@@ -3974,16 +6342,16 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
|
|
|
3974
6342
|
this.nodes.forEach(notifyLayoutUpdate);
|
|
3975
6343
|
this.clearAllSnapshots();
|
|
3976
6344
|
// Flush any scheduled updates
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
6345
|
+
flushSync.update();
|
|
6346
|
+
flushSync.preRender();
|
|
6347
|
+
flushSync.render();
|
|
3980
6348
|
}
|
|
3981
6349
|
clearAllSnapshots() {
|
|
3982
6350
|
this.nodes.forEach(clearSnapshot);
|
|
3983
6351
|
this.sharedNodes.forEach(removeLeadSnapshots);
|
|
3984
6352
|
}
|
|
3985
6353
|
scheduleUpdateProjection() {
|
|
3986
|
-
|
|
6354
|
+
sync.preRender(this.updateProjection, false, true);
|
|
3987
6355
|
}
|
|
3988
6356
|
scheduleCheckAfterUnmount() {
|
|
3989
6357
|
/**
|
|
@@ -3991,7 +6359,7 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
|
|
|
3991
6359
|
* we manually call didUpdate to give a chance to the siblings to animate.
|
|
3992
6360
|
* Otherwise, cleanup all snapshots to prevents future nodes from reusing them.
|
|
3993
6361
|
*/
|
|
3994
|
-
|
|
6362
|
+
sync.postRender(() => {
|
|
3995
6363
|
if (this.isLayoutDirty) {
|
|
3996
6364
|
this.root.didUpdate();
|
|
3997
6365
|
}
|
|
@@ -4503,7 +6871,7 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
|
|
|
4503
6871
|
this.resumingFrom.currentAnimation.stop();
|
|
4504
6872
|
}
|
|
4505
6873
|
if (this.pendingAnimation) {
|
|
4506
|
-
|
|
6874
|
+
cancelSync.update(this.pendingAnimation);
|
|
4507
6875
|
this.pendingAnimation = undefined;
|
|
4508
6876
|
}
|
|
4509
6877
|
/**
|
|
@@ -4511,9 +6879,9 @@ function createProjectionNode({ attachResizeListener, defaultParent, measureScro
|
|
|
4511
6879
|
* where the target is the same as when the animation started, so we can
|
|
4512
6880
|
* calculate the relative positions correctly for instant transitions.
|
|
4513
6881
|
*/
|
|
4514
|
-
this.pendingAnimation =
|
|
6882
|
+
this.pendingAnimation = sync.update(() => {
|
|
4515
6883
|
globalProjectionState.hasAnimatedSinceResize = true;
|
|
4516
|
-
this.currentAnimation =
|
|
6884
|
+
this.currentAnimation = animate(0, animationTarget, {
|
|
4517
6885
|
...options,
|
|
4518
6886
|
onUpdate: (latest) => {
|
|
4519
6887
|
this.mixTargetDelta(latest);
|
|
@@ -4958,14 +7326,14 @@ function removeLeadSnapshots(stack) {
|
|
|
4958
7326
|
stack.removeLeadSnapshot();
|
|
4959
7327
|
}
|
|
4960
7328
|
function mixAxisDelta(output, delta, p) {
|
|
4961
|
-
output.translate =
|
|
4962
|
-
output.scale =
|
|
7329
|
+
output.translate = mix(delta.translate, 0, p);
|
|
7330
|
+
output.scale = mix(delta.scale, 1, p);
|
|
4963
7331
|
output.origin = delta.origin;
|
|
4964
7332
|
output.originPoint = delta.originPoint;
|
|
4965
7333
|
}
|
|
4966
7334
|
function mixAxis(output, from, to, p) {
|
|
4967
|
-
output.min =
|
|
4968
|
-
output.max =
|
|
7335
|
+
output.min = mix(from.min, to.min, p);
|
|
7336
|
+
output.max = mix(from.max, to.max, p);
|
|
4969
7337
|
}
|
|
4970
7338
|
function mixBox(output, from, to, p) {
|
|
4971
7339
|
mixAxis(output.x, from.x, to.x, p);
|
|
@@ -5085,7 +7453,7 @@ const correctBorderRadius = {
|
|
|
5085
7453
|
* going to be stretched appropriately. Otherwise, if it's a pixel, convert it to a number.
|
|
5086
7454
|
*/
|
|
5087
7455
|
if (typeof latest === "string") {
|
|
5088
|
-
if (
|
|
7456
|
+
if (px.test(latest)) {
|
|
5089
7457
|
latest = parseFloat(latest);
|
|
5090
7458
|
}
|
|
5091
7459
|
else {
|
|
@@ -5121,7 +7489,7 @@ function parseCSSVariable(current) {
|
|
|
5121
7489
|
}
|
|
5122
7490
|
const maxDepth = 4;
|
|
5123
7491
|
function getVariableValue(current, element, depth = 1) {
|
|
5124
|
-
|
|
7492
|
+
exports.invariant(depth <= maxDepth, `Max CSS variable fallback depth detected in property "${current}". This may indicate a circular fallback dependency.`);
|
|
5125
7493
|
const [token, fallback] = parseCSSVariable(current);
|
|
5126
7494
|
// No CSS variable detected
|
|
5127
7495
|
if (!token)
|
|
@@ -5198,11 +7566,11 @@ const correctBoxShadow = {
|
|
|
5198
7566
|
return varToken;
|
|
5199
7567
|
});
|
|
5200
7568
|
}
|
|
5201
|
-
const shadow =
|
|
7569
|
+
const shadow = complex.parse(latest);
|
|
5202
7570
|
// TODO: Doesn't support multiple shadows
|
|
5203
7571
|
if (shadow.length > 5)
|
|
5204
7572
|
return original;
|
|
5205
|
-
const template =
|
|
7573
|
+
const template = complex.createTransformer(latest);
|
|
5206
7574
|
const offset = typeof shadow[0] !== "number" ? 1 : 0;
|
|
5207
7575
|
// Calculate the overall context scale
|
|
5208
7576
|
const xScale = projectionDelta.x.scale * treeScale.x;
|
|
@@ -5215,7 +7583,7 @@ const correctBoxShadow = {
|
|
|
5215
7583
|
* We could potentially improve the outcome of this by incorporating the ratio between
|
|
5216
7584
|
* the two scales.
|
|
5217
7585
|
*/
|
|
5218
|
-
const averageScale =
|
|
7586
|
+
const averageScale = mix(xScale, yScale, 0.5);
|
|
5219
7587
|
// Blur
|
|
5220
7588
|
if (typeof shadow[2 + offset] === "number")
|
|
5221
7589
|
shadow[2 + offset] /= averageScale;
|
|
@@ -5249,7 +7617,7 @@ const isPositionalKey = (key) => positionalKeys.has(key);
|
|
|
5249
7617
|
const hasPositionalKey = (target) => {
|
|
5250
7618
|
return Object.keys(target).some(isPositionalKey);
|
|
5251
7619
|
};
|
|
5252
|
-
const isNumOrPxType = (v) => v ===
|
|
7620
|
+
const isNumOrPxType = (v) => v === number || v === px;
|
|
5253
7621
|
const getPosFromMatrix = (matrix, pos) => parseFloat(matrix.split(", ")[pos]);
|
|
5254
7622
|
const getTranslateFromMatrix = (pos2, pos3) => (_bbox, { transform }) => {
|
|
5255
7623
|
if (transform === "none" || !transform)
|
|
@@ -5269,7 +7637,7 @@ const getTranslateFromMatrix = (pos2, pos3) => (_bbox, { transform }) => {
|
|
|
5269
7637
|
}
|
|
5270
7638
|
};
|
|
5271
7639
|
const transformKeys = new Set(["x", "y", "z"]);
|
|
5272
|
-
const nonTranslationalTransformKeys =
|
|
7640
|
+
const nonTranslationalTransformKeys = transformPropOrder.filter((key) => !transformKeys.has(key));
|
|
5273
7641
|
function removeNonTranslationalTransform(visualElement) {
|
|
5274
7642
|
const removedTransforms = [];
|
|
5275
7643
|
nonTranslationalTransformKeys.forEach((key) => {
|
|
@@ -5354,11 +7722,11 @@ const checkAndConvertChangedValueTypes = (visualElement, target, origin = {}, tr
|
|
|
5354
7722
|
for (let i = fromIndex; i < numKeyframes; i++) {
|
|
5355
7723
|
if (!toType) {
|
|
5356
7724
|
toType = findDimensionValueType(to[i]);
|
|
5357
|
-
|
|
7725
|
+
exports.invariant(toType === fromType ||
|
|
5358
7726
|
(isNumOrPxType(fromType) && isNumOrPxType(toType)), "Keyframes must be of the same dimension as the current value");
|
|
5359
7727
|
}
|
|
5360
7728
|
else {
|
|
5361
|
-
|
|
7729
|
+
exports.invariant(findDimensionValueType(to[i]) === toType, "All keyframes must be of the same type");
|
|
5362
7730
|
}
|
|
5363
7731
|
}
|
|
5364
7732
|
}
|
|
@@ -5376,7 +7744,7 @@ const checkAndConvertChangedValueTypes = (visualElement, target, origin = {}, tr
|
|
|
5376
7744
|
if (typeof to === "string") {
|
|
5377
7745
|
target[key] = parseFloat(to);
|
|
5378
7746
|
}
|
|
5379
|
-
else if (Array.isArray(to) && toType ===
|
|
7747
|
+
else if (Array.isArray(to) && toType === px) {
|
|
5380
7748
|
target[key] = to.map(parseFloat);
|
|
5381
7749
|
}
|
|
5382
7750
|
}
|
|
@@ -5480,7 +7848,7 @@ function updateMotionValuesFromProps(element, next, prev) {
|
|
|
5480
7848
|
for (const key in next) {
|
|
5481
7849
|
const nextValue = next[key];
|
|
5482
7850
|
const prevValue = prev[key];
|
|
5483
|
-
if (
|
|
7851
|
+
if (isMotionValue(nextValue)) {
|
|
5484
7852
|
/**
|
|
5485
7853
|
* If this is a motion value found in props or style, we want to add it
|
|
5486
7854
|
* to our visual element's motion value map.
|
|
@@ -5494,15 +7862,15 @@ function updateMotionValuesFromProps(element, next, prev) {
|
|
|
5494
7862
|
* and warn against mismatches.
|
|
5495
7863
|
*/
|
|
5496
7864
|
if (process.env.NODE_ENV === "development") {
|
|
5497
|
-
|
|
7865
|
+
warnOnce(nextValue.version === "10.1.0", `Attempting to mix Framer Motion versions ${nextValue.version} with 10.1.0 may not work as expected.`);
|
|
5498
7866
|
}
|
|
5499
7867
|
}
|
|
5500
|
-
else if (
|
|
7868
|
+
else if (isMotionValue(prevValue)) {
|
|
5501
7869
|
/**
|
|
5502
7870
|
* If we're swapping from a motion value to a static value,
|
|
5503
7871
|
* create a new motion value from that
|
|
5504
7872
|
*/
|
|
5505
|
-
element.addValue(key,
|
|
7873
|
+
element.addValue(key, motionValue(nextValue, { owner: element }));
|
|
5506
7874
|
if (isWillChangeMotionValue(willChange)) {
|
|
5507
7875
|
willChange.remove(key);
|
|
5508
7876
|
}
|
|
@@ -5520,7 +7888,7 @@ function updateMotionValuesFromProps(element, next, prev) {
|
|
|
5520
7888
|
}
|
|
5521
7889
|
else {
|
|
5522
7890
|
const latestValue = element.getStaticValue(key);
|
|
5523
|
-
element.addValue(key,
|
|
7891
|
+
element.addValue(key, motionValue(latestValue !== undefined ? latestValue : nextValue, { owner: element }));
|
|
5524
7892
|
}
|
|
5525
7893
|
}
|
|
5526
7894
|
}
|
|
@@ -5610,7 +7978,7 @@ class VisualElement {
|
|
|
5610
7978
|
this.triggerBuild();
|
|
5611
7979
|
this.renderInstance(this.current, this.renderState, this.props.style, this.projection);
|
|
5612
7980
|
};
|
|
5613
|
-
this.scheduleRender = () =>
|
|
7981
|
+
this.scheduleRender = () => sync.render(this.render, false, true);
|
|
5614
7982
|
const { latestValues, renderState } = visualState;
|
|
5615
7983
|
this.latestValues = latestValues;
|
|
5616
7984
|
this.baseTarget = { ...latestValues };
|
|
@@ -5641,7 +8009,7 @@ class VisualElement {
|
|
|
5641
8009
|
const { willChange, ...initialMotionValues } = this.scrapeMotionValuesFromProps(props, {});
|
|
5642
8010
|
for (const key in initialMotionValues) {
|
|
5643
8011
|
const value = initialMotionValues[key];
|
|
5644
|
-
if (latestValues[key] !== undefined &&
|
|
8012
|
+
if (latestValues[key] !== undefined && isMotionValue(value)) {
|
|
5645
8013
|
value.set(latestValues[key], false);
|
|
5646
8014
|
if (isWillChangeMotionValue(willChange)) {
|
|
5647
8015
|
willChange.add(key);
|
|
@@ -5678,7 +8046,7 @@ class VisualElement {
|
|
|
5678
8046
|
? true
|
|
5679
8047
|
: prefersReducedMotion.current;
|
|
5680
8048
|
if (process.env.NODE_ENV !== "production") {
|
|
5681
|
-
|
|
8049
|
+
warnOnce(this.shouldReduceMotion !== true, "You have Reduced Motion enabled on your device. Animations may not appear as expected.");
|
|
5682
8050
|
}
|
|
5683
8051
|
if (this.parent)
|
|
5684
8052
|
this.parent.children.add(this);
|
|
@@ -5686,8 +8054,8 @@ class VisualElement {
|
|
|
5686
8054
|
}
|
|
5687
8055
|
unmount() {
|
|
5688
8056
|
this.projection && this.projection.unmount();
|
|
5689
|
-
|
|
5690
|
-
|
|
8057
|
+
cancelSync.update(this.notifyUpdate);
|
|
8058
|
+
cancelSync.render(this.render);
|
|
5691
8059
|
this.valueSubscriptions.forEach((remove) => remove());
|
|
5692
8060
|
this.removeFromVariantTree && this.removeFromVariantTree();
|
|
5693
8061
|
this.parent && this.parent.children.delete(this);
|
|
@@ -5700,11 +8068,11 @@ class VisualElement {
|
|
|
5700
8068
|
this.current = null;
|
|
5701
8069
|
}
|
|
5702
8070
|
bindToMotionValue(key, value) {
|
|
5703
|
-
const valueIsTransform =
|
|
8071
|
+
const valueIsTransform = transformProps.has(key);
|
|
5704
8072
|
const removeOnChange = value.on("change", (latestValue) => {
|
|
5705
8073
|
this.latestValues[key] = latestValue;
|
|
5706
8074
|
this.props.onUpdate &&
|
|
5707
|
-
|
|
8075
|
+
sync.update(this.notifyUpdate, false, true);
|
|
5708
8076
|
if (valueIsTransform && this.projection) {
|
|
5709
8077
|
this.projection.isTransformDirty = true;
|
|
5710
8078
|
}
|
|
@@ -5738,8 +8106,8 @@ class VisualElement {
|
|
|
5738
8106
|
isStrict) {
|
|
5739
8107
|
const strictMessage = "You have rendered a `motion` component within a `LazyMotion` component. This will break tree shaking. Import and render a `m` component instead.";
|
|
5740
8108
|
renderedProps.ignoreStrict
|
|
5741
|
-
?
|
|
5742
|
-
:
|
|
8109
|
+
? exports.warning(false, strictMessage)
|
|
8110
|
+
: exports.invariant(false, strictMessage);
|
|
5743
8111
|
}
|
|
5744
8112
|
for (let i = 0; i < numFeatures; i++) {
|
|
5745
8113
|
const name = featureNames[i];
|
|
@@ -5948,7 +8316,7 @@ class VisualElement {
|
|
|
5948
8316
|
}
|
|
5949
8317
|
let value = this.values.get(key);
|
|
5950
8318
|
if (value === undefined && defaultValue !== undefined) {
|
|
5951
|
-
value =
|
|
8319
|
+
value = motionValue(defaultValue, { owner: this });
|
|
5952
8320
|
this.addValue(key, value);
|
|
5953
8321
|
}
|
|
5954
8322
|
return value;
|
|
@@ -5991,7 +8359,7 @@ class VisualElement {
|
|
|
5991
8359
|
* so we can read the value from an alternative source, try that.
|
|
5992
8360
|
*/
|
|
5993
8361
|
const target = this.getBaseTargetFromProps(this.props, key);
|
|
5994
|
-
if (target !== undefined && !
|
|
8362
|
+
if (target !== undefined && !isMotionValue(target))
|
|
5995
8363
|
return target;
|
|
5996
8364
|
/**
|
|
5997
8365
|
* If the value was initially defined on initial, but it doesn't any more,
|
|
@@ -6004,7 +8372,7 @@ class VisualElement {
|
|
|
6004
8372
|
}
|
|
6005
8373
|
on(eventName, callback) {
|
|
6006
8374
|
if (!this.events[eventName]) {
|
|
6007
|
-
this.events[eventName] = new
|
|
8375
|
+
this.events[eventName] = new SubscriptionManager();
|
|
6008
8376
|
}
|
|
6009
8377
|
return this.events[eventName].add(callback);
|
|
6010
8378
|
}
|
|
@@ -6063,8 +8431,8 @@ function getComputedStyle$1(element) {
|
|
|
6063
8431
|
}
|
|
6064
8432
|
class HTMLVisualElement extends DOMVisualElement {
|
|
6065
8433
|
readValueFromInstance(instance, key) {
|
|
6066
|
-
if (
|
|
6067
|
-
const defaultType =
|
|
8434
|
+
if (transformProps.has(key)) {
|
|
8435
|
+
const defaultType = getDefaultValueType(key);
|
|
6068
8436
|
return defaultType ? defaultType.default || 0 : 0;
|
|
6069
8437
|
}
|
|
6070
8438
|
else {
|
|
@@ -6090,7 +8458,7 @@ class HTMLVisualElement extends DOMVisualElement {
|
|
|
6090
8458
|
delete this.childSubscription;
|
|
6091
8459
|
}
|
|
6092
8460
|
const { children } = this.props;
|
|
6093
|
-
if (
|
|
8461
|
+
if (isMotionValue(children)) {
|
|
6094
8462
|
this.childSubscription = children.on("change", (latest) => {
|
|
6095
8463
|
if (this.current)
|
|
6096
8464
|
this.current.textContent = `${latest}`;
|
|
@@ -6222,7 +8590,7 @@ class MeasureLayoutWithContext extends React__default["default"].Component {
|
|
|
6222
8590
|
* it's in charge of the exit animation and therefore should
|
|
6223
8591
|
* be in charge of the safe to remove. Otherwise we call it here.
|
|
6224
8592
|
*/
|
|
6225
|
-
|
|
8593
|
+
sync.postRender(() => {
|
|
6226
8594
|
const stack = projection.getStack();
|
|
6227
8595
|
if (!stack || !stack.members.length) {
|
|
6228
8596
|
this.safeToRemove();
|
|
@@ -6302,8 +8670,8 @@ class SVGVisualElement extends DOMVisualElement {
|
|
|
6302
8670
|
return props[key];
|
|
6303
8671
|
}
|
|
6304
8672
|
readValueFromInstance(instance, key) {
|
|
6305
|
-
if (
|
|
6306
|
-
const defaultType =
|
|
8673
|
+
if (transformProps.has(key)) {
|
|
8674
|
+
const defaultType = getDefaultValueType(key);
|
|
6307
8675
|
return defaultType ? defaultType.default || 0 : 0;
|
|
6308
8676
|
}
|
|
6309
8677
|
key = !camelCaseAttributes.has(key) ? camelToDash(key) : key;
|
|
@@ -6398,7 +8766,7 @@ function useForceUpdate() {
|
|
|
6398
8766
|
* Defer this to the end of the next animation frame in case there are multiple
|
|
6399
8767
|
* synchronous calls.
|
|
6400
8768
|
*/
|
|
6401
|
-
const deferredForceRender = React.useCallback(() =>
|
|
8769
|
+
const deferredForceRender = React.useCallback(() => sync.postRender(forceRender), [forceRender]);
|
|
6402
8770
|
return [deferredForceRender, forcedRenderCount];
|
|
6403
8771
|
}
|
|
6404
8772
|
|
|
@@ -6572,7 +8940,7 @@ function onlyElements(children) {
|
|
|
6572
8940
|
* @public
|
|
6573
8941
|
*/
|
|
6574
8942
|
const AnimatePresence = ({ children, custom, initial = true, onExitComplete, exitBeforeEnter, presenceAffectsLayout = true, mode = "sync", }) => {
|
|
6575
|
-
|
|
8943
|
+
exports.invariant(!exitBeforeEnter, "Replace exitBeforeEnter with mode='wait'");
|
|
6576
8944
|
// We want to force a re-render once all exiting animations have finished. We
|
|
6577
8945
|
// either use a local forceRender function, or one from a parent context if it exists.
|
|
6578
8946
|
let [forceRender] = useForceUpdate();
|
|
@@ -6800,10 +9168,10 @@ function checkReorder(order, value, offset, velocity) {
|
|
|
6800
9168
|
return order;
|
|
6801
9169
|
const item = order[index];
|
|
6802
9170
|
const nextLayout = nextItem.layout;
|
|
6803
|
-
const nextItemCenter =
|
|
9171
|
+
const nextItemCenter = mix(nextLayout.min, nextLayout.max, 0.5);
|
|
6804
9172
|
if ((nextOffset === 1 && item.layout.max + offset > nextItemCenter) ||
|
|
6805
9173
|
(nextOffset === -1 && item.layout.min + offset < nextItemCenter)) {
|
|
6806
|
-
return
|
|
9174
|
+
return moveItem(order, index, index + nextOffset);
|
|
6807
9175
|
}
|
|
6808
9176
|
return order;
|
|
6809
9177
|
}
|
|
@@ -6812,7 +9180,7 @@ function ReorderGroup({ children, as = "ul", axis = "y", onReorder, values, ...p
|
|
|
6812
9180
|
const Component = useConstant(() => motion(as));
|
|
6813
9181
|
const order = [];
|
|
6814
9182
|
const isReordering = React.useRef(false);
|
|
6815
|
-
|
|
9183
|
+
exports.invariant(Boolean(values), "Reorder.Group must be provided a values prop");
|
|
6816
9184
|
const context = {
|
|
6817
9185
|
axis,
|
|
6818
9186
|
registerItem: (value, layout) => {
|
|
@@ -6869,7 +9237,7 @@ function compareMin(a, b) {
|
|
|
6869
9237
|
* @public
|
|
6870
9238
|
*/
|
|
6871
9239
|
function useMotionValue(initial) {
|
|
6872
|
-
const value = useConstant(() =>
|
|
9240
|
+
const value = useConstant(() => motionValue(initial));
|
|
6873
9241
|
/**
|
|
6874
9242
|
* If this motion value is being used in static mode, like on
|
|
6875
9243
|
* the Framer canvas, force components to rerender when the motion
|
|
@@ -6883,6 +9251,24 @@ function useMotionValue(initial) {
|
|
|
6883
9251
|
return value;
|
|
6884
9252
|
}
|
|
6885
9253
|
|
|
9254
|
+
const isCustomValueType = (v) => {
|
|
9255
|
+
return typeof v === "object" && v.mix;
|
|
9256
|
+
};
|
|
9257
|
+
const getMixer = (v) => (isCustomValueType(v) ? v.mix : undefined);
|
|
9258
|
+
function transform(...args) {
|
|
9259
|
+
const useImmediate = !Array.isArray(args[0]);
|
|
9260
|
+
const argOffset = useImmediate ? 0 : -1;
|
|
9261
|
+
const inputValue = args[0 + argOffset];
|
|
9262
|
+
const inputRange = args[1 + argOffset];
|
|
9263
|
+
const outputRange = args[2 + argOffset];
|
|
9264
|
+
const options = args[3 + argOffset];
|
|
9265
|
+
const interpolator = interpolate(inputRange, outputRange, {
|
|
9266
|
+
mixer: getMixer(outputRange[0]),
|
|
9267
|
+
...options,
|
|
9268
|
+
});
|
|
9269
|
+
return useImmediate ? interpolator(inputValue) : interpolator;
|
|
9270
|
+
}
|
|
9271
|
+
|
|
6886
9272
|
function useCombineMotionValues(values, combineValues) {
|
|
6887
9273
|
/**
|
|
6888
9274
|
* Initialise the returned motion value. This remains the same between renders.
|
|
@@ -6905,11 +9291,11 @@ function useCombineMotionValues(values, combineValues) {
|
|
|
6905
9291
|
* schedule an update.
|
|
6906
9292
|
*/
|
|
6907
9293
|
useIsomorphicLayoutEffect(() => {
|
|
6908
|
-
const scheduleUpdate = () =>
|
|
9294
|
+
const scheduleUpdate = () => sync.update(updateValue, false, true);
|
|
6909
9295
|
const subscriptions = values.map((v) => v.on("change", scheduleUpdate));
|
|
6910
9296
|
return () => {
|
|
6911
9297
|
subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
6912
|
-
|
|
9298
|
+
cancelSync.update(updateValue);
|
|
6913
9299
|
};
|
|
6914
9300
|
});
|
|
6915
9301
|
return value;
|
|
@@ -6918,7 +9304,7 @@ function useCombineMotionValues(values, combineValues) {
|
|
|
6918
9304
|
function useTransform(input, inputRangeOrTransformer, outputRange, options) {
|
|
6919
9305
|
const transformer = typeof inputRangeOrTransformer === "function"
|
|
6920
9306
|
? inputRangeOrTransformer
|
|
6921
|
-
:
|
|
9307
|
+
: transform(inputRangeOrTransformer, outputRange, options);
|
|
6922
9308
|
return Array.isArray(input)
|
|
6923
9309
|
? useListTransform(input, transformer)
|
|
6924
9310
|
: useListTransform([input], ([latest]) => transformer(latest));
|
|
@@ -6936,7 +9322,7 @@ function useListTransform(values, transformer) {
|
|
|
6936
9322
|
}
|
|
6937
9323
|
|
|
6938
9324
|
function useDefaultMotionValue(value, defaultValue = 0) {
|
|
6939
|
-
return
|
|
9325
|
+
return isMotionValue(value) ? value : useMotionValue(defaultValue);
|
|
6940
9326
|
}
|
|
6941
9327
|
function ReorderItem({ children, style = {}, value, as = "li", onDrag, layout = true, ...props }, externalRef) {
|
|
6942
9328
|
const Component = useConstant(() => motion(as));
|
|
@@ -6947,7 +9333,7 @@ function ReorderItem({ children, style = {}, value, as = "li", onDrag, layout =
|
|
|
6947
9333
|
};
|
|
6948
9334
|
const zIndex = useTransform([point.x, point.y], ([latestX, latestY]) => latestX || latestY ? 1 : "unset");
|
|
6949
9335
|
const measuredLayout = React.useRef(null);
|
|
6950
|
-
|
|
9336
|
+
exports.invariant(Boolean(context), "Reorder.Item must be a child of Reorder.Group");
|
|
6951
9337
|
const { axis, registerItem, updateOrder } = context;
|
|
6952
9338
|
React.useEffect(() => {
|
|
6953
9339
|
registerItem(value, measuredLayout.current);
|
|
@@ -7019,12 +9405,12 @@ function useMotionTemplate(fragments, ...values) {
|
|
|
7019
9405
|
output += fragments[i];
|
|
7020
9406
|
const value = values[i];
|
|
7021
9407
|
if (value) {
|
|
7022
|
-
output +=
|
|
9408
|
+
output += isMotionValue(value) ? value.get() : value;
|
|
7023
9409
|
}
|
|
7024
9410
|
}
|
|
7025
9411
|
return output;
|
|
7026
9412
|
}
|
|
7027
|
-
return useCombineMotionValues(values.filter(
|
|
9413
|
+
return useCombineMotionValues(values.filter(isMotionValue), buildValue);
|
|
7028
9414
|
}
|
|
7029
9415
|
|
|
7030
9416
|
/**
|
|
@@ -7049,7 +9435,7 @@ function useMotionTemplate(fragments, ...values) {
|
|
|
7049
9435
|
function useSpring(source, config = {}) {
|
|
7050
9436
|
const { isStatic } = React.useContext(MotionConfigContext);
|
|
7051
9437
|
const activeSpringAnimation = React.useRef(null);
|
|
7052
|
-
const value = useMotionValue(
|
|
9438
|
+
const value = useMotionValue(isMotionValue(source) ? source.get() : source);
|
|
7053
9439
|
const stopAnimation = () => {
|
|
7054
9440
|
if (activeSpringAnimation.current) {
|
|
7055
9441
|
activeSpringAnimation.current.stop();
|
|
@@ -7064,7 +9450,7 @@ function useSpring(source, config = {}) {
|
|
|
7064
9450
|
if (isStatic)
|
|
7065
9451
|
return set(v);
|
|
7066
9452
|
stopAnimation();
|
|
7067
|
-
activeSpringAnimation.current =
|
|
9453
|
+
activeSpringAnimation.current = animateValue({
|
|
7068
9454
|
keyframes: [value.get(), v],
|
|
7069
9455
|
velocity: value.getVelocity(),
|
|
7070
9456
|
type: "spring",
|
|
@@ -7075,7 +9461,7 @@ function useSpring(source, config = {}) {
|
|
|
7075
9461
|
}, stopAnimation);
|
|
7076
9462
|
}, [JSON.stringify(config)]);
|
|
7077
9463
|
useIsomorphicLayoutEffect(() => {
|
|
7078
|
-
if (
|
|
9464
|
+
if (isMotionValue(source)) {
|
|
7079
9465
|
return source.on("change", (v) => value.set(parseFloat(v)));
|
|
7080
9466
|
}
|
|
7081
9467
|
}, [value]);
|
|
@@ -7103,14 +9489,449 @@ function useVelocity(value) {
|
|
|
7103
9489
|
return velocity;
|
|
7104
9490
|
}
|
|
7105
9491
|
|
|
9492
|
+
function resolveElements(elements, selectorCache) {
|
|
9493
|
+
var _a;
|
|
9494
|
+
if (typeof elements === "string") {
|
|
9495
|
+
if (selectorCache) {
|
|
9496
|
+
(_a = selectorCache[elements]) !== null && _a !== void 0 ? _a : (selectorCache[elements] = document.querySelectorAll(elements));
|
|
9497
|
+
elements = selectorCache[elements];
|
|
9498
|
+
}
|
|
9499
|
+
else {
|
|
9500
|
+
elements = document.querySelectorAll(elements);
|
|
9501
|
+
}
|
|
9502
|
+
}
|
|
9503
|
+
else if (elements instanceof Element) {
|
|
9504
|
+
elements = [elements];
|
|
9505
|
+
}
|
|
9506
|
+
/**
|
|
9507
|
+
* Return an empty array
|
|
9508
|
+
*/
|
|
9509
|
+
return Array.from(elements || []);
|
|
9510
|
+
}
|
|
9511
|
+
|
|
9512
|
+
const resizeHandlers = new WeakMap();
|
|
9513
|
+
let observer;
|
|
9514
|
+
function getElementSize(target, borderBoxSize) {
|
|
9515
|
+
if (borderBoxSize) {
|
|
9516
|
+
const { inlineSize, blockSize } = borderBoxSize[0];
|
|
9517
|
+
return { width: inlineSize, height: blockSize };
|
|
9518
|
+
}
|
|
9519
|
+
else if (target instanceof SVGElement && "getBBox" in target) {
|
|
9520
|
+
return target.getBBox();
|
|
9521
|
+
}
|
|
9522
|
+
else {
|
|
9523
|
+
return {
|
|
9524
|
+
width: target.offsetWidth,
|
|
9525
|
+
height: target.offsetHeight,
|
|
9526
|
+
};
|
|
9527
|
+
}
|
|
9528
|
+
}
|
|
9529
|
+
function notifyTarget({ target, contentRect, borderBoxSize, }) {
|
|
9530
|
+
var _a;
|
|
9531
|
+
(_a = resizeHandlers.get(target)) === null || _a === void 0 ? void 0 : _a.forEach((handler) => {
|
|
9532
|
+
handler({
|
|
9533
|
+
target,
|
|
9534
|
+
contentSize: contentRect,
|
|
9535
|
+
get size() {
|
|
9536
|
+
return getElementSize(target, borderBoxSize);
|
|
9537
|
+
},
|
|
9538
|
+
});
|
|
9539
|
+
});
|
|
9540
|
+
}
|
|
9541
|
+
function notifyAll(entries) {
|
|
9542
|
+
entries.forEach(notifyTarget);
|
|
9543
|
+
}
|
|
9544
|
+
function createResizeObserver() {
|
|
9545
|
+
if (typeof ResizeObserver === "undefined")
|
|
9546
|
+
return;
|
|
9547
|
+
observer = new ResizeObserver(notifyAll);
|
|
9548
|
+
}
|
|
9549
|
+
function resizeElement(target, handler) {
|
|
9550
|
+
if (!observer)
|
|
9551
|
+
createResizeObserver();
|
|
9552
|
+
const elements = resolveElements(target);
|
|
9553
|
+
elements.forEach((element) => {
|
|
9554
|
+
let elementHandlers = resizeHandlers.get(element);
|
|
9555
|
+
if (!elementHandlers) {
|
|
9556
|
+
elementHandlers = new Set();
|
|
9557
|
+
resizeHandlers.set(element, elementHandlers);
|
|
9558
|
+
}
|
|
9559
|
+
elementHandlers.add(handler);
|
|
9560
|
+
observer === null || observer === void 0 ? void 0 : observer.observe(element);
|
|
9561
|
+
});
|
|
9562
|
+
return () => {
|
|
9563
|
+
elements.forEach((element) => {
|
|
9564
|
+
const elementHandlers = resizeHandlers.get(element);
|
|
9565
|
+
elementHandlers === null || elementHandlers === void 0 ? void 0 : elementHandlers.delete(handler);
|
|
9566
|
+
if (!(elementHandlers === null || elementHandlers === void 0 ? void 0 : elementHandlers.size)) {
|
|
9567
|
+
observer === null || observer === void 0 ? void 0 : observer.unobserve(element);
|
|
9568
|
+
}
|
|
9569
|
+
});
|
|
9570
|
+
};
|
|
9571
|
+
}
|
|
9572
|
+
|
|
9573
|
+
const windowCallbacks = new Set();
|
|
9574
|
+
let windowResizeHandler;
|
|
9575
|
+
function createWindowResizeHandler() {
|
|
9576
|
+
windowResizeHandler = () => {
|
|
9577
|
+
const size = {
|
|
9578
|
+
width: window.innerWidth,
|
|
9579
|
+
height: window.innerHeight,
|
|
9580
|
+
};
|
|
9581
|
+
const info = {
|
|
9582
|
+
target: window,
|
|
9583
|
+
size,
|
|
9584
|
+
contentSize: size,
|
|
9585
|
+
};
|
|
9586
|
+
windowCallbacks.forEach((callback) => callback(info));
|
|
9587
|
+
};
|
|
9588
|
+
window.addEventListener("resize", windowResizeHandler);
|
|
9589
|
+
}
|
|
9590
|
+
function resizeWindow(callback) {
|
|
9591
|
+
windowCallbacks.add(callback);
|
|
9592
|
+
if (!windowResizeHandler)
|
|
9593
|
+
createWindowResizeHandler();
|
|
9594
|
+
return () => {
|
|
9595
|
+
windowCallbacks.delete(callback);
|
|
9596
|
+
if (!windowCallbacks.size && windowResizeHandler) {
|
|
9597
|
+
windowResizeHandler = undefined;
|
|
9598
|
+
}
|
|
9599
|
+
};
|
|
9600
|
+
}
|
|
9601
|
+
|
|
9602
|
+
function resize(a, b) {
|
|
9603
|
+
return typeof a === "function" ? resizeWindow(a) : resizeElement(a, b);
|
|
9604
|
+
}
|
|
9605
|
+
|
|
9606
|
+
/**
|
|
9607
|
+
* A time in milliseconds, beyond which we consider the scroll velocity to be 0.
|
|
9608
|
+
*/
|
|
9609
|
+
const maxElapsed = 50;
|
|
9610
|
+
const createAxisInfo = () => ({
|
|
9611
|
+
current: 0,
|
|
9612
|
+
offset: [],
|
|
9613
|
+
progress: 0,
|
|
9614
|
+
scrollLength: 0,
|
|
9615
|
+
targetOffset: 0,
|
|
9616
|
+
targetLength: 0,
|
|
9617
|
+
containerLength: 0,
|
|
9618
|
+
velocity: 0,
|
|
9619
|
+
});
|
|
9620
|
+
const createScrollInfo = () => ({
|
|
9621
|
+
time: 0,
|
|
9622
|
+
x: createAxisInfo(),
|
|
9623
|
+
y: createAxisInfo(),
|
|
9624
|
+
});
|
|
9625
|
+
const keys = {
|
|
9626
|
+
x: {
|
|
9627
|
+
length: "Width",
|
|
9628
|
+
position: "Left",
|
|
9629
|
+
},
|
|
9630
|
+
y: {
|
|
9631
|
+
length: "Height",
|
|
9632
|
+
position: "Top",
|
|
9633
|
+
},
|
|
9634
|
+
};
|
|
9635
|
+
function updateAxisInfo(element, axisName, info, time) {
|
|
9636
|
+
const axis = info[axisName];
|
|
9637
|
+
const { length, position } = keys[axisName];
|
|
9638
|
+
const prev = axis.current;
|
|
9639
|
+
const prevTime = info.time;
|
|
9640
|
+
axis.current = element["scroll" + position];
|
|
9641
|
+
axis.scrollLength = element["scroll" + length] - element["client" + length];
|
|
9642
|
+
axis.offset.length = 0;
|
|
9643
|
+
axis.offset[0] = 0;
|
|
9644
|
+
axis.offset[1] = axis.scrollLength;
|
|
9645
|
+
axis.progress = progress(0, axis.scrollLength, axis.current);
|
|
9646
|
+
const elapsed = time - prevTime;
|
|
9647
|
+
axis.velocity =
|
|
9648
|
+
elapsed > maxElapsed
|
|
9649
|
+
? 0
|
|
9650
|
+
: velocityPerSecond(axis.current - prev, elapsed);
|
|
9651
|
+
}
|
|
9652
|
+
function updateScrollInfo(element, info, time) {
|
|
9653
|
+
updateAxisInfo(element, "x", info, time);
|
|
9654
|
+
updateAxisInfo(element, "y", info, time);
|
|
9655
|
+
info.time = time;
|
|
9656
|
+
}
|
|
9657
|
+
|
|
9658
|
+
function calcInset(element, container) {
|
|
9659
|
+
let inset = { x: 0, y: 0 };
|
|
9660
|
+
let current = element;
|
|
9661
|
+
while (current && current !== container) {
|
|
9662
|
+
if (current instanceof HTMLElement) {
|
|
9663
|
+
inset.x += current.offsetLeft;
|
|
9664
|
+
inset.y += current.offsetTop;
|
|
9665
|
+
current = current.offsetParent;
|
|
9666
|
+
}
|
|
9667
|
+
else if (current instanceof SVGGraphicsElement && "getBBox" in current) {
|
|
9668
|
+
const { top, left } = current.getBBox();
|
|
9669
|
+
inset.x += left;
|
|
9670
|
+
inset.y += top;
|
|
9671
|
+
/**
|
|
9672
|
+
* Assign the next parent element as the <svg /> tag.
|
|
9673
|
+
*/
|
|
9674
|
+
while (current && current.tagName !== "svg") {
|
|
9675
|
+
current = current.parentNode;
|
|
9676
|
+
}
|
|
9677
|
+
}
|
|
9678
|
+
}
|
|
9679
|
+
return inset;
|
|
9680
|
+
}
|
|
9681
|
+
|
|
9682
|
+
const ScrollOffset = {
|
|
9683
|
+
Enter: [
|
|
9684
|
+
[0, 1],
|
|
9685
|
+
[1, 1],
|
|
9686
|
+
],
|
|
9687
|
+
Exit: [
|
|
9688
|
+
[0, 0],
|
|
9689
|
+
[1, 0],
|
|
9690
|
+
],
|
|
9691
|
+
Any: [
|
|
9692
|
+
[1, 0],
|
|
9693
|
+
[0, 1],
|
|
9694
|
+
],
|
|
9695
|
+
All: [
|
|
9696
|
+
[0, 0],
|
|
9697
|
+
[1, 1],
|
|
9698
|
+
],
|
|
9699
|
+
};
|
|
9700
|
+
|
|
9701
|
+
const namedEdges = {
|
|
9702
|
+
start: 0,
|
|
9703
|
+
center: 0.5,
|
|
9704
|
+
end: 1,
|
|
9705
|
+
};
|
|
9706
|
+
function resolveEdge(edge, length, inset = 0) {
|
|
9707
|
+
let delta = 0;
|
|
9708
|
+
/**
|
|
9709
|
+
* If we have this edge defined as a preset, replace the definition
|
|
9710
|
+
* with the numerical value.
|
|
9711
|
+
*/
|
|
9712
|
+
if (namedEdges[edge] !== undefined) {
|
|
9713
|
+
edge = namedEdges[edge];
|
|
9714
|
+
}
|
|
9715
|
+
/**
|
|
9716
|
+
* Handle unit values
|
|
9717
|
+
*/
|
|
9718
|
+
if (typeof edge === "string") {
|
|
9719
|
+
const asNumber = parseFloat(edge);
|
|
9720
|
+
if (edge.endsWith("px")) {
|
|
9721
|
+
delta = asNumber;
|
|
9722
|
+
}
|
|
9723
|
+
else if (edge.endsWith("%")) {
|
|
9724
|
+
edge = asNumber / 100;
|
|
9725
|
+
}
|
|
9726
|
+
else if (edge.endsWith("vw")) {
|
|
9727
|
+
delta = (asNumber / 100) * document.documentElement.clientWidth;
|
|
9728
|
+
}
|
|
9729
|
+
else if (edge.endsWith("vh")) {
|
|
9730
|
+
delta = (asNumber / 100) * document.documentElement.clientHeight;
|
|
9731
|
+
}
|
|
9732
|
+
else {
|
|
9733
|
+
edge = asNumber;
|
|
9734
|
+
}
|
|
9735
|
+
}
|
|
9736
|
+
/**
|
|
9737
|
+
* If the edge is defined as a number, handle as a progress value.
|
|
9738
|
+
*/
|
|
9739
|
+
if (typeof edge === "number") {
|
|
9740
|
+
delta = length * edge;
|
|
9741
|
+
}
|
|
9742
|
+
return inset + delta;
|
|
9743
|
+
}
|
|
9744
|
+
|
|
9745
|
+
const defaultOffset = [0, 0];
|
|
9746
|
+
function resolveOffset(offset, containerLength, targetLength, targetInset) {
|
|
9747
|
+
let offsetDefinition = Array.isArray(offset) ? offset : defaultOffset;
|
|
9748
|
+
let targetPoint = 0;
|
|
9749
|
+
let containerPoint = 0;
|
|
9750
|
+
if (typeof offset === "number") {
|
|
9751
|
+
/**
|
|
9752
|
+
* If we're provided offset: [0, 0.5, 1] then each number x should become
|
|
9753
|
+
* [x, x], so we default to the behaviour of mapping 0 => 0 of both target
|
|
9754
|
+
* and container etc.
|
|
9755
|
+
*/
|
|
9756
|
+
offsetDefinition = [offset, offset];
|
|
9757
|
+
}
|
|
9758
|
+
else if (typeof offset === "string") {
|
|
9759
|
+
offset = offset.trim();
|
|
9760
|
+
if (offset.includes(" ")) {
|
|
9761
|
+
offsetDefinition = offset.split(" ");
|
|
9762
|
+
}
|
|
9763
|
+
else {
|
|
9764
|
+
/**
|
|
9765
|
+
* If we're provided a definition like "100px" then we want to apply
|
|
9766
|
+
* that only to the top of the target point, leaving the container at 0.
|
|
9767
|
+
* Whereas a named offset like "end" should be applied to both.
|
|
9768
|
+
*/
|
|
9769
|
+
offsetDefinition = [offset, namedEdges[offset] ? offset : `0`];
|
|
9770
|
+
}
|
|
9771
|
+
}
|
|
9772
|
+
targetPoint = resolveEdge(offsetDefinition[0], targetLength, targetInset);
|
|
9773
|
+
containerPoint = resolveEdge(offsetDefinition[1], containerLength);
|
|
9774
|
+
return targetPoint - containerPoint;
|
|
9775
|
+
}
|
|
9776
|
+
|
|
9777
|
+
const point = { x: 0, y: 0 };
|
|
9778
|
+
function resolveOffsets(container, info, options) {
|
|
9779
|
+
let { offset: offsetDefinition = ScrollOffset.All } = options;
|
|
9780
|
+
const { target = container, axis = "y" } = options;
|
|
9781
|
+
const lengthLabel = axis === "y" ? "height" : "width";
|
|
9782
|
+
const inset = target !== container ? calcInset(target, container) : point;
|
|
9783
|
+
/**
|
|
9784
|
+
* Measure the target and container. If they're the same thing then we
|
|
9785
|
+
* use the container's scrollWidth/Height as the target, from there
|
|
9786
|
+
* all other calculations can remain the same.
|
|
9787
|
+
*/
|
|
9788
|
+
const targetSize = target === container
|
|
9789
|
+
? { width: container.scrollWidth, height: container.scrollHeight }
|
|
9790
|
+
: { width: target.clientWidth, height: target.clientHeight };
|
|
9791
|
+
const containerSize = {
|
|
9792
|
+
width: container.clientWidth,
|
|
9793
|
+
height: container.clientHeight,
|
|
9794
|
+
};
|
|
9795
|
+
/**
|
|
9796
|
+
* Reset the length of the resolved offset array rather than creating a new one.
|
|
9797
|
+
* TODO: More reusable data structures for targetSize/containerSize would also be good.
|
|
9798
|
+
*/
|
|
9799
|
+
info[axis].offset.length = 0;
|
|
9800
|
+
/**
|
|
9801
|
+
* Populate the offset array by resolving the user's offset definition into
|
|
9802
|
+
* a list of pixel scroll offets.
|
|
9803
|
+
*/
|
|
9804
|
+
let hasChanged = !info[axis].interpolate;
|
|
9805
|
+
const numOffsets = offsetDefinition.length;
|
|
9806
|
+
for (let i = 0; i < numOffsets; i++) {
|
|
9807
|
+
const offset = resolveOffset(offsetDefinition[i], containerSize[lengthLabel], targetSize[lengthLabel], inset[axis]);
|
|
9808
|
+
if (!hasChanged && offset !== info[axis].interpolatorOffsets[i]) {
|
|
9809
|
+
hasChanged = true;
|
|
9810
|
+
}
|
|
9811
|
+
info[axis].offset[i] = offset;
|
|
9812
|
+
}
|
|
9813
|
+
/**
|
|
9814
|
+
* If the pixel scroll offsets have changed, create a new interpolator function
|
|
9815
|
+
* to map scroll value into a progress.
|
|
9816
|
+
*/
|
|
9817
|
+
if (hasChanged) {
|
|
9818
|
+
info[axis].interpolate = interpolate(info[axis].offset, defaultOffset$1(offsetDefinition));
|
|
9819
|
+
info[axis].interpolatorOffsets = [...info[axis].offset];
|
|
9820
|
+
}
|
|
9821
|
+
info[axis].progress = info[axis].interpolate(info[axis].current);
|
|
9822
|
+
}
|
|
9823
|
+
|
|
9824
|
+
function measure(container, target = container, info) {
|
|
9825
|
+
/**
|
|
9826
|
+
* Find inset of target within scrollable container
|
|
9827
|
+
*/
|
|
9828
|
+
info.x.targetOffset = 0;
|
|
9829
|
+
info.y.targetOffset = 0;
|
|
9830
|
+
if (target !== container) {
|
|
9831
|
+
let node = target;
|
|
9832
|
+
while (node && node !== container) {
|
|
9833
|
+
info.x.targetOffset += node.offsetLeft;
|
|
9834
|
+
info.y.targetOffset += node.offsetTop;
|
|
9835
|
+
node = node.offsetParent;
|
|
9836
|
+
}
|
|
9837
|
+
}
|
|
9838
|
+
info.x.targetLength =
|
|
9839
|
+
target === container ? target.scrollWidth : target.clientWidth;
|
|
9840
|
+
info.y.targetLength =
|
|
9841
|
+
target === container ? target.scrollHeight : target.clientHeight;
|
|
9842
|
+
info.x.containerLength = container.clientWidth;
|
|
9843
|
+
info.y.containerLength = container.clientHeight;
|
|
9844
|
+
}
|
|
9845
|
+
function createOnScrollHandler(element, onScroll, info, options = {}) {
|
|
9846
|
+
return {
|
|
9847
|
+
measure: () => measure(element, options.target, info),
|
|
9848
|
+
update: (time) => {
|
|
9849
|
+
updateScrollInfo(element, info, time);
|
|
9850
|
+
if (options.offset || options.target) {
|
|
9851
|
+
resolveOffsets(element, info, options);
|
|
9852
|
+
}
|
|
9853
|
+
},
|
|
9854
|
+
notify: () => onScroll(info),
|
|
9855
|
+
};
|
|
9856
|
+
}
|
|
9857
|
+
|
|
9858
|
+
const scrollListeners = new WeakMap();
|
|
9859
|
+
const resizeListeners = new WeakMap();
|
|
9860
|
+
const onScrollHandlers = new WeakMap();
|
|
9861
|
+
const getEventTarget = (element) => element === document.documentElement ? window : element;
|
|
9862
|
+
function scroll(onScroll, { container = document.documentElement, ...options } = {}) {
|
|
9863
|
+
let containerHandlers = onScrollHandlers.get(container);
|
|
9864
|
+
/**
|
|
9865
|
+
* Get the onScroll handlers for this container.
|
|
9866
|
+
* If one isn't found, create a new one.
|
|
9867
|
+
*/
|
|
9868
|
+
if (!containerHandlers) {
|
|
9869
|
+
containerHandlers = new Set();
|
|
9870
|
+
onScrollHandlers.set(container, containerHandlers);
|
|
9871
|
+
}
|
|
9872
|
+
/**
|
|
9873
|
+
* Create a new onScroll handler for the provided callback.
|
|
9874
|
+
*/
|
|
9875
|
+
const info = createScrollInfo();
|
|
9876
|
+
const containerHandler = createOnScrollHandler(container, onScroll, info, options);
|
|
9877
|
+
containerHandlers.add(containerHandler);
|
|
9878
|
+
/**
|
|
9879
|
+
* Check if there's a scroll event listener for this container.
|
|
9880
|
+
* If not, create one.
|
|
9881
|
+
*/
|
|
9882
|
+
if (!scrollListeners.has(container)) {
|
|
9883
|
+
const listener = () => {
|
|
9884
|
+
const time = performance.now();
|
|
9885
|
+
for (const handler of containerHandlers)
|
|
9886
|
+
handler.measure();
|
|
9887
|
+
for (const handler of containerHandlers)
|
|
9888
|
+
handler.update(time);
|
|
9889
|
+
for (const handler of containerHandlers)
|
|
9890
|
+
handler.notify();
|
|
9891
|
+
};
|
|
9892
|
+
scrollListeners.set(container, listener);
|
|
9893
|
+
const target = getEventTarget(container);
|
|
9894
|
+
window.addEventListener("resize", listener, { passive: true });
|
|
9895
|
+
if (container !== document.documentElement) {
|
|
9896
|
+
resizeListeners.set(container, resize(container, listener));
|
|
9897
|
+
}
|
|
9898
|
+
target.addEventListener("scroll", listener, { passive: true });
|
|
9899
|
+
}
|
|
9900
|
+
const listener = scrollListeners.get(container);
|
|
9901
|
+
const onLoadProcesss = requestAnimationFrame(listener);
|
|
9902
|
+
return () => {
|
|
9903
|
+
var _a;
|
|
9904
|
+
cancelAnimationFrame(onLoadProcesss);
|
|
9905
|
+
/**
|
|
9906
|
+
* Check if we even have any handlers for this container.
|
|
9907
|
+
*/
|
|
9908
|
+
const currentHandlers = onScrollHandlers.get(container);
|
|
9909
|
+
if (!currentHandlers)
|
|
9910
|
+
return;
|
|
9911
|
+
currentHandlers.delete(containerHandler);
|
|
9912
|
+
if (currentHandlers.size)
|
|
9913
|
+
return;
|
|
9914
|
+
/**
|
|
9915
|
+
* If no more handlers, remove the scroll listener too.
|
|
9916
|
+
*/
|
|
9917
|
+
const scrollListener = scrollListeners.get(container);
|
|
9918
|
+
scrollListeners.delete(container);
|
|
9919
|
+
if (scrollListener) {
|
|
9920
|
+
getEventTarget(container).removeEventListener("scroll", scrollListener);
|
|
9921
|
+
(_a = resizeListeners.get(container)) === null || _a === void 0 ? void 0 : _a();
|
|
9922
|
+
window.removeEventListener("resize", scrollListener);
|
|
9923
|
+
}
|
|
9924
|
+
};
|
|
9925
|
+
}
|
|
9926
|
+
|
|
7106
9927
|
function refWarning(name, ref) {
|
|
7107
|
-
|
|
9928
|
+
exports.warning(Boolean(!ref || ref.current), `You have defined a ${name} options but the provided ref is not yet hydrated, probably because it's defined higher up the tree. Try calling useScroll() in the same component as the ref, or setting its \`layoutEffect: false\` option.`);
|
|
7108
9929
|
}
|
|
7109
9930
|
const createScrollMotionValues = () => ({
|
|
7110
|
-
scrollX:
|
|
7111
|
-
scrollY:
|
|
7112
|
-
scrollXProgress:
|
|
7113
|
-
scrollYProgress:
|
|
9931
|
+
scrollX: motionValue(0),
|
|
9932
|
+
scrollY: motionValue(0),
|
|
9933
|
+
scrollXProgress: motionValue(0),
|
|
9934
|
+
scrollYProgress: motionValue(0),
|
|
7114
9935
|
});
|
|
7115
9936
|
function useScroll({ container, target, layoutEffect = true, ...options } = {}) {
|
|
7116
9937
|
const values = useConstant(createScrollMotionValues);
|
|
@@ -7120,7 +9941,7 @@ function useScroll({ container, target, layoutEffect = true, ...options } = {})
|
|
|
7120
9941
|
useLifecycleEffect(() => {
|
|
7121
9942
|
refWarning("target", target);
|
|
7122
9943
|
refWarning("container", container);
|
|
7123
|
-
return
|
|
9944
|
+
return scroll(({ x, y }) => {
|
|
7124
9945
|
values.scrollX.set(x.current);
|
|
7125
9946
|
values.scrollXProgress.set(x.progress);
|
|
7126
9947
|
values.scrollY.set(y.current);
|
|
@@ -7139,7 +9960,7 @@ function useScroll({ container, target, layoutEffect = true, ...options } = {})
|
|
|
7139
9960
|
*/
|
|
7140
9961
|
function useElementScroll(ref) {
|
|
7141
9962
|
if (process.env.NODE_ENV === "development") {
|
|
7142
|
-
|
|
9963
|
+
warnOnce(false, "useElementScroll is deprecated. Convert to useScroll({ container: ref }).");
|
|
7143
9964
|
}
|
|
7144
9965
|
return useScroll({ container: ref });
|
|
7145
9966
|
}
|
|
@@ -7149,7 +9970,7 @@ function useElementScroll(ref) {
|
|
|
7149
9970
|
*/
|
|
7150
9971
|
function useViewportScroll() {
|
|
7151
9972
|
if (process.env.NODE_ENV !== "production") {
|
|
7152
|
-
|
|
9973
|
+
warnOnce(false, "useViewportScroll is deprecated. Convert to useScroll().");
|
|
7153
9974
|
}
|
|
7154
9975
|
return useScroll();
|
|
7155
9976
|
}
|
|
@@ -7165,8 +9986,8 @@ function useAnimationFrame(callback) {
|
|
|
7165
9986
|
initialTimestamp.current = timestamp;
|
|
7166
9987
|
callback(timestamp - initialTimestamp.current, delta);
|
|
7167
9988
|
};
|
|
7168
|
-
|
|
7169
|
-
return () =>
|
|
9989
|
+
sync.update(provideTimeSinceStart, true);
|
|
9990
|
+
return () => cancelSync.update(provideTimeSinceStart);
|
|
7170
9991
|
}, [callback]);
|
|
7171
9992
|
}
|
|
7172
9993
|
|
|
@@ -7176,7 +9997,7 @@ function useTime() {
|
|
|
7176
9997
|
return time;
|
|
7177
9998
|
}
|
|
7178
9999
|
|
|
7179
|
-
class WillChangeMotionValue extends
|
|
10000
|
+
class WillChangeMotionValue extends MotionValue {
|
|
7180
10001
|
constructor() {
|
|
7181
10002
|
super(...arguments);
|
|
7182
10003
|
this.members = [];
|
|
@@ -7184,7 +10005,7 @@ class WillChangeMotionValue extends wrap.MotionValue {
|
|
|
7184
10005
|
}
|
|
7185
10006
|
add(name) {
|
|
7186
10007
|
let memberName;
|
|
7187
|
-
if (
|
|
10008
|
+
if (transformProps.has(name)) {
|
|
7188
10009
|
this.transforms.add(name);
|
|
7189
10010
|
memberName = "transform";
|
|
7190
10011
|
}
|
|
@@ -7194,19 +10015,19 @@ class WillChangeMotionValue extends wrap.MotionValue {
|
|
|
7194
10015
|
memberName = camelToDash(name);
|
|
7195
10016
|
}
|
|
7196
10017
|
if (memberName) {
|
|
7197
|
-
|
|
10018
|
+
addUniqueItem(this.members, memberName);
|
|
7198
10019
|
this.update();
|
|
7199
10020
|
}
|
|
7200
10021
|
}
|
|
7201
10022
|
remove(name) {
|
|
7202
|
-
if (
|
|
10023
|
+
if (transformProps.has(name)) {
|
|
7203
10024
|
this.transforms.delete(name);
|
|
7204
10025
|
if (!this.transforms.size) {
|
|
7205
|
-
|
|
10026
|
+
removeItem(this.members, "transform");
|
|
7206
10027
|
}
|
|
7207
10028
|
}
|
|
7208
10029
|
else {
|
|
7209
|
-
|
|
10030
|
+
removeItem(this.members, camelToDash(name));
|
|
7210
10031
|
}
|
|
7211
10032
|
this.update();
|
|
7212
10033
|
}
|
|
@@ -7261,7 +10082,7 @@ function useReducedMotion() {
|
|
|
7261
10082
|
!hasReducedMotionListener.current && initPrefersReducedMotion();
|
|
7262
10083
|
const [shouldReduceMotion] = React.useState(prefersReducedMotion.current);
|
|
7263
10084
|
if (process.env.NODE_ENV !== "production") {
|
|
7264
|
-
|
|
10085
|
+
warnOnce(shouldReduceMotion !== true, "You have Reduced Motion enabled on your device. Animations may not appear as expected.");
|
|
7265
10086
|
}
|
|
7266
10087
|
/**
|
|
7267
10088
|
* TODO See if people miss automatically updating shouldReduceMotion setting
|
|
@@ -7301,7 +10122,7 @@ function animationControls() {
|
|
|
7301
10122
|
return () => void subscribers.delete(visualElement);
|
|
7302
10123
|
},
|
|
7303
10124
|
start(definition, transitionOverride) {
|
|
7304
|
-
|
|
10125
|
+
exports.invariant(hasMounted, "controls.start() should only be called after a component has mounted. Consider calling within a useEffect hook.");
|
|
7305
10126
|
const animations = [];
|
|
7306
10127
|
subscribers.forEach((visualElement) => {
|
|
7307
10128
|
animations.push(animateVisualElement(visualElement, definition, {
|
|
@@ -7311,7 +10132,7 @@ function animationControls() {
|
|
|
7311
10132
|
return Promise.all(animations);
|
|
7312
10133
|
},
|
|
7313
10134
|
set(definition) {
|
|
7314
|
-
|
|
10135
|
+
exports.invariant(hasMounted, "controls.set() should only be called after a component has mounted. Consider calling within a useEffect hook.");
|
|
7315
10136
|
return subscribers.forEach((visualElement) => {
|
|
7316
10137
|
setValues(visualElement, definition);
|
|
7317
10138
|
});
|
|
@@ -7368,6 +10189,11 @@ function useAnimationControls() {
|
|
|
7368
10189
|
}
|
|
7369
10190
|
const useAnimation = useAnimationControls;
|
|
7370
10191
|
|
|
10192
|
+
const wrap = (min, max, v) => {
|
|
10193
|
+
const rangeSize = max - min;
|
|
10194
|
+
return ((((v - min) % rangeSize) + rangeSize) % rangeSize) + min;
|
|
10195
|
+
};
|
|
10196
|
+
|
|
7371
10197
|
/**
|
|
7372
10198
|
* Cycles through a series of visual properties. Can be used to toggle between or cycle through animations. It works similar to `useState` in React. It is provided an initial array of possible states, and returns an array of two arguments.
|
|
7373
10199
|
*
|
|
@@ -7400,7 +10226,7 @@ function useCycle(...items) {
|
|
|
7400
10226
|
const runCycle = React.useCallback((next) => {
|
|
7401
10227
|
index.current =
|
|
7402
10228
|
typeof next !== "number"
|
|
7403
|
-
? wrap
|
|
10229
|
+
? wrap(0, items.length, index.current + 1)
|
|
7404
10230
|
: next;
|
|
7405
10231
|
setItem(items[index.current]);
|
|
7406
10232
|
},
|
|
@@ -7411,6 +10237,46 @@ function useCycle(...items) {
|
|
|
7411
10237
|
return [item, runCycle];
|
|
7412
10238
|
}
|
|
7413
10239
|
|
|
10240
|
+
const thresholds = {
|
|
10241
|
+
any: 0,
|
|
10242
|
+
all: 1,
|
|
10243
|
+
};
|
|
10244
|
+
function inView(elementOrSelector, onStart, { root, margin: rootMargin, amount = "any" } = {}) {
|
|
10245
|
+
const elements = resolveElements(elementOrSelector);
|
|
10246
|
+
const activeIntersections = new WeakMap();
|
|
10247
|
+
const onIntersectionChange = (entries) => {
|
|
10248
|
+
entries.forEach((entry) => {
|
|
10249
|
+
const onEnd = activeIntersections.get(entry.target);
|
|
10250
|
+
/**
|
|
10251
|
+
* If there's no change to the intersection, we don't need to
|
|
10252
|
+
* do anything here.
|
|
10253
|
+
*/
|
|
10254
|
+
if (entry.isIntersecting === Boolean(onEnd))
|
|
10255
|
+
return;
|
|
10256
|
+
if (entry.isIntersecting) {
|
|
10257
|
+
const newOnEnd = onStart(entry);
|
|
10258
|
+
if (typeof newOnEnd === "function") {
|
|
10259
|
+
activeIntersections.set(entry.target, newOnEnd);
|
|
10260
|
+
}
|
|
10261
|
+
else {
|
|
10262
|
+
observer.unobserve(entry.target);
|
|
10263
|
+
}
|
|
10264
|
+
}
|
|
10265
|
+
else if (onEnd) {
|
|
10266
|
+
onEnd(entry);
|
|
10267
|
+
activeIntersections.delete(entry.target);
|
|
10268
|
+
}
|
|
10269
|
+
});
|
|
10270
|
+
};
|
|
10271
|
+
const observer = new IntersectionObserver(onIntersectionChange, {
|
|
10272
|
+
root,
|
|
10273
|
+
rootMargin,
|
|
10274
|
+
threshold: typeof amount === "number" ? amount : thresholds[amount],
|
|
10275
|
+
});
|
|
10276
|
+
elements.forEach((element) => observer.observe(element));
|
|
10277
|
+
return () => observer.disconnect();
|
|
10278
|
+
}
|
|
10279
|
+
|
|
7414
10280
|
function useInView(ref, { root, margin, amount, once = false } = {}) {
|
|
7415
10281
|
const [isInView, setInView] = React.useState(false);
|
|
7416
10282
|
React.useEffect(() => {
|
|
@@ -7425,7 +10291,7 @@ function useInView(ref, { root, margin, amount, once = false } = {}) {
|
|
|
7425
10291
|
margin,
|
|
7426
10292
|
amount: amount === "some" ? "any" : amount,
|
|
7427
10293
|
};
|
|
7428
|
-
return
|
|
10294
|
+
return inView(ref.current, onEnter, options);
|
|
7429
10295
|
}, [root, ref, margin, once]);
|
|
7430
10296
|
return isInView;
|
|
7431
10297
|
}
|
|
@@ -7585,11 +10451,11 @@ function useInstantTransition() {
|
|
|
7585
10451
|
/**
|
|
7586
10452
|
* Unblock after two animation frames, otherwise this will unblock too soon.
|
|
7587
10453
|
*/
|
|
7588
|
-
|
|
10454
|
+
sync.postRender(() => sync.postRender(() => (instantAnimationState.current = false)));
|
|
7589
10455
|
}, [forcedRenderCount]);
|
|
7590
10456
|
return (callback) => {
|
|
7591
10457
|
startInstantLayoutTransition(() => {
|
|
7592
|
-
|
|
10458
|
+
instantAnimationState.current = true;
|
|
7593
10459
|
forceUpdate();
|
|
7594
10460
|
callback();
|
|
7595
10461
|
});
|
|
@@ -7619,7 +10485,7 @@ function handoffOptimizedAppearAnimation(id, name, value,
|
|
|
7619
10485
|
* Framer Motion bundles where it's not needed.
|
|
7620
10486
|
*/
|
|
7621
10487
|
sync) {
|
|
7622
|
-
const storeId = appearStoreId(id,
|
|
10488
|
+
const storeId = appearStoreId(id, transformProps.has(name) ? "transform" : name);
|
|
7623
10489
|
const appearAnimation = appearAnimationStore.get(storeId);
|
|
7624
10490
|
if (!appearAnimation)
|
|
7625
10491
|
return 0;
|
|
@@ -7687,7 +10553,7 @@ function startOptimizedAppearAnimation(element, name, keyframes, options, onRead
|
|
|
7687
10553
|
*
|
|
7688
10554
|
* https://bugs.chromium.org/p/chromium/issues/detail?id=1406850
|
|
7689
10555
|
*/
|
|
7690
|
-
const readyAnimation =
|
|
10556
|
+
const readyAnimation = animateStyle(element, name, [keyframes[0], keyframes[0]],
|
|
7691
10557
|
/**
|
|
7692
10558
|
* 10 secs is basically just a super-safe duration to give Chrome
|
|
7693
10559
|
* long enough to get the animation ready.
|
|
@@ -7699,7 +10565,7 @@ function startOptimizedAppearAnimation(element, name, keyframes, options, onRead
|
|
|
7699
10565
|
});
|
|
7700
10566
|
const startAnimation = () => {
|
|
7701
10567
|
readyAnimation.cancel();
|
|
7702
|
-
const appearAnimation =
|
|
10568
|
+
const appearAnimation = animateStyle(element, name, keyframes, options);
|
|
7703
10569
|
if (document.timeline) {
|
|
7704
10570
|
appearAnimation.startTime = document.timeline.currentTime;
|
|
7705
10571
|
}
|
|
@@ -7711,7 +10577,7 @@ function startOptimizedAppearAnimation(element, name, keyframes, options, onRead
|
|
|
7711
10577
|
onReady(appearAnimation);
|
|
7712
10578
|
};
|
|
7713
10579
|
if (readyAnimation.ready) {
|
|
7714
|
-
readyAnimation.ready.then(startAnimation).catch(
|
|
10580
|
+
readyAnimation.ready.then(startAnimation).catch(noop);
|
|
7715
10581
|
}
|
|
7716
10582
|
else {
|
|
7717
10583
|
startAnimation();
|
|
@@ -7806,8 +10672,8 @@ function useInvertedScale(scale) {
|
|
|
7806
10672
|
let parentScaleX = useMotionValue(1);
|
|
7807
10673
|
let parentScaleY = useMotionValue(1);
|
|
7808
10674
|
const { visualElement } = React.useContext(MotionContext);
|
|
7809
|
-
|
|
7810
|
-
|
|
10675
|
+
exports.invariant(!!(scale || visualElement), "If no scale values are provided, useInvertedScale must be used within a child of another motion component.");
|
|
10676
|
+
exports.warning(hasWarned, "useInvertedScale is deprecated and will be removed in 3.0. Use the layout prop instead.");
|
|
7811
10677
|
hasWarned = true;
|
|
7812
10678
|
if (scale) {
|
|
7813
10679
|
parentScaleX = scale.scaleX || parentScaleX;
|
|
@@ -7825,54 +10691,11 @@ function useInvertedScale(scale) {
|
|
|
7825
10691
|
let id = 0;
|
|
7826
10692
|
const AnimateSharedLayout = ({ children }) => {
|
|
7827
10693
|
React__namespace.useEffect(() => {
|
|
7828
|
-
|
|
10694
|
+
exports.invariant(false, "AnimateSharedLayout is deprecated: https://www.framer.com/docs/guide-upgrade/##shared-layout-animations");
|
|
7829
10695
|
}, []);
|
|
7830
10696
|
return (React__namespace.createElement(LayoutGroup, { id: useConstant(() => `asl-${id++}`) }, children));
|
|
7831
10697
|
};
|
|
7832
10698
|
|
|
7833
|
-
exports.MotionValue = wrap.MotionValue;
|
|
7834
|
-
exports.animate = wrap.animate;
|
|
7835
|
-
exports.animateValue = wrap.animateValue;
|
|
7836
|
-
exports.anticipate = wrap.anticipate;
|
|
7837
|
-
exports.backIn = wrap.backIn;
|
|
7838
|
-
exports.backInOut = wrap.backInOut;
|
|
7839
|
-
exports.backOut = wrap.backOut;
|
|
7840
|
-
exports.circIn = wrap.circIn;
|
|
7841
|
-
exports.circInOut = wrap.circInOut;
|
|
7842
|
-
exports.circOut = wrap.circOut;
|
|
7843
|
-
exports.clamp = wrap.clamp;
|
|
7844
|
-
exports.color = wrap.color;
|
|
7845
|
-
exports.complex = wrap.complex;
|
|
7846
|
-
exports.cubicBezier = wrap.cubicBezier;
|
|
7847
|
-
exports.delay = wrap.delay;
|
|
7848
|
-
exports.distance = wrap.distance;
|
|
7849
|
-
exports.distance2D = wrap.distance2D;
|
|
7850
|
-
exports.easeIn = wrap.easeIn;
|
|
7851
|
-
exports.easeInOut = wrap.easeInOut;
|
|
7852
|
-
exports.easeOut = wrap.easeOut;
|
|
7853
|
-
exports.frameData = wrap.frameData;
|
|
7854
|
-
exports.inView = wrap.inView;
|
|
7855
|
-
exports.inertia = wrap.inertia;
|
|
7856
|
-
exports.interpolate = wrap.interpolate;
|
|
7857
|
-
Object.defineProperty(exports, 'invariant', {
|
|
7858
|
-
enumerable: true,
|
|
7859
|
-
get: function () { return wrap.invariant; }
|
|
7860
|
-
});
|
|
7861
|
-
exports.isMotionValue = wrap.isMotionValue;
|
|
7862
|
-
exports.mix = wrap.mix;
|
|
7863
|
-
exports.motionValue = wrap.motionValue;
|
|
7864
|
-
exports.pipe = wrap.pipe;
|
|
7865
|
-
exports.progress = wrap.progress;
|
|
7866
|
-
exports.px = wrap.px;
|
|
7867
|
-
exports.scroll = wrap.scroll;
|
|
7868
|
-
exports.spring = wrap.spring;
|
|
7869
|
-
exports.sync = wrap.sync;
|
|
7870
|
-
exports.transform = wrap.transform;
|
|
7871
|
-
Object.defineProperty(exports, 'warning', {
|
|
7872
|
-
enumerable: true,
|
|
7873
|
-
get: function () { return wrap.warning; }
|
|
7874
|
-
});
|
|
7875
|
-
exports.wrap = wrap.wrap;
|
|
7876
10699
|
exports.AnimatePresence = AnimatePresence;
|
|
7877
10700
|
exports.AnimateSharedLayout = AnimateSharedLayout;
|
|
7878
10701
|
exports.DragControls = DragControls;
|
|
@@ -7883,6 +10706,7 @@ exports.LazyMotion = LazyMotion;
|
|
|
7883
10706
|
exports.MotionConfig = MotionConfig;
|
|
7884
10707
|
exports.MotionConfigContext = MotionConfigContext;
|
|
7885
10708
|
exports.MotionContext = MotionContext;
|
|
10709
|
+
exports.MotionValue = MotionValue;
|
|
7886
10710
|
exports.PresenceContext = PresenceContext;
|
|
7887
10711
|
exports.Reorder = Reorder;
|
|
7888
10712
|
exports.SwitchLayoutGroupContext = SwitchLayoutGroupContext;
|
|
@@ -7890,28 +10714,60 @@ exports.VisualElement = VisualElement;
|
|
|
7890
10714
|
exports.addPointerEvent = addPointerEvent;
|
|
7891
10715
|
exports.addPointerInfo = addPointerInfo;
|
|
7892
10716
|
exports.addScaleCorrector = addScaleCorrector;
|
|
10717
|
+
exports.animate = animate;
|
|
10718
|
+
exports.animateValue = animateValue;
|
|
7893
10719
|
exports.animateVisualElement = animateVisualElement;
|
|
7894
10720
|
exports.animationControls = animationControls;
|
|
7895
10721
|
exports.animations = animations;
|
|
10722
|
+
exports.anticipate = anticipate;
|
|
10723
|
+
exports.backIn = backIn;
|
|
10724
|
+
exports.backInOut = backInOut;
|
|
10725
|
+
exports.backOut = backOut;
|
|
7896
10726
|
exports.buildTransform = buildTransform;
|
|
7897
10727
|
exports.calcLength = calcLength;
|
|
7898
10728
|
exports.checkTargetForNewValues = checkTargetForNewValues;
|
|
10729
|
+
exports.circIn = circIn;
|
|
10730
|
+
exports.circInOut = circInOut;
|
|
10731
|
+
exports.circOut = circOut;
|
|
10732
|
+
exports.clamp = clamp;
|
|
10733
|
+
exports.color = color;
|
|
10734
|
+
exports.complex = complex;
|
|
7899
10735
|
exports.createBox = createBox;
|
|
7900
10736
|
exports.createDomMotionComponent = createDomMotionComponent;
|
|
7901
10737
|
exports.createMotionComponent = createMotionComponent;
|
|
10738
|
+
exports.cubicBezier = cubicBezier;
|
|
10739
|
+
exports.delay = delay;
|
|
10740
|
+
exports.distance = distance;
|
|
10741
|
+
exports.distance2D = distance2D;
|
|
7902
10742
|
exports.domAnimation = domAnimation;
|
|
7903
10743
|
exports.domMax = domMax;
|
|
10744
|
+
exports.easeIn = easeIn;
|
|
10745
|
+
exports.easeInOut = easeInOut;
|
|
10746
|
+
exports.easeOut = easeOut;
|
|
7904
10747
|
exports.filterProps = filterProps;
|
|
10748
|
+
exports.frameData = frameData;
|
|
10749
|
+
exports.inertia = inertia;
|
|
10750
|
+
exports.interpolate = interpolate;
|
|
7905
10751
|
exports.isBrowser = isBrowser;
|
|
7906
10752
|
exports.isDragActive = isDragActive;
|
|
7907
10753
|
exports.isMotionComponent = isMotionComponent;
|
|
10754
|
+
exports.isMotionValue = isMotionValue;
|
|
7908
10755
|
exports.isValidMotionProp = isValidMotionProp;
|
|
7909
10756
|
exports.m = m;
|
|
7910
10757
|
exports.makeUseVisualState = makeUseVisualState;
|
|
10758
|
+
exports.mix = mix;
|
|
7911
10759
|
exports.motion = motion;
|
|
10760
|
+
exports.motionValue = motionValue;
|
|
7912
10761
|
exports.optimizedAppearDataAttribute = optimizedAppearDataAttribute;
|
|
10762
|
+
exports.pipe = pipe;
|
|
10763
|
+
exports.progress = progress;
|
|
10764
|
+
exports.px = px;
|
|
7913
10765
|
exports.resolveMotionValue = resolveMotionValue;
|
|
10766
|
+
exports.scroll = scroll;
|
|
10767
|
+
exports.spring = spring;
|
|
7914
10768
|
exports.startOptimizedAppearAnimation = startOptimizedAppearAnimation;
|
|
10769
|
+
exports.sync = sync;
|
|
10770
|
+
exports.transform = transform;
|
|
7915
10771
|
exports.unwrapMotionComponent = unwrapMotionComponent;
|
|
7916
10772
|
exports.useAnimation = useAnimation;
|
|
7917
10773
|
exports.useAnimationControls = useAnimationControls;
|
|
@@ -7943,3 +10799,4 @@ exports.useUnmountEffect = useUnmountEffect;
|
|
|
7943
10799
|
exports.useVelocity = useVelocity;
|
|
7944
10800
|
exports.useViewportScroll = useViewportScroll;
|
|
7945
10801
|
exports.useWillChange = useWillChange;
|
|
10802
|
+
exports.wrap = wrap;
|