motion 14.2.0 → 14.3.1
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/motion.dev.js +257 -106
- package/dist/motion.js +1 -1
- package/package.json +3 -3
package/dist/motion.dev.js
CHANGED
|
@@ -556,8 +556,9 @@
|
|
|
556
556
|
const VAR_TOKEN = "var";
|
|
557
557
|
const VAR_FUNCTION_TOKEN = "var(";
|
|
558
558
|
const SPLIT_TOKEN = "${}";
|
|
559
|
-
// this regex consists of the `singleCssVariableRegex|rgbHSLValueRegex|digitRegex
|
|
560
|
-
|
|
559
|
+
// this regex consists of the `singleCssVariableRegex|rgbHSLValueRegex|digitRegex`.
|
|
560
|
+
// The lookahead skips digits in function names, like the 3 in matrix3d(.
|
|
561
|
+
const complexRegex = /var\s*\(\s*--(?:[\w-]+\s*|[\w-]+\s*,(?:\s*[^)(\s]|\s*\((?:[^)(]|\([^)(]*\))*\))+\s*)\)|#[\da-f]{3,8}|(?:rgb|hsl)a?\((?:-?[\d.]+%?[,\s]+){2}-?[\d.]+%?\s*(?:[,/]\s*)?(?:\b\d+(?:\.\d+)?|\.\d+)?%?\)|-?(?:\d+(?:\.\d+)?|\.\d+)(?![a-z]*\()/giu;
|
|
561
562
|
/**
|
|
562
563
|
* Whether analyseComplexValue(value) would find any values, without
|
|
563
564
|
* tokenising the string. CSS variables are resolved before this is
|
|
@@ -2499,6 +2500,7 @@
|
|
|
2499
2500
|
translateY: 5,
|
|
2500
2501
|
scaleX: 0,
|
|
2501
2502
|
scaleY: 3,
|
|
2503
|
+
scaleZ: () => 1,
|
|
2502
2504
|
scale: (v) => (Math.abs(v[0]) + Math.abs(v[3])) / 2,
|
|
2503
2505
|
rotate,
|
|
2504
2506
|
rotateZ: rotate,
|
|
@@ -2513,8 +2515,8 @@
|
|
|
2513
2515
|
return angle;
|
|
2514
2516
|
};
|
|
2515
2517
|
const rotateZ = rotate;
|
|
2516
|
-
const scaleX = (v) => Math.
|
|
2517
|
-
const scaleY = (v) => Math.
|
|
2518
|
+
const scaleX = (v) => Math.hypot(v[0], v[1]);
|
|
2519
|
+
const scaleY = (v) => Math.hypot(v[4], v[5]);
|
|
2518
2520
|
const matrix3dParsers = {
|
|
2519
2521
|
x: 12,
|
|
2520
2522
|
y: 13,
|
|
@@ -2524,6 +2526,7 @@
|
|
|
2524
2526
|
translateZ: 14,
|
|
2525
2527
|
scaleX,
|
|
2526
2528
|
scaleY,
|
|
2529
|
+
scaleZ: (v) => Math.hypot(v[8], v[9], v[10]),
|
|
2527
2530
|
scale: (v) => (scaleX(v) + scaleY(v)) / 2,
|
|
2528
2531
|
rotateX: (v) => rebaseAngle(radToDeg(Math.atan2(v[6], v[5]))),
|
|
2529
2532
|
rotateY: (v) => rebaseAngle(radToDeg(Math.atan2(-v[2], v[0]))),
|
|
@@ -2583,6 +2586,7 @@
|
|
|
2583
2586
|
"scale",
|
|
2584
2587
|
"scaleX",
|
|
2585
2588
|
"scaleY",
|
|
2589
|
+
"scaleZ",
|
|
2586
2590
|
"rotate",
|
|
2587
2591
|
"rotateX",
|
|
2588
2592
|
"rotateY",
|
|
@@ -3542,6 +3546,7 @@
|
|
|
3542
3546
|
constructor(options) {
|
|
3543
3547
|
const element = getOwner(options).current;
|
|
3544
3548
|
const { driver = frameloopDriver } = options;
|
|
3549
|
+
let animation;
|
|
3545
3550
|
/**
|
|
3546
3551
|
* Before JSAnimation replaces the type with its generator.
|
|
3547
3552
|
*/
|
|
@@ -3571,11 +3576,12 @@
|
|
|
3571
3576
|
stop: () => {
|
|
3572
3577
|
js.stop();
|
|
3573
3578
|
const group = getGroup(element);
|
|
3574
|
-
group
|
|
3579
|
+
group && !group.isFinishing && group.release(animation);
|
|
3575
3580
|
},
|
|
3576
3581
|
};
|
|
3577
3582
|
};
|
|
3578
3583
|
super(options);
|
|
3584
|
+
animation = this;
|
|
3579
3585
|
this.canAccelerate = canAccelerate;
|
|
3580
3586
|
/**
|
|
3581
3587
|
* stop is bound in JSAnimation, so wrap it rather than override.
|
|
@@ -3731,8 +3737,14 @@
|
|
|
3731
3737
|
}
|
|
3732
3738
|
return super.time;
|
|
3733
3739
|
}
|
|
3740
|
+
/**
|
|
3741
|
+
* A JS animation sets its motion value from a seek on its next tick.
|
|
3742
|
+
* This doesn't tick, so set it now, rather than leave it to be set
|
|
3743
|
+
* when this next stops.
|
|
3744
|
+
*/
|
|
3734
3745
|
set time(newTime) {
|
|
3735
3746
|
super.time = newTime;
|
|
3747
|
+
this.isAccelerated() && this.sync();
|
|
3736
3748
|
}
|
|
3737
3749
|
/**
|
|
3738
3750
|
* Scroll timelines set time every frame, so run these on the main
|
|
@@ -3760,6 +3772,7 @@
|
|
|
3760
3772
|
let segments = [];
|
|
3761
3773
|
let needsBuild = false;
|
|
3762
3774
|
let isScheduled = false;
|
|
3775
|
+
let isEnded = false;
|
|
3763
3776
|
let hasChurned = false;
|
|
3764
3777
|
let lastChurn = -Infinity;
|
|
3765
3778
|
const has = (key) => {
|
|
@@ -3876,8 +3889,19 @@
|
|
|
3876
3889
|
for (const track of moving) {
|
|
3877
3890
|
const values = at.map((t) => track.numberAt(t));
|
|
3878
3891
|
const [first] = values;
|
|
3879
|
-
const
|
|
3880
|
-
const
|
|
3892
|
+
const changes = values.map((value) => value - first);
|
|
3893
|
+
const end = changes[changes.length - 1];
|
|
3894
|
+
/**
|
|
3895
|
+
* A value that ends only a tiny way from where it started, as
|
|
3896
|
+
* a round trip does if it's built just after it starts, or
|
|
3897
|
+
* from a float residue, ends where it started. Dividing by
|
|
3898
|
+
* that tiny range would stretch the easing to values the
|
|
3899
|
+
* compositor can't render.
|
|
3900
|
+
*/
|
|
3901
|
+
const range = changes.some((change) => Math.abs(change) * easingTolerance > Math.abs(end))
|
|
3902
|
+
? 0
|
|
3903
|
+
: end;
|
|
3904
|
+
const progress = changes.map((change) => range ? change / range : change);
|
|
3881
3905
|
/**
|
|
3882
3906
|
* A value that doesn't change, like one still in its delay,
|
|
3883
3907
|
* fits any easing. One that changes but ends where it started
|
|
@@ -4179,6 +4203,18 @@
|
|
|
4179
4203
|
}
|
|
4180
4204
|
return true;
|
|
4181
4205
|
};
|
|
4206
|
+
/**
|
|
4207
|
+
* If no animations are left, write where they got to and cancel the
|
|
4208
|
+
* WAAPI animations. Returns whether the group ended.
|
|
4209
|
+
*/
|
|
4210
|
+
const end = () => {
|
|
4211
|
+
if (tracks.size)
|
|
4212
|
+
return false;
|
|
4213
|
+
isEnded = true;
|
|
4214
|
+
transformGroups.delete(element);
|
|
4215
|
+
group.isJS || stop();
|
|
4216
|
+
return true;
|
|
4217
|
+
};
|
|
4182
4218
|
const build = () => {
|
|
4183
4219
|
const now = time.now();
|
|
4184
4220
|
/**
|
|
@@ -4206,15 +4242,14 @@
|
|
|
4206
4242
|
group.isFinishing = false;
|
|
4207
4243
|
}
|
|
4208
4244
|
isScheduled = false;
|
|
4245
|
+
if (isEnded)
|
|
4246
|
+
return;
|
|
4209
4247
|
tracks.forEach(({ state }, track) => {
|
|
4210
4248
|
if (state === "idle" || state === "finished")
|
|
4211
4249
|
tracks.delete(track);
|
|
4212
4250
|
});
|
|
4213
|
-
if (
|
|
4214
|
-
transformGroups.delete(element);
|
|
4215
|
-
group.isJS || stop();
|
|
4251
|
+
if (end())
|
|
4216
4252
|
return;
|
|
4217
|
-
}
|
|
4218
4253
|
for (const track of tracks) {
|
|
4219
4254
|
if (!track.canAccelerate)
|
|
4220
4255
|
return group.demote(true);
|
|
@@ -4246,6 +4281,8 @@
|
|
|
4246
4281
|
isJS: false,
|
|
4247
4282
|
isFinishing: false,
|
|
4248
4283
|
schedule(build_, isChurn) {
|
|
4284
|
+
if (isEnded)
|
|
4285
|
+
return;
|
|
4249
4286
|
needsBuild || (needsBuild = build_);
|
|
4250
4287
|
hasChurned || (hasChurned = !!isChurn);
|
|
4251
4288
|
/**
|
|
@@ -4260,6 +4297,21 @@
|
|
|
4260
4297
|
: queueMicrotask(build);
|
|
4261
4298
|
}
|
|
4262
4299
|
},
|
|
4300
|
+
/**
|
|
4301
|
+
* End the group now, rather than when it next builds, so the
|
|
4302
|
+
* element's transform is the caller's again as soon as its last
|
|
4303
|
+
* animation is stopped.
|
|
4304
|
+
*/
|
|
4305
|
+
release(track) {
|
|
4306
|
+
/**
|
|
4307
|
+
* Only drop the stopped animation. Others can be idle because
|
|
4308
|
+
* they're part way through play(), and are about to run.
|
|
4309
|
+
*/
|
|
4310
|
+
const { state } = track;
|
|
4311
|
+
if (state === "idle" || state === "finished")
|
|
4312
|
+
tracks.delete(track);
|
|
4313
|
+
end() || group.schedule(true);
|
|
4314
|
+
},
|
|
4263
4315
|
/**
|
|
4264
4316
|
* An outside write. Rebuild once, but if it keeps happening, move
|
|
4265
4317
|
* to the main thread.
|
|
@@ -4274,7 +4326,7 @@
|
|
|
4274
4326
|
}
|
|
4275
4327
|
},
|
|
4276
4328
|
demote(canReturn) {
|
|
4277
|
-
if (group.isJS)
|
|
4329
|
+
if (group.isJS || isEnded)
|
|
4278
4330
|
return;
|
|
4279
4331
|
/**
|
|
4280
4332
|
* Unless only another animation is holding the group on the
|
|
@@ -5281,11 +5333,11 @@
|
|
|
5281
5333
|
}
|
|
5282
5334
|
return sample({ x: tx, y: ty }, { x: 0, y: 0 });
|
|
5283
5335
|
},
|
|
5284
|
-
|
|
5336
|
+
animateValues(getValue, target, transition, delay, animations) {
|
|
5285
5337
|
if (!("x" in target || "y" in target))
|
|
5286
5338
|
return;
|
|
5287
|
-
const xValue =
|
|
5288
|
-
const yValue =
|
|
5339
|
+
const xValue = getValue("x");
|
|
5340
|
+
const yValue = getValue("y");
|
|
5289
5341
|
const xRaw = target.x;
|
|
5290
5342
|
const yRaw = target.y;
|
|
5291
5343
|
const xFrom = (Array.isArray(xRaw) && xRaw[0] != null
|
|
@@ -5308,7 +5360,7 @@
|
|
|
5308
5360
|
// concurrent rotate animation composes and nothing accumulates
|
|
5309
5361
|
// on interrupt.
|
|
5310
5362
|
const pathRotationValue = interpolate(0).rotate !== undefined
|
|
5311
|
-
?
|
|
5363
|
+
? getValue("pathRotation")
|
|
5312
5364
|
: undefined;
|
|
5313
5365
|
const pathTransition = {
|
|
5314
5366
|
delay,
|
|
@@ -5518,7 +5570,9 @@
|
|
|
5518
5570
|
const path = transition?.path;
|
|
5519
5571
|
if (path) {
|
|
5520
5572
|
// path mutates `target` to claim x/y; loop below skips them.
|
|
5521
|
-
path.
|
|
5573
|
+
path.animateValues
|
|
5574
|
+
? path.animateValues((key) => visualElement.getValue(key, visualElement.latestValues[key] ?? 0), target, transition, delay, animations)
|
|
5575
|
+
: path.animateVisualElement?.(visualElement, target, transition, delay, animations);
|
|
5522
5576
|
}
|
|
5523
5577
|
for (const key in target) {
|
|
5524
5578
|
const value = visualElement.getValue(key, visualElement.latestValues[key] ?? null);
|
|
@@ -5977,8 +6031,16 @@
|
|
|
5977
6031
|
*/
|
|
5978
6032
|
function animateValues(getValue, keyframes, transition = {}, element) {
|
|
5979
6033
|
const animations = [];
|
|
5980
|
-
const { velocity } = transition;
|
|
6034
|
+
const { velocity, path } = transition;
|
|
5981
6035
|
const reduceMotion = transition.reduceMotion ?? element?.shouldReduceMotion;
|
|
6036
|
+
/**
|
|
6037
|
+
* Only elements have x/y to move along a path. path claims them by
|
|
6038
|
+
* deleting them, so hand it a copy.
|
|
6039
|
+
*/
|
|
6040
|
+
if (path?.animateValues && element) {
|
|
6041
|
+
keyframes = { ...keyframes };
|
|
6042
|
+
path.animateValues(getValue, keyframes, transition, 0, animations);
|
|
6043
|
+
}
|
|
5982
6044
|
for (const key in keyframes) {
|
|
5983
6045
|
if (key === "transition" || key === "transitionEnd")
|
|
5984
6046
|
continue;
|
|
@@ -6583,7 +6645,7 @@
|
|
|
6583
6645
|
}
|
|
6584
6646
|
}
|
|
6585
6647
|
const addSVGValue = (element, state, key, value) => {
|
|
6586
|
-
if (key.startsWith("path")) {
|
|
6648
|
+
if (key.startsWith("path") && !transformProps.has(key)) {
|
|
6587
6649
|
return addSVGPathValue(element, state, key, value);
|
|
6588
6650
|
}
|
|
6589
6651
|
else if (key.startsWith("attr")) {
|
|
@@ -6684,6 +6746,7 @@
|
|
|
6684
6746
|
values.rotate ||
|
|
6685
6747
|
values.rotateX ||
|
|
6686
6748
|
values.rotateY ||
|
|
6749
|
+
values.rotateZ ||
|
|
6687
6750
|
values.skewX ||
|
|
6688
6751
|
values.skewY);
|
|
6689
6752
|
}
|
|
@@ -7367,13 +7430,46 @@
|
|
|
7367
7430
|
return from === "last" ? lastIndex : lastIndex / 2;
|
|
7368
7431
|
}
|
|
7369
7432
|
}
|
|
7370
|
-
function stagger(duration = 0.1, { startDelay = 0, from = 0, ease } = {}) {
|
|
7433
|
+
function stagger(duration = 0.1, { startDelay = 0, from = 0, ease, grid } = {}) {
|
|
7434
|
+
/**
|
|
7435
|
+
* Distances from the origin for each element count. The final entry
|
|
7436
|
+
* holds the distance used to normalise easing.
|
|
7437
|
+
*/
|
|
7438
|
+
const cache = {};
|
|
7371
7439
|
return (i, total) => {
|
|
7372
|
-
|
|
7373
|
-
|
|
7374
|
-
|
|
7375
|
-
|
|
7376
|
-
const
|
|
7440
|
+
let distances = cache[total];
|
|
7441
|
+
if (!distances) {
|
|
7442
|
+
const [columns, rows] = grid || [Infinity, 1];
|
|
7443
|
+
const lastX = Math.min(columns, total) - 1;
|
|
7444
|
+
const lastY = Math.max(rows, Math.ceil(total / columns)) - 1;
|
|
7445
|
+
const isIndex = typeof from === "number";
|
|
7446
|
+
const scale = from === "first" ? 0 : from === "last" ? 1 : 0.5;
|
|
7447
|
+
const originX = isIndex ? from % columns : lastX * scale;
|
|
7448
|
+
const originY = isIndex ? Math.floor(from / columns) : lastY * scale;
|
|
7449
|
+
distances = cache[total] = [];
|
|
7450
|
+
for (let j = 0; j < total; j++) {
|
|
7451
|
+
const x = j % columns;
|
|
7452
|
+
const y = Math.floor(j / columns);
|
|
7453
|
+
const edgeX = Math.min(x, lastX - x);
|
|
7454
|
+
const edgeY = Math.min(y, lastY - y);
|
|
7455
|
+
if (from === "random") {
|
|
7456
|
+
// Inside-out shuffle, so each element gets a distinct step
|
|
7457
|
+
const k = (Math.random() * (j + 1)) | 0;
|
|
7458
|
+
distances[j] = distances[k];
|
|
7459
|
+
distances[k] = j;
|
|
7460
|
+
}
|
|
7461
|
+
else {
|
|
7462
|
+
distances[j] =
|
|
7463
|
+
from === "edges"
|
|
7464
|
+
? Math.min(lastX ? edgeX : edgeY, lastY ? edgeY : edgeX)
|
|
7465
|
+
: Math.hypot(x - originX, y - originY);
|
|
7466
|
+
}
|
|
7467
|
+
}
|
|
7468
|
+
distances.push(grid ? Math.max(...distances) : total);
|
|
7469
|
+
}
|
|
7470
|
+
let delay = duration * distances[i];
|
|
7471
|
+
const maxDelay = duration * distances[total];
|
|
7472
|
+
if (ease && maxDelay) {
|
|
7377
7473
|
const easingFunction = easingDefinitionToFunction(ease);
|
|
7378
7474
|
delay = easingFunction(delay / maxDelay) * maxDelay;
|
|
7379
7475
|
}
|
|
@@ -7607,7 +7703,7 @@
|
|
|
7607
7703
|
}, stopAnimation);
|
|
7608
7704
|
if (isMotionValue(source)) {
|
|
7609
7705
|
let skipNextAnimation = options.skipInitialAnimation === true;
|
|
7610
|
-
const
|
|
7706
|
+
const onSourceChange = (v) => {
|
|
7611
7707
|
if (skipNextAnimation) {
|
|
7612
7708
|
skipNextAnimation = false;
|
|
7613
7709
|
value.jump(parseValue(v, unit), false);
|
|
@@ -7615,11 +7711,28 @@
|
|
|
7615
7711
|
else {
|
|
7616
7712
|
value.set(parseValue(v, unit));
|
|
7617
7713
|
}
|
|
7618
|
-
}
|
|
7714
|
+
};
|
|
7715
|
+
const removeSourceOnChange = source.on("change", onSourceChange);
|
|
7716
|
+
/**
|
|
7717
|
+
* Catch up on a change made before we subscribed, for instance
|
|
7718
|
+
* between a React render reading the source and the effect
|
|
7719
|
+
* attaching to it. Subtracting is falsy for equal or non-numeric
|
|
7720
|
+
* (NaN) values.
|
|
7721
|
+
*/
|
|
7722
|
+
const latest = source.get();
|
|
7723
|
+
if (asNumber(latest) - asNumber(value.get()))
|
|
7724
|
+
onSourceChange(latest);
|
|
7619
7725
|
const removeValueOnDestroy = value.on("destroy", removeSourceOnChange);
|
|
7726
|
+
/**
|
|
7727
|
+
* Stop our animation on detach, otherwise a re-attach (new
|
|
7728
|
+
* options, Strict Mode) starts a second animation that fights
|
|
7729
|
+
* this orphaned one. The catch-up above resumes from the
|
|
7730
|
+
* current value and velocity.
|
|
7731
|
+
*/
|
|
7620
7732
|
return () => {
|
|
7621
7733
|
removeSourceOnChange();
|
|
7622
7734
|
removeValueOnDestroy();
|
|
7735
|
+
stopAnimation();
|
|
7623
7736
|
};
|
|
7624
7737
|
}
|
|
7625
7738
|
return stopAnimation;
|
|
@@ -9782,8 +9895,13 @@
|
|
|
9782
9895
|
function scrapeMotionValuesFromProps(props, prevProps, visualElement) {
|
|
9783
9896
|
const newValues = scrapeMotionValuesFromProps$1(props, prevProps, visualElement);
|
|
9784
9897
|
for (const key in props) {
|
|
9785
|
-
|
|
9786
|
-
|
|
9898
|
+
/**
|
|
9899
|
+
* A MotionValue child is rendered as text content by
|
|
9900
|
+
* DOMVisualElement, so it mustn't also be written as an attribute.
|
|
9901
|
+
*/
|
|
9902
|
+
if (key !== "children" &&
|
|
9903
|
+
(isMotionValue(props[key]) ||
|
|
9904
|
+
isMotionValue(prevProps[key]))) {
|
|
9787
9905
|
const targetKey = transformPropOrder.indexOf(key) !== -1
|
|
9788
9906
|
? "attr" + key.charAt(0).toUpperCase() + key.substring(1)
|
|
9789
9907
|
: key;
|
|
@@ -10414,7 +10532,7 @@
|
|
|
10414
10532
|
transform += `scale(${1 / treeScale.x}, ${1 / treeScale.y}) `;
|
|
10415
10533
|
}
|
|
10416
10534
|
if (latestTransform) {
|
|
10417
|
-
const { transformPerspective, rotate, pathRotation, rotateX, rotateY, skewX, skewY, } = latestTransform;
|
|
10535
|
+
const { transformPerspective, rotate, pathRotation, rotateX, rotateY, rotateZ, skewX, skewY, } = latestTransform;
|
|
10418
10536
|
if (transformPerspective)
|
|
10419
10537
|
transform = `perspective(${transformPerspective}px) ${transform}`;
|
|
10420
10538
|
if (rotate)
|
|
@@ -10426,6 +10544,8 @@
|
|
|
10426
10544
|
transform += `rotateX(${rotateX}deg) `;
|
|
10427
10545
|
if (rotateY)
|
|
10428
10546
|
transform += `rotateY(${rotateY}deg) `;
|
|
10547
|
+
if (rotateZ)
|
|
10548
|
+
transform += `rotateZ(${rotateZ}deg) `;
|
|
10429
10549
|
if (skewX)
|
|
10430
10550
|
transform += `skewX(${skewX}deg) `;
|
|
10431
10551
|
if (skewY)
|
|
@@ -10444,6 +10564,15 @@
|
|
|
10444
10564
|
}
|
|
10445
10565
|
|
|
10446
10566
|
const numBorders = cornerRadiusProps.length;
|
|
10567
|
+
const mixedTransforms = [
|
|
10568
|
+
"rotate",
|
|
10569
|
+
"rotateX",
|
|
10570
|
+
"rotateY",
|
|
10571
|
+
"rotateZ",
|
|
10572
|
+
"skewX",
|
|
10573
|
+
"skewY",
|
|
10574
|
+
];
|
|
10575
|
+
const numMixedTransforms = mixedTransforms.length;
|
|
10447
10576
|
const isPx = (value) => typeof value === "number" || px.test(value);
|
|
10448
10577
|
function mixValues(target, follow, lead, progress, shouldCrossfadeOpacity, isOnlyMember) {
|
|
10449
10578
|
if (shouldCrossfadeOpacity) {
|
|
@@ -10478,10 +10607,14 @@
|
|
|
10478
10607
|
}
|
|
10479
10608
|
}
|
|
10480
10609
|
/**
|
|
10481
|
-
* Mix rotation
|
|
10610
|
+
* Mix rotation and skew. These can't be projected, so mix them every
|
|
10611
|
+
* frame rather than leaving them frozen at the animation-start snapshot.
|
|
10482
10612
|
*/
|
|
10483
|
-
|
|
10484
|
-
|
|
10613
|
+
for (let i = 0; i < numMixedTransforms; i++) {
|
|
10614
|
+
const key = mixedTransforms[i];
|
|
10615
|
+
if (follow[key] || lead[key]) {
|
|
10616
|
+
target[key] = mixNumber$1(follow[key] || 0, lead[key] || 0, progress);
|
|
10617
|
+
}
|
|
10485
10618
|
}
|
|
10486
10619
|
}
|
|
10487
10620
|
function getRadius(values, radiusName) {
|
|
@@ -10931,82 +11064,92 @@
|
|
|
10931
11064
|
if (layoutId) {
|
|
10932
11065
|
this.root.registerSharedNode(layoutId, this);
|
|
10933
11066
|
}
|
|
10934
|
-
|
|
10935
|
-
|
|
10936
|
-
|
|
10937
|
-
|
|
10938
|
-
|
|
10939
|
-
|
|
10940
|
-
|
|
10941
|
-
|
|
10942
|
-
|
|
11067
|
+
this.addLayoutListener();
|
|
11068
|
+
}
|
|
11069
|
+
/**
|
|
11070
|
+
* Only register the layout animation handler if this node requires
|
|
11071
|
+
* layout animation. This is called on mount, and again whenever options
|
|
11072
|
+
* change, as `layout` or `layoutId` may be enabled after mount.
|
|
11073
|
+
*/
|
|
11074
|
+
addLayoutListener() {
|
|
11075
|
+
const { layoutId, layout, visualElement, animate } = this.options;
|
|
11076
|
+
if (!this.instance ||
|
|
11077
|
+
this.hasLayoutListener ||
|
|
11078
|
+
animate === false ||
|
|
11079
|
+
!visualElement ||
|
|
11080
|
+
!(layoutId || layout))
|
|
11081
|
+
return;
|
|
11082
|
+
this.hasLayoutListener = true;
|
|
11083
|
+
this.addEventListener("didUpdate", ({ delta, hasLayoutChanged, hasRelativeLayoutChanged, layout: newLayout, }) => {
|
|
11084
|
+
if (this.isAnimationBlocked) {
|
|
11085
|
+
this.target = undefined;
|
|
11086
|
+
this.relativeTarget = undefined;
|
|
11087
|
+
return;
|
|
11088
|
+
}
|
|
11089
|
+
// TODO: Check here if an animation exists
|
|
11090
|
+
const layoutTransition = this.options.transition ||
|
|
11091
|
+
visualElement.getDefaultTransition() ||
|
|
11092
|
+
defaultLayoutTransition;
|
|
11093
|
+
const { onLayoutAnimationStart, onLayoutAnimationComplete, } = visualElement.getProps();
|
|
11094
|
+
/**
|
|
11095
|
+
* The target layout of the element might stay the same,
|
|
11096
|
+
* but its position relative to its parent has changed.
|
|
11097
|
+
*/
|
|
11098
|
+
const hasTargetChanged = !this.targetLayout ||
|
|
11099
|
+
!boxEqualsRounded(this.targetLayout, newLayout);
|
|
11100
|
+
/*
|
|
11101
|
+
* Note: Disabled to fix relative animations always triggering new
|
|
11102
|
+
* layout animations. If this causes further issues, we can try
|
|
11103
|
+
* a different approach to detecting relative target changes.
|
|
11104
|
+
*/
|
|
11105
|
+
// || hasRelativeLayoutChanged
|
|
11106
|
+
/**
|
|
11107
|
+
* If the layout hasn't seemed to have changed, it might be that the
|
|
11108
|
+
* element is visually in the same place in the document but its position
|
|
11109
|
+
* relative to its parent has indeed changed. So here we check for that.
|
|
11110
|
+
*/
|
|
11111
|
+
const hasOnlyRelativeTargetChanged = !hasLayoutChanged && hasRelativeLayoutChanged;
|
|
11112
|
+
if (this.options.layoutRoot ||
|
|
11113
|
+
this.resumeFrom ||
|
|
11114
|
+
hasOnlyRelativeTargetChanged ||
|
|
11115
|
+
(hasLayoutChanged &&
|
|
11116
|
+
(hasTargetChanged || !this.currentAnimation))) {
|
|
11117
|
+
if (this.resumeFrom) {
|
|
11118
|
+
this.resumingFrom = this.resumeFrom;
|
|
11119
|
+
this.resumingFrom.resumingFrom = undefined;
|
|
11120
|
+
}
|
|
11121
|
+
const animationOptions = {
|
|
11122
|
+
...getValueTransition$1(layoutTransition, "layout"),
|
|
11123
|
+
onPlay: onLayoutAnimationStart,
|
|
11124
|
+
onComplete: onLayoutAnimationComplete,
|
|
11125
|
+
};
|
|
11126
|
+
if (visualElement.shouldReduceMotion ||
|
|
11127
|
+
this.options.layoutRoot) {
|
|
11128
|
+
animationOptions.delay = 0;
|
|
11129
|
+
animationOptions.type = false;
|
|
10943
11130
|
}
|
|
10944
|
-
|
|
10945
|
-
const layoutTransition = this.options.transition ||
|
|
10946
|
-
visualElement.getDefaultTransition() ||
|
|
10947
|
-
defaultLayoutTransition;
|
|
10948
|
-
const { onLayoutAnimationStart, onLayoutAnimationComplete, } = visualElement.getProps();
|
|
11131
|
+
this.startAnimation(animationOptions);
|
|
10949
11132
|
/**
|
|
10950
|
-
*
|
|
10951
|
-
*
|
|
10952
|
-
*/
|
|
10953
|
-
const hasTargetChanged = !this.targetLayout ||
|
|
10954
|
-
!boxEqualsRounded(this.targetLayout, newLayout);
|
|
10955
|
-
/*
|
|
10956
|
-
* Note: Disabled to fix relative animations always triggering new
|
|
10957
|
-
* layout animations. If this causes further issues, we can try
|
|
10958
|
-
* a different approach to detecting relative target changes.
|
|
11133
|
+
* Set animation origin after starting animation to avoid layout jump
|
|
11134
|
+
* caused by stopping previous layout animation
|
|
10959
11135
|
*/
|
|
10960
|
-
|
|
11136
|
+
this.setAnimationOrigin(delta, hasOnlyRelativeTargetChanged, animationOptions.path);
|
|
11137
|
+
}
|
|
11138
|
+
else {
|
|
10961
11139
|
/**
|
|
10962
|
-
* If the layout hasn't
|
|
10963
|
-
*
|
|
10964
|
-
*
|
|
11140
|
+
* If the layout hasn't changed and we have an animation that hasn't started yet,
|
|
11141
|
+
* finish it immediately. Otherwise it will be animating from a location
|
|
11142
|
+
* that was probably never committed to screen and look like a jumpy box.
|
|
10965
11143
|
*/
|
|
10966
|
-
|
|
10967
|
-
|
|
10968
|
-
this.resumeFrom ||
|
|
10969
|
-
hasOnlyRelativeTargetChanged ||
|
|
10970
|
-
(hasLayoutChanged &&
|
|
10971
|
-
(hasTargetChanged || !this.currentAnimation))) {
|
|
10972
|
-
if (this.resumeFrom) {
|
|
10973
|
-
this.resumingFrom = this.resumeFrom;
|
|
10974
|
-
this.resumingFrom.resumingFrom = undefined;
|
|
10975
|
-
}
|
|
10976
|
-
const animationOptions = {
|
|
10977
|
-
...getValueTransition$1(layoutTransition, "layout"),
|
|
10978
|
-
onPlay: onLayoutAnimationStart,
|
|
10979
|
-
onComplete: onLayoutAnimationComplete,
|
|
10980
|
-
};
|
|
10981
|
-
if (visualElement.shouldReduceMotion ||
|
|
10982
|
-
this.options.layoutRoot) {
|
|
10983
|
-
animationOptions.delay = 0;
|
|
10984
|
-
animationOptions.type = false;
|
|
10985
|
-
}
|
|
10986
|
-
this.startAnimation(animationOptions);
|
|
10987
|
-
/**
|
|
10988
|
-
* Set animation origin after starting animation to avoid layout jump
|
|
10989
|
-
* caused by stopping previous layout animation
|
|
10990
|
-
*/
|
|
10991
|
-
this.setAnimationOrigin(delta, hasOnlyRelativeTargetChanged, animationOptions
|
|
10992
|
-
.path);
|
|
11144
|
+
if (!hasLayoutChanged) {
|
|
11145
|
+
finishAnimation(this);
|
|
10993
11146
|
}
|
|
10994
|
-
|
|
10995
|
-
|
|
10996
|
-
* If the layout hasn't changed and we have an animation that hasn't started yet,
|
|
10997
|
-
* finish it immediately. Otherwise it will be animating from a location
|
|
10998
|
-
* that was probably never committed to screen and look like a jumpy box.
|
|
10999
|
-
*/
|
|
11000
|
-
if (!hasLayoutChanged) {
|
|
11001
|
-
finishAnimation(this);
|
|
11002
|
-
}
|
|
11003
|
-
if (this.isLead() && this.options.onExitComplete) {
|
|
11004
|
-
this.options.onExitComplete();
|
|
11005
|
-
}
|
|
11147
|
+
if (this.isLead() && this.options.onExitComplete) {
|
|
11148
|
+
this.options.onExitComplete();
|
|
11006
11149
|
}
|
|
11007
|
-
|
|
11008
|
-
|
|
11009
|
-
}
|
|
11150
|
+
}
|
|
11151
|
+
this.targetLayout = newLayout;
|
|
11152
|
+
});
|
|
11010
11153
|
}
|
|
11011
11154
|
unmount() {
|
|
11012
11155
|
this.options.layoutId && this.willUpdate();
|
|
@@ -11016,6 +11159,7 @@
|
|
|
11016
11159
|
this.parent && this.parent.children.delete(this);
|
|
11017
11160
|
this.instance = undefined;
|
|
11018
11161
|
this.eventHandlers.clear();
|
|
11162
|
+
this.hasLayoutListener = false;
|
|
11019
11163
|
cancelFrame(this.updateProjection);
|
|
11020
11164
|
}
|
|
11021
11165
|
// only on the root
|
|
@@ -11186,7 +11330,16 @@
|
|
|
11186
11330
|
}
|
|
11187
11331
|
clearAllSnapshots() {
|
|
11188
11332
|
this.nodes.forEach(clearSnapshot);
|
|
11189
|
-
|
|
11333
|
+
/**
|
|
11334
|
+
* A stack's last member can unmount while a replacement with the
|
|
11335
|
+
* same layoutId mounts in the same commit, so the stack (and its
|
|
11336
|
+
* lead) must outlive the unmount for the replacement to resume
|
|
11337
|
+
* from it. By now that handoff has happened, so stacks that are
|
|
11338
|
+
* still empty only retain unmounted nodes and can be released.
|
|
11339
|
+
*/
|
|
11340
|
+
this.sharedNodes.forEach((stack, layoutId) => stack.members.length
|
|
11341
|
+
? stack.removeLeadSnapshot()
|
|
11342
|
+
: this.sharedNodes.delete(layoutId));
|
|
11190
11343
|
}
|
|
11191
11344
|
scheduleUpdateProjection() {
|
|
11192
11345
|
if (!this.projectionUpdateScheduled) {
|
|
@@ -11410,6 +11563,7 @@
|
|
|
11410
11563
|
...options,
|
|
11411
11564
|
crossfade: options.crossfade !== undefined ? options.crossfade : true,
|
|
11412
11565
|
};
|
|
11566
|
+
this.addLayoutListener();
|
|
11413
11567
|
}
|
|
11414
11568
|
clearMeasurements() {
|
|
11415
11569
|
this.scroll = undefined;
|
|
@@ -12339,9 +12493,6 @@
|
|
|
12339
12493
|
function resetSkewAndRotation(node) {
|
|
12340
12494
|
node.resetSkewAndRotation();
|
|
12341
12495
|
}
|
|
12342
|
-
function removeLeadSnapshots(stack) {
|
|
12343
|
-
stack.removeLeadSnapshot();
|
|
12344
|
-
}
|
|
12345
12496
|
function mixAxisDeltaLinear(output, delta, p) {
|
|
12346
12497
|
output.translate = mixNumber$1(delta.translate, 0, p);
|
|
12347
12498
|
output.scale = mixNumber$1(delta.scale, 1, p);
|