motion 12.36.0 → 12.38.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 +217 -89
- 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
|
@@ -1632,9 +1632,9 @@
|
|
|
1632
1632
|
};
|
|
1633
1633
|
}
|
|
1634
1634
|
|
|
1635
|
-
const isNotNull
|
|
1636
|
-
function getFinalKeyframe
|
|
1637
|
-
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);
|
|
1638
1638
|
const useFirstKeyframe = speed < 0 || (repeat && repeatType !== "loop" && repeat % 2 === 1);
|
|
1639
1639
|
const index = useFirstKeyframe ? 0 : resolvedKeyframes.length - 1;
|
|
1640
1640
|
return !index || finalKeyframe === undefined
|
|
@@ -1699,6 +1699,14 @@
|
|
|
1699
1699
|
* Playback speed as a factor. 0 would be stopped, -1 reverse and 2 double speed.
|
|
1700
1700
|
*/
|
|
1701
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
|
+
};
|
|
1702
1710
|
/**
|
|
1703
1711
|
* This method is bound to the instance to fix a pattern where
|
|
1704
1712
|
* animation.stop is returned as a reference from a useEffect.
|
|
@@ -1860,9 +1868,14 @@
|
|
|
1860
1868
|
* This prevents delay: x, duration: 0 animations from finishing
|
|
1861
1869
|
* instantly.
|
|
1862
1870
|
*/
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
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
|
+
}
|
|
1866
1879
|
if (mixKeyframes && !isInDelayPhase) {
|
|
1867
1880
|
state.value = mixKeyframes(state.value);
|
|
1868
1881
|
}
|
|
@@ -1877,7 +1890,7 @@
|
|
|
1877
1890
|
(this.state === "finished" || (this.state === "running" && done));
|
|
1878
1891
|
// TODO: The exception for inertia could be cleaner here
|
|
1879
1892
|
if (isAnimationFinished && type !== inertia) {
|
|
1880
|
-
state.value = getFinalKeyframe
|
|
1893
|
+
state.value = getFinalKeyframe(keyframes, this.options, finalKeyframe, this.speed);
|
|
1881
1894
|
}
|
|
1882
1895
|
if (onUpdate) {
|
|
1883
1896
|
onUpdate(state.value);
|
|
@@ -2485,7 +2498,7 @@
|
|
|
2485
2498
|
this.animation.onfinish = () => {
|
|
2486
2499
|
this.finishedTime = this.time;
|
|
2487
2500
|
if (!pseudoElement) {
|
|
2488
|
-
const keyframe = getFinalKeyframe
|
|
2501
|
+
const keyframe = getFinalKeyframe(keyframes, this.options, finalKeyframe, this.speed);
|
|
2489
2502
|
if (this.updateMotionValue) {
|
|
2490
2503
|
this.updateMotionValue(keyframe);
|
|
2491
2504
|
}
|
|
@@ -2791,17 +2804,42 @@
|
|
|
2791
2804
|
/**
|
|
2792
2805
|
* A list of values that can be hardware-accelerated.
|
|
2793
2806
|
*/
|
|
2794
|
-
const acceleratedValues
|
|
2807
|
+
const acceleratedValues = new Set([
|
|
2795
2808
|
"opacity",
|
|
2796
2809
|
"clipPath",
|
|
2797
2810
|
"filter",
|
|
2798
2811
|
"transform",
|
|
2799
|
-
// 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.
|
|
2800
2814
|
// "background-color"
|
|
2801
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
|
+
]);
|
|
2802
2840
|
const supportsWaapi = /*@__PURE__*/ memo(() => Object.hasOwnProperty.call(Element.prototype, "animate"));
|
|
2803
2841
|
function supportsBrowserAnimation(options) {
|
|
2804
|
-
const { motionValue, name, repeatDelay, repeatType, damping, type } = options;
|
|
2842
|
+
const { motionValue, name, repeatDelay, repeatType, damping, type, keyframes, } = options;
|
|
2805
2843
|
const subject = motionValue?.owner?.current;
|
|
2806
2844
|
/**
|
|
2807
2845
|
* We use this check instead of isHTMLElement() because we explicitly
|
|
@@ -2815,7 +2853,13 @@
|
|
|
2815
2853
|
const { onUpdate, transformTemplate } = motionValue.owner.getProps();
|
|
2816
2854
|
return (supportsWaapi() &&
|
|
2817
2855
|
name &&
|
|
2818
|
-
|
|
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))) &&
|
|
2819
2863
|
(name !== "transform" || !transformTemplate) &&
|
|
2820
2864
|
/**
|
|
2821
2865
|
* If we're outputting values to onUpdate then we can't use WAAPI as there's
|
|
@@ -2879,7 +2923,7 @@
|
|
|
2879
2923
|
if (!canAnimate(keyframes, name, type, velocity)) {
|
|
2880
2924
|
canAnimateValue = false;
|
|
2881
2925
|
if (MotionGlobalConfig.instantAnimations || !delay) {
|
|
2882
|
-
onUpdate?.(getFinalKeyframe
|
|
2926
|
+
onUpdate?.(getFinalKeyframe(keyframes, options, finalKeyframe));
|
|
2883
2927
|
}
|
|
2884
2928
|
keyframes[0] = keyframes[keyframes.length - 1];
|
|
2885
2929
|
makeAnimationInstant(options);
|
|
@@ -2922,12 +2966,21 @@
|
|
|
2922
2966
|
!isHandoff &&
|
|
2923
2967
|
supportsBrowserAnimation(resolvedOptions);
|
|
2924
2968
|
const element = resolvedOptions.motionValue?.owner?.current;
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
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
|
+
}
|
|
2931
2984
|
animation.finished.then(() => {
|
|
2932
2985
|
this.notifyFinished();
|
|
2933
2986
|
}).catch(noop$1);
|
|
@@ -3105,8 +3158,11 @@
|
|
|
3105
3158
|
const animationMaps = new WeakMap();
|
|
3106
3159
|
const animationMapKey = (name, pseudoElement = "") => `${name}:${pseudoElement}`;
|
|
3107
3160
|
function getAnimationMap(element) {
|
|
3108
|
-
|
|
3109
|
-
|
|
3161
|
+
let map = animationMaps.get(element);
|
|
3162
|
+
if (!map) {
|
|
3163
|
+
map = new Map();
|
|
3164
|
+
animationMaps.set(element, map);
|
|
3165
|
+
}
|
|
3110
3166
|
return map;
|
|
3111
3167
|
}
|
|
3112
3168
|
|
|
@@ -3198,17 +3254,6 @@
|
|
|
3198
3254
|
return ease;
|
|
3199
3255
|
};
|
|
3200
3256
|
|
|
3201
|
-
const isNotNull = (value) => value !== null;
|
|
3202
|
-
function getFinalKeyframe(keyframes, { repeat, repeatType = "loop" }, finalKeyframe) {
|
|
3203
|
-
const resolvedKeyframes = keyframes.filter(isNotNull);
|
|
3204
|
-
const index = repeat && repeatType !== "loop" && repeat % 2 === 1
|
|
3205
|
-
? 0
|
|
3206
|
-
: resolvedKeyframes.length - 1;
|
|
3207
|
-
return !index || finalKeyframe === undefined
|
|
3208
|
-
? resolvedKeyframes[index]
|
|
3209
|
-
: finalKeyframe;
|
|
3210
|
-
}
|
|
3211
|
-
|
|
3212
3257
|
/**
|
|
3213
3258
|
* If `transition` has `inherit: true`, shallow-merge it with
|
|
3214
3259
|
* `parentTransition` (child keys win) and strip the `inherit` key.
|
|
@@ -3232,13 +3277,29 @@
|
|
|
3232
3277
|
return valueTransition;
|
|
3233
3278
|
}
|
|
3234
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
|
+
]);
|
|
3235
3292
|
/**
|
|
3236
3293
|
* Decide whether a transition is defined on a given Transition.
|
|
3237
3294
|
* This filters out orchestration options and returns true
|
|
3238
3295
|
* if any options are left.
|
|
3239
3296
|
*/
|
|
3240
|
-
function isTransitionDefined(
|
|
3241
|
-
|
|
3297
|
+
function isTransitionDefined(transition) {
|
|
3298
|
+
for (const key in transition) {
|
|
3299
|
+
if (!orchestrationKeys.has(key))
|
|
3300
|
+
return true;
|
|
3301
|
+
}
|
|
3302
|
+
return false;
|
|
3242
3303
|
}
|
|
3243
3304
|
|
|
3244
3305
|
const animateMotionValue = (name, value, target, transition = {}, element, isHandoff) => (onComplete) => {
|
|
@@ -3802,13 +3863,16 @@
|
|
|
3802
3863
|
};
|
|
3803
3864
|
/**
|
|
3804
3865
|
* If the value is already at the defined target, skip the animation.
|
|
3866
|
+
* We still re-assert the value via frame.update to take precedence
|
|
3867
|
+
* over any stale transitionEnd callbacks from previous animations.
|
|
3805
3868
|
*/
|
|
3806
3869
|
const currentValue = value.get();
|
|
3807
3870
|
if (currentValue !== undefined &&
|
|
3808
|
-
!value.isAnimating &&
|
|
3871
|
+
!value.isAnimating() &&
|
|
3809
3872
|
!Array.isArray(valueTarget) &&
|
|
3810
3873
|
valueTarget === currentValue &&
|
|
3811
3874
|
!valueTransition.velocity) {
|
|
3875
|
+
frame.update(() => value.set(valueTarget));
|
|
3812
3876
|
continue;
|
|
3813
3877
|
}
|
|
3814
3878
|
/**
|
|
@@ -4370,19 +4434,6 @@
|
|
|
4370
4434
|
return true;
|
|
4371
4435
|
});
|
|
4372
4436
|
|
|
4373
|
-
/**
|
|
4374
|
-
* A list of values that can be hardware-accelerated.
|
|
4375
|
-
*/
|
|
4376
|
-
const acceleratedValues = new Set([
|
|
4377
|
-
"opacity",
|
|
4378
|
-
"clipPath",
|
|
4379
|
-
"filter",
|
|
4380
|
-
"transform",
|
|
4381
|
-
// TODO: Can be accelerated but currently disabled until https://issues.chromium.org/issues/41491098 is resolved
|
|
4382
|
-
// or until we implement support for linear() easing.
|
|
4383
|
-
// "background-color"
|
|
4384
|
-
]);
|
|
4385
|
-
|
|
4386
4437
|
function resolveElements(elementOrSelector, scope, selectorCache) {
|
|
4387
4438
|
if (elementOrSelector == null) {
|
|
4388
4439
|
return [];
|
|
@@ -5268,6 +5319,7 @@
|
|
|
5268
5319
|
activeAnimation.stop();
|
|
5269
5320
|
activeAnimation = null;
|
|
5270
5321
|
}
|
|
5322
|
+
value.animation = undefined;
|
|
5271
5323
|
};
|
|
5272
5324
|
const startAnimation = () => {
|
|
5273
5325
|
const currentValue = asNumber$1(value.get());
|
|
@@ -5299,8 +5351,10 @@
|
|
|
5299
5351
|
// multiple calls within the same frame (e.g. rapid mouse events)
|
|
5300
5352
|
const scheduleAnimation = () => {
|
|
5301
5353
|
startAnimation();
|
|
5354
|
+
value.animation = activeAnimation ?? undefined;
|
|
5302
5355
|
value["events"].animationStart?.notify();
|
|
5303
5356
|
activeAnimation?.then(() => {
|
|
5357
|
+
value.animation = undefined;
|
|
5304
5358
|
value["events"].animationComplete?.notify();
|
|
5305
5359
|
});
|
|
5306
5360
|
};
|
|
@@ -6599,10 +6653,8 @@
|
|
|
6599
6653
|
node.options.layoutScroll &&
|
|
6600
6654
|
node.scroll &&
|
|
6601
6655
|
node !== node.root) {
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
y: -node.scroll.offset.y,
|
|
6605
|
-
});
|
|
6656
|
+
translateAxis(box.x, -node.scroll.offset.x);
|
|
6657
|
+
translateAxis(box.y, -node.scroll.offset.y);
|
|
6606
6658
|
}
|
|
6607
6659
|
if (delta) {
|
|
6608
6660
|
// Incoporate each ancestor's scale into a cumulative treeScale for this component
|
|
@@ -6629,8 +6681,8 @@
|
|
|
6629
6681
|
}
|
|
6630
6682
|
}
|
|
6631
6683
|
function translateAxis(axis, distance) {
|
|
6632
|
-
axis.min
|
|
6633
|
-
axis.max
|
|
6684
|
+
axis.min += distance;
|
|
6685
|
+
axis.max += distance;
|
|
6634
6686
|
}
|
|
6635
6687
|
/**
|
|
6636
6688
|
* Apply a transform to an axis from the latest resolved motion values.
|
|
@@ -7651,21 +7703,27 @@
|
|
|
7651
7703
|
calcAxisDelta(delta.x, source.x, target.x, origin ? origin.originX : undefined);
|
|
7652
7704
|
calcAxisDelta(delta.y, source.y, target.y, origin ? origin.originY : undefined);
|
|
7653
7705
|
}
|
|
7654
|
-
function calcRelativeAxis(target, relative, parent) {
|
|
7655
|
-
|
|
7706
|
+
function calcRelativeAxis(target, relative, parent, anchor = 0) {
|
|
7707
|
+
const anchorPoint = anchor
|
|
7708
|
+
? mixNumber$1(parent.min, parent.max, anchor)
|
|
7709
|
+
: parent.min;
|
|
7710
|
+
target.min = anchorPoint + relative.min;
|
|
7656
7711
|
target.max = target.min + calcLength(relative);
|
|
7657
7712
|
}
|
|
7658
|
-
function calcRelativeBox(target, relative, parent) {
|
|
7659
|
-
calcRelativeAxis(target.x, relative.x, parent.x);
|
|
7660
|
-
calcRelativeAxis(target.y, relative.y, parent.y);
|
|
7713
|
+
function calcRelativeBox(target, relative, parent, anchor) {
|
|
7714
|
+
calcRelativeAxis(target.x, relative.x, parent.x, anchor?.x);
|
|
7715
|
+
calcRelativeAxis(target.y, relative.y, parent.y, anchor?.y);
|
|
7661
7716
|
}
|
|
7662
|
-
function calcRelativeAxisPosition(target, layout, parent) {
|
|
7663
|
-
|
|
7717
|
+
function calcRelativeAxisPosition(target, layout, parent, anchor = 0) {
|
|
7718
|
+
const anchorPoint = anchor
|
|
7719
|
+
? mixNumber$1(parent.min, parent.max, anchor)
|
|
7720
|
+
: parent.min;
|
|
7721
|
+
target.min = layout.min - anchorPoint;
|
|
7664
7722
|
target.max = target.min + calcLength(layout);
|
|
7665
7723
|
}
|
|
7666
|
-
function calcRelativePosition(target, layout, parent) {
|
|
7667
|
-
calcRelativeAxisPosition(target.x, layout.x, parent.x);
|
|
7668
|
-
calcRelativeAxisPosition(target.y, layout.y, parent.y);
|
|
7724
|
+
function calcRelativePosition(target, layout, parent, anchor) {
|
|
7725
|
+
calcRelativeAxisPosition(target.x, layout.x, parent.x, anchor?.x);
|
|
7726
|
+
calcRelativeAxisPosition(target.y, layout.y, parent.y, anchor?.y);
|
|
7669
7727
|
}
|
|
7670
7728
|
|
|
7671
7729
|
/**
|
|
@@ -7797,8 +7855,13 @@
|
|
|
7797
7855
|
return transform || "none";
|
|
7798
7856
|
}
|
|
7799
7857
|
|
|
7800
|
-
const
|
|
7801
|
-
|
|
7858
|
+
const borderLabels = [
|
|
7859
|
+
"borderTopLeftRadius",
|
|
7860
|
+
"borderTopRightRadius",
|
|
7861
|
+
"borderBottomLeftRadius",
|
|
7862
|
+
"borderBottomRightRadius",
|
|
7863
|
+
];
|
|
7864
|
+
const numBorders = borderLabels.length;
|
|
7802
7865
|
const asNumber = (value) => typeof value === "string" ? parseFloat(value) : value;
|
|
7803
7866
|
const isPx = (value) => typeof value === "number" || px.test(value);
|
|
7804
7867
|
function mixValues(target, follow, lead, progress, shouldCrossfadeOpacity, isOnlyMember) {
|
|
@@ -7813,7 +7876,7 @@
|
|
|
7813
7876
|
* Mix border radius
|
|
7814
7877
|
*/
|
|
7815
7878
|
for (let i = 0; i < numBorders; i++) {
|
|
7816
|
-
const borderLabel =
|
|
7879
|
+
const borderLabel = borderLabels[i];
|
|
7817
7880
|
let followRadius = getRadius(follow, borderLabel);
|
|
7818
7881
|
let leadRadius = getRadius(lead, borderLabel);
|
|
7819
7882
|
if (followRadius === undefined && leadRadius === undefined)
|
|
@@ -8440,8 +8503,18 @@
|
|
|
8440
8503
|
// but should still clean up the measurements so that the next
|
|
8441
8504
|
// snapshot could be taken correctly.
|
|
8442
8505
|
if (updateWasBlocked) {
|
|
8506
|
+
const wasBlockedByResize = this.updateBlockedByResize;
|
|
8443
8507
|
this.unblockUpdate();
|
|
8508
|
+
this.updateBlockedByResize = false;
|
|
8444
8509
|
this.clearAllSnapshots();
|
|
8510
|
+
/**
|
|
8511
|
+
* When blocked by resize, still measure layouts so
|
|
8512
|
+
* callbacks like onLayoutMeasure fire (e.g. Reorder).
|
|
8513
|
+
* Skip notifyLayoutUpdate to prevent animations.
|
|
8514
|
+
*/
|
|
8515
|
+
if (wasBlockedByResize) {
|
|
8516
|
+
this.nodes.forEach(forceLayoutMeasure);
|
|
8517
|
+
}
|
|
8445
8518
|
this.nodes.forEach(clearMeasurements);
|
|
8446
8519
|
return;
|
|
8447
8520
|
}
|
|
@@ -8458,6 +8531,11 @@
|
|
|
8458
8531
|
}
|
|
8459
8532
|
else {
|
|
8460
8533
|
this.isUpdating = false;
|
|
8534
|
+
/**
|
|
8535
|
+
* Ensure animation-blocked nodes (e.g. during drag)
|
|
8536
|
+
* get measured even when memoized (willUpdate skipped).
|
|
8537
|
+
*/
|
|
8538
|
+
this.nodes.forEach(ensureDraggedNodesSnapshotted);
|
|
8461
8539
|
/**
|
|
8462
8540
|
* Write
|
|
8463
8541
|
*/
|
|
@@ -8556,7 +8634,8 @@
|
|
|
8556
8634
|
const prevLayout = this.layout;
|
|
8557
8635
|
this.layout = this.measure(false);
|
|
8558
8636
|
this.layoutVersion++;
|
|
8559
|
-
this.layoutCorrected
|
|
8637
|
+
if (!this.layoutCorrected)
|
|
8638
|
+
this.layoutCorrected = createBox();
|
|
8560
8639
|
this.isLayoutDirty = false;
|
|
8561
8640
|
this.projectionDelta = undefined;
|
|
8562
8641
|
this.notifyListeners("measure", this.layout.layoutBox);
|
|
@@ -8667,8 +8746,8 @@
|
|
|
8667
8746
|
}
|
|
8668
8747
|
return boxWithoutScroll;
|
|
8669
8748
|
}
|
|
8670
|
-
applyTransform(box, transformOnly = false) {
|
|
8671
|
-
const withTransforms = createBox();
|
|
8749
|
+
applyTransform(box, transformOnly = false, output) {
|
|
8750
|
+
const withTransforms = output || createBox();
|
|
8672
8751
|
copyBoxInto(withTransforms, box);
|
|
8673
8752
|
for (let i = 0; i < this.path.length; i++) {
|
|
8674
8753
|
const node = this.path[i];
|
|
@@ -8676,10 +8755,8 @@
|
|
|
8676
8755
|
node.options.layoutScroll &&
|
|
8677
8756
|
node.scroll &&
|
|
8678
8757
|
node !== node.root) {
|
|
8679
|
-
|
|
8680
|
-
|
|
8681
|
-
y: -node.scroll.offset.y,
|
|
8682
|
-
});
|
|
8758
|
+
translateAxis(withTransforms.x, -node.scroll.offset.x);
|
|
8759
|
+
translateAxis(withTransforms.y, -node.scroll.offset.y);
|
|
8683
8760
|
}
|
|
8684
8761
|
if (!hasTransform(node.latestValues))
|
|
8685
8762
|
continue;
|
|
@@ -8787,7 +8864,9 @@
|
|
|
8787
8864
|
* even if no animation has started.
|
|
8788
8865
|
*/
|
|
8789
8866
|
if (!this.targetDelta && !this.relativeTarget) {
|
|
8790
|
-
if (
|
|
8867
|
+
if (this.options.layoutAnchor !== false &&
|
|
8868
|
+
relativeParent &&
|
|
8869
|
+
relativeParent.layout) {
|
|
8791
8870
|
this.createRelativeTarget(relativeParent, this.layout.layoutBox, relativeParent.layout.layoutBox);
|
|
8792
8871
|
}
|
|
8793
8872
|
else {
|
|
@@ -8815,15 +8894,14 @@
|
|
|
8815
8894
|
this.relativeParent &&
|
|
8816
8895
|
this.relativeParent.target) {
|
|
8817
8896
|
this.forceRelativeParentToResolveTarget();
|
|
8818
|
-
calcRelativeBox(this.target, this.relativeTarget, this.relativeParent.target);
|
|
8897
|
+
calcRelativeBox(this.target, this.relativeTarget, this.relativeParent.target, this.options.layoutAnchor || undefined);
|
|
8819
8898
|
/**
|
|
8820
8899
|
* If we've only got a targetDelta, resolve it into a target
|
|
8821
8900
|
*/
|
|
8822
8901
|
}
|
|
8823
8902
|
else if (this.targetDelta) {
|
|
8824
8903
|
if (Boolean(this.resumingFrom)) {
|
|
8825
|
-
|
|
8826
|
-
this.target = this.applyTransform(this.layout.layoutBox);
|
|
8904
|
+
this.applyTransform(this.layout.layoutBox, false, this.target);
|
|
8827
8905
|
}
|
|
8828
8906
|
else {
|
|
8829
8907
|
copyBoxInto(this.target, this.layout.layoutBox);
|
|
@@ -8841,7 +8919,8 @@
|
|
|
8841
8919
|
*/
|
|
8842
8920
|
if (this.attemptToResolveRelativeTarget) {
|
|
8843
8921
|
this.attemptToResolveRelativeTarget = false;
|
|
8844
|
-
if (
|
|
8922
|
+
if (this.options.layoutAnchor !== false &&
|
|
8923
|
+
relativeParent &&
|
|
8845
8924
|
Boolean(relativeParent.resumingFrom) ===
|
|
8846
8925
|
Boolean(this.resumingFrom) &&
|
|
8847
8926
|
!relativeParent.options.layoutScroll &&
|
|
@@ -8885,7 +8964,7 @@
|
|
|
8885
8964
|
this.forceRelativeParentToResolveTarget();
|
|
8886
8965
|
this.relativeTarget = createBox();
|
|
8887
8966
|
this.relativeTargetOrigin = createBox();
|
|
8888
|
-
calcRelativePosition(this.relativeTargetOrigin, layout, parentLayout);
|
|
8967
|
+
calcRelativePosition(this.relativeTargetOrigin, layout, parentLayout, this.options.layoutAnchor || undefined);
|
|
8889
8968
|
copyBoxInto(this.relativeTarget, this.relativeTargetOrigin);
|
|
8890
8969
|
}
|
|
8891
8970
|
removeRelativeTarget() {
|
|
@@ -9057,7 +9136,7 @@
|
|
|
9057
9136
|
this.layout &&
|
|
9058
9137
|
this.relativeParent &&
|
|
9059
9138
|
this.relativeParent.layout) {
|
|
9060
|
-
calcRelativePosition(relativeLayout, this.layout.layoutBox, this.relativeParent.layout.layoutBox);
|
|
9139
|
+
calcRelativePosition(relativeLayout, this.layout.layoutBox, this.relativeParent.layout.layoutBox, this.options.layoutAnchor || undefined);
|
|
9061
9140
|
mixBox(this.relativeTarget, this.relativeTargetOrigin, relativeLayout, progress);
|
|
9062
9141
|
/**
|
|
9063
9142
|
* If this is an unchanged relative target we can consider the
|
|
@@ -9476,10 +9555,11 @@
|
|
|
9476
9555
|
if (relativeParent && !relativeParent.resumeFrom) {
|
|
9477
9556
|
const { snapshot: parentSnapshot, layout: parentLayout } = relativeParent;
|
|
9478
9557
|
if (parentSnapshot && parentLayout) {
|
|
9558
|
+
const anchor = node.options.layoutAnchor || undefined;
|
|
9479
9559
|
const relativeSnapshot = createBox();
|
|
9480
|
-
calcRelativePosition(relativeSnapshot, snapshot.layoutBox, parentSnapshot.layoutBox);
|
|
9560
|
+
calcRelativePosition(relativeSnapshot, snapshot.layoutBox, parentSnapshot.layoutBox, anchor);
|
|
9481
9561
|
const relativeLayout = createBox();
|
|
9482
|
-
calcRelativePosition(relativeLayout, layout, parentLayout.layoutBox);
|
|
9562
|
+
calcRelativePosition(relativeLayout, layout, parentLayout.layoutBox, anchor);
|
|
9483
9563
|
if (!boxEqualsRounded(relativeSnapshot, relativeLayout)) {
|
|
9484
9564
|
hasRelativeLayoutChanged = true;
|
|
9485
9565
|
}
|
|
@@ -9551,9 +9631,25 @@
|
|
|
9551
9631
|
function clearMeasurements(node) {
|
|
9552
9632
|
node.clearMeasurements();
|
|
9553
9633
|
}
|
|
9634
|
+
function forceLayoutMeasure(node) {
|
|
9635
|
+
node.isLayoutDirty = true;
|
|
9636
|
+
node.updateLayout();
|
|
9637
|
+
}
|
|
9554
9638
|
function clearIsLayoutDirty(node) {
|
|
9555
9639
|
node.isLayoutDirty = false;
|
|
9556
9640
|
}
|
|
9641
|
+
/**
|
|
9642
|
+
* When a node is animation-blocked (e.g. during drag) and its component
|
|
9643
|
+
* didn't re-render (memoized), willUpdate() is never called so there's
|
|
9644
|
+
* no snapshot. Use the previous layout as a snapshot and mark dirty so
|
|
9645
|
+
* resetTransform/updateLayout/notifyLayoutUpdate process it normally.
|
|
9646
|
+
*/
|
|
9647
|
+
function ensureDraggedNodesSnapshotted(node) {
|
|
9648
|
+
if (node.isAnimationBlocked && node.layout && !node.isLayoutDirty) {
|
|
9649
|
+
node.snapshot = node.layout;
|
|
9650
|
+
node.isLayoutDirty = true;
|
|
9651
|
+
}
|
|
9652
|
+
}
|
|
9557
9653
|
function resetTransformStyle(node) {
|
|
9558
9654
|
const { visualElement } = node.options;
|
|
9559
9655
|
if (visualElement && visualElement.getProps().onBeforeLayoutMeasure) {
|
|
@@ -10992,16 +11088,48 @@
|
|
|
10992
11088
|
[ScrollOffset.Any, "cover"],
|
|
10993
11089
|
[ScrollOffset.All, "contain"],
|
|
10994
11090
|
];
|
|
10995
|
-
|
|
11091
|
+
const stringToProgress = {
|
|
11092
|
+
start: 0,
|
|
11093
|
+
end: 1,
|
|
11094
|
+
};
|
|
11095
|
+
function parseStringOffset(s) {
|
|
11096
|
+
const parts = s.trim().split(/\s+/);
|
|
11097
|
+
if (parts.length !== 2)
|
|
11098
|
+
return undefined;
|
|
11099
|
+
const a = stringToProgress[parts[0]];
|
|
11100
|
+
const b = stringToProgress[parts[1]];
|
|
11101
|
+
if (a === undefined || b === undefined)
|
|
11102
|
+
return undefined;
|
|
11103
|
+
return [a, b];
|
|
11104
|
+
}
|
|
11105
|
+
function normaliseOffset(offset) {
|
|
10996
11106
|
if (offset.length !== 2)
|
|
11107
|
+
return undefined;
|
|
11108
|
+
const result = [];
|
|
11109
|
+
for (const item of offset) {
|
|
11110
|
+
if (Array.isArray(item)) {
|
|
11111
|
+
result.push(item);
|
|
11112
|
+
}
|
|
11113
|
+
else if (typeof item === "string") {
|
|
11114
|
+
const parsed = parseStringOffset(item);
|
|
11115
|
+
if (!parsed)
|
|
11116
|
+
return undefined;
|
|
11117
|
+
result.push(parsed);
|
|
11118
|
+
}
|
|
11119
|
+
else {
|
|
11120
|
+
return undefined;
|
|
11121
|
+
}
|
|
11122
|
+
}
|
|
11123
|
+
return result;
|
|
11124
|
+
}
|
|
11125
|
+
function matchesPreset(offset, preset) {
|
|
11126
|
+
const normalised = normaliseOffset(offset);
|
|
11127
|
+
if (!normalised)
|
|
10997
11128
|
return false;
|
|
10998
11129
|
for (let i = 0; i < 2; i++) {
|
|
10999
|
-
const o =
|
|
11130
|
+
const o = normalised[i];
|
|
11000
11131
|
const p = preset[i];
|
|
11001
|
-
if (
|
|
11002
|
-
o.length !== 2 ||
|
|
11003
|
-
o[0] !== p[0] ||
|
|
11004
|
-
o[1] !== p[1])
|
|
11132
|
+
if (o[0] !== p[0] || o[1] !== p[1])
|
|
11005
11133
|
return false;
|
|
11006
11134
|
}
|
|
11007
11135
|
return true;
|