motion 12.35.2 → 12.37.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/README.md +12 -15
- package/dist/motion.dev.js +224 -82
- package/dist/motion.js +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -39,8 +39,7 @@ npm install motion-v
|
|
|
39
39
|
|
|
40
40
|
Motion is available for [React](https://motion.dev/docs/react), [JavaScript](https://motion.dev/docs/quick-start) and [Vue](https://motion.dev/docs/vue).
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
<summary>React ⬇</summary>
|
|
42
|
+
### React
|
|
44
43
|
|
|
45
44
|
```jsx
|
|
46
45
|
import { motion } from "motion/react"
|
|
@@ -52,10 +51,9 @@ function Component() {
|
|
|
52
51
|
|
|
53
52
|
Get started with [Motion for React](https://motion.dev/docs/react).
|
|
54
53
|
|
|
55
|
-
|
|
54
|
+
**Note:** Framer Motion is now Motion. Import from `motion/react` instead of `framer-motion`.
|
|
56
55
|
|
|
57
|
-
|
|
58
|
-
<summary>JavaScript ⬇</summary>
|
|
56
|
+
### JS
|
|
59
57
|
|
|
60
58
|
```javascript
|
|
61
59
|
import { animate } from "motion"
|
|
@@ -65,10 +63,7 @@ animate("#box", { x: 100 })
|
|
|
65
63
|
|
|
66
64
|
Get started with [JavaScript](https://motion.dev/docs/quick-start).
|
|
67
65
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
<details>
|
|
71
|
-
<summary>Vue ⬇</summary>
|
|
66
|
+
### Vue
|
|
72
67
|
|
|
73
68
|
```html
|
|
74
69
|
<script>
|
|
@@ -80,19 +75,21 @@ Get started with [JavaScript](https://motion.dev/docs/quick-start).
|
|
|
80
75
|
|
|
81
76
|
Get started with [Motion for Vue](https://motion.dev/docs/vue).
|
|
82
77
|
|
|
83
|
-
|
|
78
|
+
## 🎓 Examples & tutorials
|
|
84
79
|
|
|
85
|
-
|
|
80
|
+
Browse 330+ [official examples](https://motion.dev/examples), with copy-paste code that'll level-up your animations whether you're a beginner or an expert.
|
|
86
81
|
|
|
87
|
-
|
|
82
|
+
Over 100 examples come with a full step-by-step [tutorial](https://motion.dev/tutorials).
|
|
88
83
|
|
|
89
84
|
## ⚡️ Motion+
|
|
90
85
|
|
|
91
86
|
A one-time payment, lifetime-updates membership:
|
|
92
87
|
|
|
93
|
-
- **
|
|
88
|
+
- **330+ examples**
|
|
89
|
+
- **100+ tutorials**
|
|
94
90
|
- **Premium APIs** like [Cursor](https://motion.dev/docs/cursor) and [Ticker](https://motion.dev/docs/react-ticker)
|
|
95
|
-
- **
|
|
91
|
+
- **Transition editor** for Cursor and VS Code
|
|
92
|
+
- **AI skills**
|
|
96
93
|
- **Private Discord**
|
|
97
94
|
- **Early access content**
|
|
98
95
|
|
|
@@ -132,7 +129,7 @@ Motion drives the animations on the Cursor homepage, and is working with Cursor
|
|
|
132
129
|
|
|
133
130
|
### Gold
|
|
134
131
|
|
|
135
|
-
<a href="https://
|
|
132
|
+
<a href="https://mintlify.com"><img alt="Mintlify" src="https://github.com/user-attachments/assets/2740db2f-1877-49ae-ae80-ba5d19ef1587" width="200px" height="120px"></a>
|
|
136
133
|
|
|
137
134
|
### Silver
|
|
138
135
|
|
package/dist/motion.dev.js
CHANGED
|
@@ -245,7 +245,11 @@
|
|
|
245
245
|
const backIn = /*@__PURE__*/ reverseEasing(backOut);
|
|
246
246
|
const backInOut = /*@__PURE__*/ mirrorEasing(backIn);
|
|
247
247
|
|
|
248
|
-
const anticipate = (p) =>
|
|
248
|
+
const anticipate = (p) => p >= 1
|
|
249
|
+
? 1
|
|
250
|
+
: (p *= 2) < 1
|
|
251
|
+
? 0.5 * backIn(p)
|
|
252
|
+
: 0.5 * (2 - Math.pow(2, -10 * (p - 1)));
|
|
249
253
|
|
|
250
254
|
const circIn = (p) => 1 - Math.sin(Math.acos(p));
|
|
251
255
|
const circOut = reverseEasing(circIn);
|
|
@@ -364,8 +368,7 @@
|
|
|
364
368
|
const queue = addToCurrentFrame ? thisFrame : nextFrame;
|
|
365
369
|
if (keepAlive)
|
|
366
370
|
toKeepAlive.add(callback);
|
|
367
|
-
|
|
368
|
-
queue.add(callback);
|
|
371
|
+
queue.add(callback);
|
|
369
372
|
return callback;
|
|
370
373
|
},
|
|
371
374
|
/**
|
|
@@ -390,7 +393,10 @@
|
|
|
390
393
|
return;
|
|
391
394
|
}
|
|
392
395
|
isProcessing = true;
|
|
393
|
-
|
|
396
|
+
// Swap this frame and the next to avoid GC
|
|
397
|
+
const prevFrame = thisFrame;
|
|
398
|
+
thisFrame = nextFrame;
|
|
399
|
+
nextFrame = prevFrame;
|
|
394
400
|
// Execute this frame
|
|
395
401
|
thisFrame.forEach(triggerCallback);
|
|
396
402
|
/**
|
|
@@ -429,11 +435,12 @@
|
|
|
429
435
|
}, {});
|
|
430
436
|
const { setup, read, resolveKeyframes, preUpdate, update, preRender, render, postRender, } = steps;
|
|
431
437
|
const processBatch = () => {
|
|
432
|
-
const
|
|
438
|
+
const useManualTiming = MotionGlobalConfig.useManualTiming;
|
|
439
|
+
const timestamp = useManualTiming
|
|
433
440
|
? state.timestamp
|
|
434
441
|
: performance.now();
|
|
435
442
|
runNextFrame = false;
|
|
436
|
-
if (!
|
|
443
|
+
if (!useManualTiming) {
|
|
437
444
|
state.delta = useDefaultElapsed
|
|
438
445
|
? 1000 / 60
|
|
439
446
|
: Math.max(Math.min(timestamp - state.timestamp, maxElapsed$1), 1);
|
|
@@ -1625,9 +1632,9 @@
|
|
|
1625
1632
|
};
|
|
1626
1633
|
}
|
|
1627
1634
|
|
|
1628
|
-
const isNotNull
|
|
1629
|
-
function getFinalKeyframe
|
|
1630
|
-
const resolvedKeyframes = keyframes.filter(isNotNull
|
|
1635
|
+
const isNotNull = (value) => value !== null;
|
|
1636
|
+
function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }, finalKeyframe, speed = 1) {
|
|
1637
|
+
const resolvedKeyframes = keyframes.filter(isNotNull);
|
|
1631
1638
|
const useFirstKeyframe = speed < 0 || (repeat && repeatType !== "loop" && repeat % 2 === 1);
|
|
1632
1639
|
const index = useFirstKeyframe ? 0 : resolvedKeyframes.length - 1;
|
|
1633
1640
|
return !index || finalKeyframe === undefined
|
|
@@ -1692,6 +1699,14 @@
|
|
|
1692
1699
|
* Playback speed as a factor. 0 would be stopped, -1 reverse and 2 double speed.
|
|
1693
1700
|
*/
|
|
1694
1701
|
this.playbackSpeed = 1;
|
|
1702
|
+
/**
|
|
1703
|
+
* Reusable state object for the delay phase to avoid
|
|
1704
|
+
* allocating a new object every frame.
|
|
1705
|
+
*/
|
|
1706
|
+
this.delayState = {
|
|
1707
|
+
done: false,
|
|
1708
|
+
value: undefined,
|
|
1709
|
+
};
|
|
1695
1710
|
/**
|
|
1696
1711
|
* This method is bound to the instance to fix a pattern where
|
|
1697
1712
|
* animation.stop is returned as a reference from a useEffect.
|
|
@@ -1853,9 +1868,14 @@
|
|
|
1853
1868
|
* This prevents delay: x, duration: 0 animations from finishing
|
|
1854
1869
|
* instantly.
|
|
1855
1870
|
*/
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1871
|
+
let state;
|
|
1872
|
+
if (isInDelayPhase) {
|
|
1873
|
+
this.delayState.value = keyframes[0];
|
|
1874
|
+
state = this.delayState;
|
|
1875
|
+
}
|
|
1876
|
+
else {
|
|
1877
|
+
state = frameGenerator.next(elapsed);
|
|
1878
|
+
}
|
|
1859
1879
|
if (mixKeyframes && !isInDelayPhase) {
|
|
1860
1880
|
state.value = mixKeyframes(state.value);
|
|
1861
1881
|
}
|
|
@@ -1870,7 +1890,7 @@
|
|
|
1870
1890
|
(this.state === "finished" || (this.state === "running" && done));
|
|
1871
1891
|
// TODO: The exception for inertia could be cleaner here
|
|
1872
1892
|
if (isAnimationFinished && type !== inertia) {
|
|
1873
|
-
state.value = getFinalKeyframe
|
|
1893
|
+
state.value = getFinalKeyframe(keyframes, this.options, finalKeyframe, this.speed);
|
|
1874
1894
|
}
|
|
1875
1895
|
if (onUpdate) {
|
|
1876
1896
|
onUpdate(state.value);
|
|
@@ -2165,8 +2185,18 @@
|
|
|
2165
2185
|
}
|
|
2166
2186
|
const positionalValues = {
|
|
2167
2187
|
// Dimensions
|
|
2168
|
-
width: ({ x }, { paddingLeft = "0", paddingRight = "0" }) =>
|
|
2169
|
-
|
|
2188
|
+
width: ({ x }, { paddingLeft = "0", paddingRight = "0", boxSizing }) => {
|
|
2189
|
+
const width = x.max - x.min;
|
|
2190
|
+
return boxSizing === "border-box"
|
|
2191
|
+
? width
|
|
2192
|
+
: width - parseFloat(paddingLeft) - parseFloat(paddingRight);
|
|
2193
|
+
},
|
|
2194
|
+
height: ({ y }, { paddingTop = "0", paddingBottom = "0", boxSizing }) => {
|
|
2195
|
+
const height = y.max - y.min;
|
|
2196
|
+
return boxSizing === "border-box"
|
|
2197
|
+
? height
|
|
2198
|
+
: height - parseFloat(paddingTop) - parseFloat(paddingBottom);
|
|
2199
|
+
},
|
|
2170
2200
|
top: (_bbox, { top }) => parseFloat(top),
|
|
2171
2201
|
left: (_bbox, { left }) => parseFloat(left),
|
|
2172
2202
|
bottom: ({ y }, { top }) => parseFloat(top) + (y.max - y.min),
|
|
@@ -2468,7 +2498,7 @@
|
|
|
2468
2498
|
this.animation.onfinish = () => {
|
|
2469
2499
|
this.finishedTime = this.time;
|
|
2470
2500
|
if (!pseudoElement) {
|
|
2471
|
-
const keyframe = getFinalKeyframe
|
|
2501
|
+
const keyframe = getFinalKeyframe(keyframes, this.options, finalKeyframe, this.speed);
|
|
2472
2502
|
if (this.updateMotionValue) {
|
|
2473
2503
|
this.updateMotionValue(keyframe);
|
|
2474
2504
|
}
|
|
@@ -2774,17 +2804,42 @@
|
|
|
2774
2804
|
/**
|
|
2775
2805
|
* A list of values that can be hardware-accelerated.
|
|
2776
2806
|
*/
|
|
2777
|
-
const acceleratedValues
|
|
2807
|
+
const acceleratedValues = new Set([
|
|
2778
2808
|
"opacity",
|
|
2779
2809
|
"clipPath",
|
|
2780
2810
|
"filter",
|
|
2781
2811
|
"transform",
|
|
2782
|
-
// TODO:
|
|
2812
|
+
// TODO: Can be accelerated but currently disabled until https://issues.chromium.org/issues/41491098 is resolved
|
|
2813
|
+
// or until we implement support for linear() easing.
|
|
2783
2814
|
// "background-color"
|
|
2784
2815
|
]);
|
|
2816
|
+
|
|
2817
|
+
const browserColorFunctions = /^(?:oklch|oklab|lab|lch|color|color-mix|light-dark)\(/;
|
|
2818
|
+
function hasBrowserOnlyColors(keyframes) {
|
|
2819
|
+
for (let i = 0; i < keyframes.length; i++) {
|
|
2820
|
+
if (typeof keyframes[i] === "string" &&
|
|
2821
|
+
browserColorFunctions.test(keyframes[i])) {
|
|
2822
|
+
return true;
|
|
2823
|
+
}
|
|
2824
|
+
}
|
|
2825
|
+
return false;
|
|
2826
|
+
}
|
|
2827
|
+
|
|
2828
|
+
const colorProperties = new Set([
|
|
2829
|
+
"color",
|
|
2830
|
+
"backgroundColor",
|
|
2831
|
+
"outlineColor",
|
|
2832
|
+
"fill",
|
|
2833
|
+
"stroke",
|
|
2834
|
+
"borderColor",
|
|
2835
|
+
"borderTopColor",
|
|
2836
|
+
"borderRightColor",
|
|
2837
|
+
"borderBottomColor",
|
|
2838
|
+
"borderLeftColor",
|
|
2839
|
+
]);
|
|
2785
2840
|
const supportsWaapi = /*@__PURE__*/ memo(() => Object.hasOwnProperty.call(Element.prototype, "animate"));
|
|
2786
2841
|
function supportsBrowserAnimation(options) {
|
|
2787
|
-
const { motionValue, name, repeatDelay, repeatType, damping, type } = options;
|
|
2842
|
+
const { motionValue, name, repeatDelay, repeatType, damping, type, keyframes, } = options;
|
|
2788
2843
|
const subject = motionValue?.owner?.current;
|
|
2789
2844
|
/**
|
|
2790
2845
|
* We use this check instead of isHTMLElement() because we explicitly
|
|
@@ -2798,7 +2853,13 @@
|
|
|
2798
2853
|
const { onUpdate, transformTemplate } = motionValue.owner.getProps();
|
|
2799
2854
|
return (supportsWaapi() &&
|
|
2800
2855
|
name &&
|
|
2801
|
-
|
|
2856
|
+
/**
|
|
2857
|
+
* Force WAAPI for color properties with browser-only color formats
|
|
2858
|
+
* (oklch, oklab, lab, lch, etc.) that the JS animation path can't parse.
|
|
2859
|
+
*/
|
|
2860
|
+
(acceleratedValues.has(name) ||
|
|
2861
|
+
(colorProperties.has(name) &&
|
|
2862
|
+
hasBrowserOnlyColors(keyframes))) &&
|
|
2802
2863
|
(name !== "transform" || !transformTemplate) &&
|
|
2803
2864
|
/**
|
|
2804
2865
|
* If we're outputting values to onUpdate then we can't use WAAPI as there's
|
|
@@ -2858,9 +2919,11 @@
|
|
|
2858
2919
|
* If we can't animate this value with the resolved keyframes
|
|
2859
2920
|
* then we should complete it immediately.
|
|
2860
2921
|
*/
|
|
2922
|
+
let canAnimateValue = true;
|
|
2861
2923
|
if (!canAnimate(keyframes, name, type, velocity)) {
|
|
2924
|
+
canAnimateValue = false;
|
|
2862
2925
|
if (MotionGlobalConfig.instantAnimations || !delay) {
|
|
2863
|
-
onUpdate?.(getFinalKeyframe
|
|
2926
|
+
onUpdate?.(getFinalKeyframe(keyframes, options, finalKeyframe));
|
|
2864
2927
|
}
|
|
2865
2928
|
keyframes[0] = keyframes[keyframes.length - 1];
|
|
2866
2929
|
makeAnimationInstant(options);
|
|
@@ -2895,15 +2958,29 @@
|
|
|
2895
2958
|
* Animate via WAAPI if possible. If this is a handoff animation, the optimised animation will be running via
|
|
2896
2959
|
* WAAPI. Therefore, this animation must be JS to ensure it runs "under" the
|
|
2897
2960
|
* optimised animation.
|
|
2961
|
+
*
|
|
2962
|
+
* Also skip WAAPI when keyframes aren't animatable, as the resolved
|
|
2963
|
+
* values may not be valid CSS and would trigger browser warnings.
|
|
2898
2964
|
*/
|
|
2899
|
-
const useWaapi =
|
|
2965
|
+
const useWaapi = canAnimateValue &&
|
|
2966
|
+
!isHandoff &&
|
|
2967
|
+
supportsBrowserAnimation(resolvedOptions);
|
|
2900
2968
|
const element = resolvedOptions.motionValue?.owner?.current;
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2969
|
+
let animation;
|
|
2970
|
+
if (useWaapi) {
|
|
2971
|
+
try {
|
|
2972
|
+
animation = new NativeAnimationExtended({
|
|
2973
|
+
...resolvedOptions,
|
|
2974
|
+
element,
|
|
2975
|
+
});
|
|
2976
|
+
}
|
|
2977
|
+
catch {
|
|
2978
|
+
animation = new JSAnimation(resolvedOptions);
|
|
2979
|
+
}
|
|
2980
|
+
}
|
|
2981
|
+
else {
|
|
2982
|
+
animation = new JSAnimation(resolvedOptions);
|
|
2983
|
+
}
|
|
2907
2984
|
animation.finished.then(() => {
|
|
2908
2985
|
this.notifyFinished();
|
|
2909
2986
|
}).catch(noop$1);
|
|
@@ -3081,8 +3158,11 @@
|
|
|
3081
3158
|
const animationMaps = new WeakMap();
|
|
3082
3159
|
const animationMapKey = (name, pseudoElement = "") => `${name}:${pseudoElement}`;
|
|
3083
3160
|
function getAnimationMap(element) {
|
|
3084
|
-
|
|
3085
|
-
|
|
3161
|
+
let map = animationMaps.get(element);
|
|
3162
|
+
if (!map) {
|
|
3163
|
+
map = new Map();
|
|
3164
|
+
animationMaps.set(element, map);
|
|
3165
|
+
}
|
|
3086
3166
|
return map;
|
|
3087
3167
|
}
|
|
3088
3168
|
|
|
@@ -3174,17 +3254,6 @@
|
|
|
3174
3254
|
return ease;
|
|
3175
3255
|
};
|
|
3176
3256
|
|
|
3177
|
-
const isNotNull = (value) => value !== null;
|
|
3178
|
-
function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }, finalKeyframe) {
|
|
3179
|
-
const resolvedKeyframes = keyframes.filter(isNotNull);
|
|
3180
|
-
const index = repeat && repeatType !== "loop" && repeat % 2 === 1
|
|
3181
|
-
? 0
|
|
3182
|
-
: resolvedKeyframes.length - 1;
|
|
3183
|
-
return !index || finalKeyframe === undefined
|
|
3184
|
-
? resolvedKeyframes[index]
|
|
3185
|
-
: finalKeyframe;
|
|
3186
|
-
}
|
|
3187
|
-
|
|
3188
3257
|
/**
|
|
3189
3258
|
* If `transition` has `inherit: true`, shallow-merge it with
|
|
3190
3259
|
* `parentTransition` (child keys win) and strip the `inherit` key.
|
|
@@ -3208,13 +3277,29 @@
|
|
|
3208
3277
|
return valueTransition;
|
|
3209
3278
|
}
|
|
3210
3279
|
|
|
3280
|
+
const orchestrationKeys = new Set([
|
|
3281
|
+
"when",
|
|
3282
|
+
"delay",
|
|
3283
|
+
"delayChildren",
|
|
3284
|
+
"staggerChildren",
|
|
3285
|
+
"staggerDirection",
|
|
3286
|
+
"repeat",
|
|
3287
|
+
"repeatType",
|
|
3288
|
+
"repeatDelay",
|
|
3289
|
+
"from",
|
|
3290
|
+
"elapsed",
|
|
3291
|
+
]);
|
|
3211
3292
|
/**
|
|
3212
3293
|
* Decide whether a transition is defined on a given Transition.
|
|
3213
3294
|
* This filters out orchestration options and returns true
|
|
3214
3295
|
* if any options are left.
|
|
3215
3296
|
*/
|
|
3216
|
-
function isTransitionDefined(
|
|
3217
|
-
|
|
3297
|
+
function isTransitionDefined(transition) {
|
|
3298
|
+
for (const key in transition) {
|
|
3299
|
+
if (!orchestrationKeys.has(key))
|
|
3300
|
+
return true;
|
|
3301
|
+
}
|
|
3302
|
+
return false;
|
|
3218
3303
|
}
|
|
3219
3304
|
|
|
3220
3305
|
const animateMotionValue = (name, value, target, transition = {}, element, isHandoff) => (onComplete) => {
|
|
@@ -4346,19 +4431,6 @@
|
|
|
4346
4431
|
return true;
|
|
4347
4432
|
});
|
|
4348
4433
|
|
|
4349
|
-
/**
|
|
4350
|
-
* A list of values that can be hardware-accelerated.
|
|
4351
|
-
*/
|
|
4352
|
-
const acceleratedValues = new Set([
|
|
4353
|
-
"opacity",
|
|
4354
|
-
"clipPath",
|
|
4355
|
-
"filter",
|
|
4356
|
-
"transform",
|
|
4357
|
-
// TODO: Can be accelerated but currently disabled until https://issues.chromium.org/issues/41491098 is resolved
|
|
4358
|
-
// or until we implement support for linear() easing.
|
|
4359
|
-
// "background-color"
|
|
4360
|
-
]);
|
|
4361
|
-
|
|
4362
4434
|
function resolveElements(elementOrSelector, scope, selectorCache) {
|
|
4363
4435
|
if (elementOrSelector == null) {
|
|
4364
4436
|
return [];
|
|
@@ -4504,7 +4576,9 @@
|
|
|
4504
4576
|
* that works across iframes
|
|
4505
4577
|
*/
|
|
4506
4578
|
function isHTMLElement(element) {
|
|
4507
|
-
return isObject(element) &&
|
|
4579
|
+
return (isObject(element) &&
|
|
4580
|
+
"offsetHeight" in element &&
|
|
4581
|
+
!("ownerSVGElement" in element));
|
|
4508
4582
|
}
|
|
4509
4583
|
|
|
4510
4584
|
const translateAlias$1 = {
|
|
@@ -5242,6 +5316,7 @@
|
|
|
5242
5316
|
activeAnimation.stop();
|
|
5243
5317
|
activeAnimation = null;
|
|
5244
5318
|
}
|
|
5319
|
+
value.animation = undefined;
|
|
5245
5320
|
};
|
|
5246
5321
|
const startAnimation = () => {
|
|
5247
5322
|
const currentValue = asNumber$1(value.get());
|
|
@@ -5273,8 +5348,10 @@
|
|
|
5273
5348
|
// multiple calls within the same frame (e.g. rapid mouse events)
|
|
5274
5349
|
const scheduleAnimation = () => {
|
|
5275
5350
|
startAnimation();
|
|
5351
|
+
value.animation = activeAnimation ?? undefined;
|
|
5276
5352
|
value["events"].animationStart?.notify();
|
|
5277
5353
|
activeAnimation?.then(() => {
|
|
5354
|
+
value.animation = undefined;
|
|
5278
5355
|
value["events"].animationComplete?.notify();
|
|
5279
5356
|
});
|
|
5280
5357
|
};
|
|
@@ -5284,7 +5361,16 @@
|
|
|
5284
5361
|
frame.postRender(scheduleAnimation);
|
|
5285
5362
|
}, stopAnimation);
|
|
5286
5363
|
if (isMotionValue(source)) {
|
|
5287
|
-
|
|
5364
|
+
let skipNextAnimation = options.skipInitialAnimation === true;
|
|
5365
|
+
const removeSourceOnChange = source.on("change", (v) => {
|
|
5366
|
+
if (skipNextAnimation) {
|
|
5367
|
+
skipNextAnimation = false;
|
|
5368
|
+
value.jump(parseValue(v, unit), false);
|
|
5369
|
+
}
|
|
5370
|
+
else {
|
|
5371
|
+
value.set(parseValue(v, unit));
|
|
5372
|
+
}
|
|
5373
|
+
});
|
|
5288
5374
|
const removeValueOnDestroy = value.on("destroy", removeSourceOnChange);
|
|
5289
5375
|
return () => {
|
|
5290
5376
|
removeSourceOnChange();
|
|
@@ -6564,10 +6650,8 @@
|
|
|
6564
6650
|
node.options.layoutScroll &&
|
|
6565
6651
|
node.scroll &&
|
|
6566
6652
|
node !== node.root) {
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
y: -node.scroll.offset.y,
|
|
6570
|
-
});
|
|
6653
|
+
translateAxis(box.x, -node.scroll.offset.x);
|
|
6654
|
+
translateAxis(box.y, -node.scroll.offset.y);
|
|
6571
6655
|
}
|
|
6572
6656
|
if (delta) {
|
|
6573
6657
|
// Incoporate each ancestor's scale into a cumulative treeScale for this component
|
|
@@ -6594,8 +6678,8 @@
|
|
|
6594
6678
|
}
|
|
6595
6679
|
}
|
|
6596
6680
|
function translateAxis(axis, distance) {
|
|
6597
|
-
axis.min
|
|
6598
|
-
axis.max
|
|
6681
|
+
axis.min += distance;
|
|
6682
|
+
axis.max += distance;
|
|
6599
6683
|
}
|
|
6600
6684
|
/**
|
|
6601
6685
|
* Apply a transform to an axis from the latest resolved motion values.
|
|
@@ -7762,8 +7846,13 @@
|
|
|
7762
7846
|
return transform || "none";
|
|
7763
7847
|
}
|
|
7764
7848
|
|
|
7765
|
-
const
|
|
7766
|
-
|
|
7849
|
+
const borderLabels = [
|
|
7850
|
+
"borderTopLeftRadius",
|
|
7851
|
+
"borderTopRightRadius",
|
|
7852
|
+
"borderBottomLeftRadius",
|
|
7853
|
+
"borderBottomRightRadius",
|
|
7854
|
+
];
|
|
7855
|
+
const numBorders = borderLabels.length;
|
|
7767
7856
|
const asNumber = (value) => typeof value === "string" ? parseFloat(value) : value;
|
|
7768
7857
|
const isPx = (value) => typeof value === "number" || px.test(value);
|
|
7769
7858
|
function mixValues(target, follow, lead, progress, shouldCrossfadeOpacity, isOnlyMember) {
|
|
@@ -7778,7 +7867,7 @@
|
|
|
7778
7867
|
* Mix border radius
|
|
7779
7868
|
*/
|
|
7780
7869
|
for (let i = 0; i < numBorders; i++) {
|
|
7781
|
-
const borderLabel =
|
|
7870
|
+
const borderLabel = borderLabels[i];
|
|
7782
7871
|
let followRadius = getRadius(follow, borderLabel);
|
|
7783
7872
|
let leadRadius = getRadius(lead, borderLabel);
|
|
7784
7873
|
if (followRadius === undefined && leadRadius === undefined)
|
|
@@ -8423,6 +8512,11 @@
|
|
|
8423
8512
|
}
|
|
8424
8513
|
else {
|
|
8425
8514
|
this.isUpdating = false;
|
|
8515
|
+
/**
|
|
8516
|
+
* Ensure animation-blocked nodes (e.g. during drag)
|
|
8517
|
+
* get measured even when memoized (willUpdate skipped).
|
|
8518
|
+
*/
|
|
8519
|
+
this.nodes.forEach(ensureDraggedNodesSnapshotted);
|
|
8426
8520
|
/**
|
|
8427
8521
|
* Write
|
|
8428
8522
|
*/
|
|
@@ -8521,7 +8615,8 @@
|
|
|
8521
8615
|
const prevLayout = this.layout;
|
|
8522
8616
|
this.layout = this.measure(false);
|
|
8523
8617
|
this.layoutVersion++;
|
|
8524
|
-
this.layoutCorrected
|
|
8618
|
+
if (!this.layoutCorrected)
|
|
8619
|
+
this.layoutCorrected = createBox();
|
|
8525
8620
|
this.isLayoutDirty = false;
|
|
8526
8621
|
this.projectionDelta = undefined;
|
|
8527
8622
|
this.notifyListeners("measure", this.layout.layoutBox);
|
|
@@ -8632,8 +8727,8 @@
|
|
|
8632
8727
|
}
|
|
8633
8728
|
return boxWithoutScroll;
|
|
8634
8729
|
}
|
|
8635
|
-
applyTransform(box, transformOnly = false) {
|
|
8636
|
-
const withTransforms = createBox();
|
|
8730
|
+
applyTransform(box, transformOnly = false, output) {
|
|
8731
|
+
const withTransforms = output || createBox();
|
|
8637
8732
|
copyBoxInto(withTransforms, box);
|
|
8638
8733
|
for (let i = 0; i < this.path.length; i++) {
|
|
8639
8734
|
const node = this.path[i];
|
|
@@ -8641,10 +8736,8 @@
|
|
|
8641
8736
|
node.options.layoutScroll &&
|
|
8642
8737
|
node.scroll &&
|
|
8643
8738
|
node !== node.root) {
|
|
8644
|
-
|
|
8645
|
-
|
|
8646
|
-
y: -node.scroll.offset.y,
|
|
8647
|
-
});
|
|
8739
|
+
translateAxis(withTransforms.x, -node.scroll.offset.x);
|
|
8740
|
+
translateAxis(withTransforms.y, -node.scroll.offset.y);
|
|
8648
8741
|
}
|
|
8649
8742
|
if (!hasTransform(node.latestValues))
|
|
8650
8743
|
continue;
|
|
@@ -8787,8 +8880,7 @@
|
|
|
8787
8880
|
}
|
|
8788
8881
|
else if (this.targetDelta) {
|
|
8789
8882
|
if (Boolean(this.resumingFrom)) {
|
|
8790
|
-
|
|
8791
|
-
this.target = this.applyTransform(this.layout.layoutBox);
|
|
8883
|
+
this.applyTransform(this.layout.layoutBox, false, this.target);
|
|
8792
8884
|
}
|
|
8793
8885
|
else {
|
|
8794
8886
|
copyBoxInto(this.target, this.layout.layoutBox);
|
|
@@ -9398,6 +9490,12 @@
|
|
|
9398
9490
|
axisSnapshot.max = axisSnapshot.min + length;
|
|
9399
9491
|
});
|
|
9400
9492
|
}
|
|
9493
|
+
else if (animationType === "x" || animationType === "y") {
|
|
9494
|
+
const snapAxis = animationType === "x" ? "y" : "x";
|
|
9495
|
+
copyAxisInto(isShared
|
|
9496
|
+
? snapshot.measuredBox[snapAxis]
|
|
9497
|
+
: snapshot.layoutBox[snapAxis], layout[snapAxis]);
|
|
9498
|
+
}
|
|
9401
9499
|
else if (shouldAnimatePositionOnly(animationType, snapshot.layoutBox, layout)) {
|
|
9402
9500
|
eachAxis((axis) => {
|
|
9403
9501
|
const axisSnapshot = isShared
|
|
@@ -9513,6 +9611,18 @@
|
|
|
9513
9611
|
function clearIsLayoutDirty(node) {
|
|
9514
9612
|
node.isLayoutDirty = false;
|
|
9515
9613
|
}
|
|
9614
|
+
/**
|
|
9615
|
+
* When a node is animation-blocked (e.g. during drag) and its component
|
|
9616
|
+
* didn't re-render (memoized), willUpdate() is never called so there's
|
|
9617
|
+
* no snapshot. Use the previous layout as a snapshot and mark dirty so
|
|
9618
|
+
* resetTransform/updateLayout/notifyLayoutUpdate process it normally.
|
|
9619
|
+
*/
|
|
9620
|
+
function ensureDraggedNodesSnapshotted(node) {
|
|
9621
|
+
if (node.isAnimationBlocked && node.layout && !node.isLayoutDirty) {
|
|
9622
|
+
node.snapshot = node.layout;
|
|
9623
|
+
node.isLayoutDirty = true;
|
|
9624
|
+
}
|
|
9625
|
+
}
|
|
9516
9626
|
function resetTransformStyle(node) {
|
|
9517
9627
|
const { visualElement } = node.options;
|
|
9518
9628
|
if (visualElement && visualElement.getProps().onBeforeLayoutMeasure) {
|
|
@@ -10951,16 +11061,48 @@
|
|
|
10951
11061
|
[ScrollOffset.Any, "cover"],
|
|
10952
11062
|
[ScrollOffset.All, "contain"],
|
|
10953
11063
|
];
|
|
10954
|
-
|
|
11064
|
+
const stringToProgress = {
|
|
11065
|
+
start: 0,
|
|
11066
|
+
end: 1,
|
|
11067
|
+
};
|
|
11068
|
+
function parseStringOffset(s) {
|
|
11069
|
+
const parts = s.trim().split(/\s+/);
|
|
11070
|
+
if (parts.length !== 2)
|
|
11071
|
+
return undefined;
|
|
11072
|
+
const a = stringToProgress[parts[0]];
|
|
11073
|
+
const b = stringToProgress[parts[1]];
|
|
11074
|
+
if (a === undefined || b === undefined)
|
|
11075
|
+
return undefined;
|
|
11076
|
+
return [a, b];
|
|
11077
|
+
}
|
|
11078
|
+
function normaliseOffset(offset) {
|
|
10955
11079
|
if (offset.length !== 2)
|
|
11080
|
+
return undefined;
|
|
11081
|
+
const result = [];
|
|
11082
|
+
for (const item of offset) {
|
|
11083
|
+
if (Array.isArray(item)) {
|
|
11084
|
+
result.push(item);
|
|
11085
|
+
}
|
|
11086
|
+
else if (typeof item === "string") {
|
|
11087
|
+
const parsed = parseStringOffset(item);
|
|
11088
|
+
if (!parsed)
|
|
11089
|
+
return undefined;
|
|
11090
|
+
result.push(parsed);
|
|
11091
|
+
}
|
|
11092
|
+
else {
|
|
11093
|
+
return undefined;
|
|
11094
|
+
}
|
|
11095
|
+
}
|
|
11096
|
+
return result;
|
|
11097
|
+
}
|
|
11098
|
+
function matchesPreset(offset, preset) {
|
|
11099
|
+
const normalised = normaliseOffset(offset);
|
|
11100
|
+
if (!normalised)
|
|
10956
11101
|
return false;
|
|
10957
11102
|
for (let i = 0; i < 2; i++) {
|
|
10958
|
-
const o =
|
|
11103
|
+
const o = normalised[i];
|
|
10959
11104
|
const p = preset[i];
|
|
10960
|
-
if (
|
|
10961
|
-
o.length !== 2 ||
|
|
10962
|
-
o[0] !== p[0] ||
|
|
10963
|
-
o[1] !== p[1])
|
|
11105
|
+
if (o[0] !== p[0] || o[1] !== p[1])
|
|
10964
11106
|
return false;
|
|
10965
11107
|
}
|
|
10966
11108
|
return true;
|