motion 14.6.0 → 14.7.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/motion.dev.js +1322 -779
- package/dist/motion.js +1 -1
- package/package.json +4 -3
package/dist/motion.dev.js
CHANGED
|
@@ -299,13 +299,17 @@
|
|
|
299
299
|
const easingDefinitionToFunction = (definition) => {
|
|
300
300
|
if (isBezierDefinition(definition)) {
|
|
301
301
|
// If cubic bezier definition, create bezier curve
|
|
302
|
-
|
|
302
|
+
typeof process !== "undefined" &&
|
|
303
|
+
"development" !== "production" &&
|
|
304
|
+
exports.invariant(definition.length === 4, `Cubic bezier arrays must contain four numerical values.`, "cubic-bezier-length");
|
|
303
305
|
const [x1, y1, x2, y2] = definition;
|
|
304
306
|
return cubicBezier(x1, y1, x2, y2);
|
|
305
307
|
}
|
|
306
308
|
else if (isValidEasing(definition)) {
|
|
307
309
|
// Else lookup from table
|
|
308
|
-
|
|
310
|
+
typeof process !== "undefined" &&
|
|
311
|
+
"development" !== "production" &&
|
|
312
|
+
exports.invariant(easingLookup[definition] !== undefined, `Invalid easing type '${definition}'`, "invalid-easing-type");
|
|
309
313
|
return easingLookup[definition];
|
|
310
314
|
}
|
|
311
315
|
return definition;
|
|
@@ -442,34 +446,21 @@
|
|
|
442
446
|
")",
|
|
443
447
|
};
|
|
444
448
|
|
|
449
|
+
/**
|
|
450
|
+
* #FF0000 has two characters per channel, #F00 one, which is doubled.
|
|
451
|
+
*/
|
|
452
|
+
const channel = (v, i, size) => {
|
|
453
|
+
const c = v.substring(1 + i * size, 1 + (i + 1) * size);
|
|
454
|
+
return size > 1 ? c : c + c;
|
|
455
|
+
};
|
|
445
456
|
function parseHex(v) {
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
let b = "";
|
|
449
|
-
let a = "";
|
|
450
|
-
// If we have 6 characters, ie #FF0000
|
|
451
|
-
if (v.length > 5) {
|
|
452
|
-
r = v.substring(1, 3);
|
|
453
|
-
g = v.substring(3, 5);
|
|
454
|
-
b = v.substring(5, 7);
|
|
455
|
-
a = v.substring(7, 9);
|
|
456
|
-
// Or we have 3 characters, ie #F00
|
|
457
|
-
}
|
|
458
|
-
else {
|
|
459
|
-
r = v.substring(1, 2);
|
|
460
|
-
g = v.substring(2, 3);
|
|
461
|
-
b = v.substring(3, 4);
|
|
462
|
-
a = v.substring(4, 5);
|
|
463
|
-
r += r;
|
|
464
|
-
g += g;
|
|
465
|
-
b += b;
|
|
466
|
-
a += a;
|
|
467
|
-
}
|
|
457
|
+
const size = v.length > 5 ? 2 : 1;
|
|
458
|
+
const alpha = channel(v, 3, size);
|
|
468
459
|
return {
|
|
469
|
-
red: parseInt(
|
|
470
|
-
green: parseInt(
|
|
471
|
-
blue: parseInt(
|
|
472
|
-
alpha:
|
|
460
|
+
red: parseInt(channel(v, 0, size), 16),
|
|
461
|
+
green: parseInt(channel(v, 1, size), 16),
|
|
462
|
+
blue: parseInt(channel(v, 2, size), 16),
|
|
463
|
+
alpha: alpha ? parseInt(alpha, 16) / 255 : 1,
|
|
473
464
|
};
|
|
474
465
|
}
|
|
475
466
|
const hex = {
|
|
@@ -846,7 +837,6 @@
|
|
|
846
837
|
return orderedOrigin;
|
|
847
838
|
}
|
|
848
839
|
const mixComplex = (origin, target) => {
|
|
849
|
-
const template = complex.createTransformer(target);
|
|
850
840
|
const originStats = analyseComplexValue(origin);
|
|
851
841
|
const targetStats = analyseComplexValue(target);
|
|
852
842
|
const canInterpolate = originStats.indexes.var.length === targetStats.indexes.var.length &&
|
|
@@ -859,7 +849,7 @@
|
|
|
859
849
|
!originStats.values.length)) {
|
|
860
850
|
return mixVisibility(origin, target);
|
|
861
851
|
}
|
|
862
|
-
return pipe(mixArray(matchOrder(originStats, targetStats), targetStats.values),
|
|
852
|
+
return pipe(mixArray(matchOrder(originStats, targetStats), targetStats.values), buildTransformer(targetStats));
|
|
863
853
|
}
|
|
864
854
|
else {
|
|
865
855
|
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.`, "complex-values-different");
|
|
@@ -1151,37 +1141,31 @@
|
|
|
1151
1141
|
};
|
|
1152
1142
|
}
|
|
1153
1143
|
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
},
|
|
1172
|
-
// Limits
|
|
1173
|
-
minDuration: 0.01, // in seconds
|
|
1174
|
-
maxDuration: 10.0, // in seconds
|
|
1175
|
-
minDamping: 0.05,
|
|
1176
|
-
};
|
|
1144
|
+
// Default spring physics
|
|
1145
|
+
const defaultStiffness = 100;
|
|
1146
|
+
const defaultDamping = 10;
|
|
1147
|
+
const defaultMass = 1.0;
|
|
1148
|
+
// Default duration/bounce-based options
|
|
1149
|
+
const defaultDuration = 800; // in ms
|
|
1150
|
+
const defaultBounce = 0.3;
|
|
1151
|
+
const defaultVisualDuration = 0.3; // in seconds
|
|
1152
|
+
// Rest thresholds
|
|
1153
|
+
const restSpeedGranular = 0.01;
|
|
1154
|
+
const restSpeedDefault = 2;
|
|
1155
|
+
const restDeltaGranular = 0.005;
|
|
1156
|
+
const restDeltaDefault = 0.5;
|
|
1157
|
+
// Limits
|
|
1158
|
+
const minDuration = 0.01; // in seconds
|
|
1159
|
+
const maxDuration = 10.0; // in seconds
|
|
1160
|
+
const minDamping = 0.05;
|
|
1177
1161
|
/**
|
|
1178
1162
|
* Maps bounce to a damping ratio as SwiftUI does: 0 is critically damped, and
|
|
1179
1163
|
* it approaches undamped towards 1 and infinitely overdamped towards -1. Both
|
|
1180
1164
|
* ends are limited so the spring still moves and settles.
|
|
1181
1165
|
*/
|
|
1182
1166
|
const bounceToDampingRatio = (bounce) => bounce < 0
|
|
1183
|
-
? 1 / Math.max(1 + bounce,
|
|
1184
|
-
: Math.max(1 - bounce,
|
|
1167
|
+
? 1 / Math.max(1 + bounce, minDamping)
|
|
1168
|
+
: Math.max(1 - bounce, minDamping);
|
|
1185
1169
|
/**
|
|
1186
1170
|
* Scales an overdamped spring's undamped frequency so that, at its defining
|
|
1187
1171
|
* time, it has as far left to go as the critically damped spring that's
|
|
@@ -1224,12 +1208,12 @@
|
|
|
1224
1208
|
* ignore inherited velocity and only resolve without valid physics. Returns
|
|
1225
1209
|
* NaN physics when the root search doesn't converge.
|
|
1226
1210
|
*/
|
|
1227
|
-
function findSpring({ duration =
|
|
1211
|
+
function findSpring({ duration = defaultDuration, bounce = defaultBounce, }) {
|
|
1228
1212
|
let envelope;
|
|
1229
1213
|
let derivative;
|
|
1230
|
-
exports.warning(duration <= secondsToMilliseconds(
|
|
1214
|
+
exports.warning(duration <= secondsToMilliseconds(maxDuration), "Spring duration must be 10 seconds or less", "spring-duration-limit");
|
|
1231
1215
|
const dampingRatio = bounceToDampingRatio(bounce);
|
|
1232
|
-
duration = clamp(
|
|
1216
|
+
duration = clamp(minDuration, maxDuration, millisecondsToSeconds(duration));
|
|
1233
1217
|
if (dampingRatio < 1) {
|
|
1234
1218
|
/**
|
|
1235
1219
|
* Underdamped spring
|
|
@@ -1314,9 +1298,9 @@
|
|
|
1314
1298
|
const validMass = resolvePhysics(options.mass);
|
|
1315
1299
|
const springOptions = {
|
|
1316
1300
|
...options,
|
|
1317
|
-
stiffness: validStiffness ??
|
|
1318
|
-
damping: validDamping ??
|
|
1319
|
-
mass: validMass ??
|
|
1301
|
+
stiffness: validStiffness ?? defaultStiffness,
|
|
1302
|
+
damping: validDamping ?? defaultDamping,
|
|
1303
|
+
mass: validMass ?? defaultMass,
|
|
1320
1304
|
isResolvedFromDuration: false,
|
|
1321
1305
|
// stiffness/damping/mass overrides duration/bounce
|
|
1322
1306
|
isTimeDefined: (validStiffness ?? validDamping ?? validMass) === undefined &&
|
|
@@ -1344,13 +1328,13 @@
|
|
|
1344
1328
|
*/
|
|
1345
1329
|
if (!isValidPhysics(springOptions.stiffness) ||
|
|
1346
1330
|
!isValidPhysics(springOptions.damping, true)) {
|
|
1347
|
-
springOptions.stiffness =
|
|
1348
|
-
springOptions.damping =
|
|
1331
|
+
springOptions.stiffness = defaultStiffness;
|
|
1332
|
+
springOptions.damping = defaultDamping;
|
|
1349
1333
|
}
|
|
1350
1334
|
}
|
|
1351
1335
|
return springOptions;
|
|
1352
1336
|
}
|
|
1353
|
-
function springGenerator(optionsOrVisualDuration =
|
|
1337
|
+
function springGenerator(optionsOrVisualDuration = defaultVisualDuration, bounce = defaultBounce) {
|
|
1354
1338
|
const options = typeof optionsOrVisualDuration !== "object"
|
|
1355
1339
|
? {
|
|
1356
1340
|
visualDuration: optionsOrVisualDuration,
|
|
@@ -1400,14 +1384,10 @@
|
|
|
1400
1384
|
const isGranularScale = Math.abs(s.delta) < 5;
|
|
1401
1385
|
s.restSpeed =
|
|
1402
1386
|
options.restSpeed ||
|
|
1403
|
-
(isGranularScale
|
|
1404
|
-
? springDefaults.restSpeed.granular
|
|
1405
|
-
: springDefaults.restSpeed.default);
|
|
1387
|
+
(isGranularScale ? restSpeedGranular : restSpeedDefault);
|
|
1406
1388
|
s.restDelta =
|
|
1407
1389
|
options.restDelta ||
|
|
1408
|
-
(isGranularScale
|
|
1409
|
-
? springDefaults.restDelta.granular
|
|
1410
|
-
: springDefaults.restDelta.default);
|
|
1390
|
+
(isGranularScale ? restDeltaGranular : restDeltaDefault);
|
|
1411
1391
|
};
|
|
1412
1392
|
setRestThresholds();
|
|
1413
1393
|
let resolveSpring;
|
|
@@ -1476,11 +1456,16 @@
|
|
|
1476
1456
|
* dampedAngularFreq * t passes 300, both terms decay at the damping
|
|
1477
1457
|
* rate. Heavily overdamped drag springs (dragElastic: 0,
|
|
1478
1458
|
* dragTransition) rely on this to settle almost at once rather than
|
|
1479
|
-
* creep at stiffness / damping.
|
|
1480
|
-
*
|
|
1459
|
+
* creep at stiffness / damping. It only applies when reached within
|
|
1460
|
+
* 300ms (a dampedAngularFreq of 1 rad/ms), so it reads as a stiff
|
|
1461
|
+
* spring snapping into place: any later and it cuts off a slow spring
|
|
1462
|
+
* that's visibly still moving (#1207). Negative bounce needs the
|
|
1463
|
+
* exact curve, so time-defined springs are exempt.
|
|
1481
1464
|
*/
|
|
1482
|
-
const limit =
|
|
1483
|
-
|
|
1465
|
+
const limit = dampedAngularFreq < 1 || isTimeDefined
|
|
1466
|
+
? Infinity
|
|
1467
|
+
: 300 / dampedAngularFreq;
|
|
1468
|
+
const decayAt = (rate, t) => Math.exp(t > limit ? (decay - rate) * limit - decay * t : -rate * t);
|
|
1484
1469
|
const c = { S: 0, F: 0 };
|
|
1485
1470
|
update = () => {
|
|
1486
1471
|
const P = (s.velocity + decay * s.delta) / dampedAngularFreq;
|
|
@@ -1561,7 +1546,7 @@
|
|
|
1561
1546
|
};
|
|
1562
1547
|
let amplitude = power * velocity;
|
|
1563
1548
|
const ideal = origin + amplitude;
|
|
1564
|
-
const target = modifyTarget
|
|
1549
|
+
const target = modifyTarget ? modifyTarget(ideal) : ideal;
|
|
1565
1550
|
/**
|
|
1566
1551
|
* If the target has changed we need to re-calculate the amplitude, otherwise
|
|
1567
1552
|
* the animation will start from the wrong position.
|
|
@@ -1572,7 +1557,8 @@
|
|
|
1572
1557
|
const applyFriction = (t) => {
|
|
1573
1558
|
const delta = calcDelta(t);
|
|
1574
1559
|
state.done = Math.abs(delta) <= restDelta;
|
|
1575
|
-
|
|
1560
|
+
// target + delta can be off from origin by a float error at t = 0
|
|
1561
|
+
state.value = state.done ? target : t ? target + delta : origin;
|
|
1576
1562
|
};
|
|
1577
1563
|
/**
|
|
1578
1564
|
* Ideally this would resolve for t in a stateless way, we could
|
|
@@ -2018,17 +2004,12 @@
|
|
|
2018
2004
|
this.generator = generator;
|
|
2019
2005
|
}
|
|
2020
2006
|
updateTime(timestamp) {
|
|
2021
|
-
|
|
2022
|
-
//
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
// Rounding the time because floating point arithmetic is not always accurate, e.g. 3000.367 - 1000.367 =
|
|
2028
|
-
// 2000.0000000000002. This is a problem when we are comparing the currentTime with the duration, for
|
|
2029
|
-
// example.
|
|
2030
|
-
this.currentTime = animationTime;
|
|
2031
|
-
}
|
|
2007
|
+
// Rounding the time because floating point arithmetic is not always accurate, e.g. 3000.367 - 1000.367 =
|
|
2008
|
+
// 2000.0000000000002. This is a problem when we are comparing the currentTime with the duration, for
|
|
2009
|
+
// example.
|
|
2010
|
+
this.currentTime =
|
|
2011
|
+
this.holdTime ??
|
|
2012
|
+
Math.round(timestamp - this.startTime) * this.playbackSpeed;
|
|
2032
2013
|
}
|
|
2033
2014
|
tick(timestamp, sample = false) {
|
|
2034
2015
|
const { generator, totalDuration, calculatedDuration } = this;
|
|
@@ -2069,10 +2050,17 @@
|
|
|
2069
2050
|
}
|
|
2070
2051
|
const state = this.generate(this.currentTime, isInDelayPhase);
|
|
2071
2052
|
let { done } = state;
|
|
2072
|
-
|
|
2053
|
+
/**
|
|
2054
|
+
* A generator that takes longer than maxGeneratorDuration to finish
|
|
2055
|
+
* has an Infinity duration, so played forwards it finishes when it
|
|
2056
|
+
* says it's done.
|
|
2057
|
+
*/
|
|
2058
|
+
if (!isInDelayPhase) {
|
|
2073
2059
|
done =
|
|
2074
2060
|
this.playbackSpeed >= 0
|
|
2075
|
-
?
|
|
2061
|
+
? calculatedDuration < Infinity
|
|
2062
|
+
? this.currentTime >= totalDuration
|
|
2063
|
+
: done
|
|
2076
2064
|
: this.currentTime <= 0;
|
|
2077
2065
|
}
|
|
2078
2066
|
const isAnimationFinished = this.holdTime === null &&
|
|
@@ -2178,20 +2166,11 @@
|
|
|
2178
2166
|
}
|
|
2179
2167
|
return state;
|
|
2180
2168
|
}
|
|
2181
|
-
/**
|
|
2182
|
-
* Allows the returned animation to be awaited or promise-chained. Currently
|
|
2183
|
-
* resolves when the animation finishes at all but in a future update could/should
|
|
2184
|
-
* reject if its cancels.
|
|
2185
|
-
*/
|
|
2186
|
-
then(resolve, reject) {
|
|
2187
|
-
return this.finished.then(resolve, reject);
|
|
2188
|
-
}
|
|
2189
2169
|
get duration() {
|
|
2190
2170
|
return millisecondsToSeconds(this.calculatedDuration);
|
|
2191
2171
|
}
|
|
2192
2172
|
get iterationDuration() {
|
|
2193
|
-
|
|
2194
|
-
return this.duration + millisecondsToSeconds(delay);
|
|
2173
|
+
return this.duration + millisecondsToSeconds(this.options.delay ?? 0);
|
|
2195
2174
|
}
|
|
2196
2175
|
get time() {
|
|
2197
2176
|
return millisecondsToSeconds(this.currentTime);
|
|
@@ -2295,14 +2274,9 @@
|
|
|
2295
2274
|
}
|
|
2296
2275
|
teardown() {
|
|
2297
2276
|
this.state = "idle";
|
|
2298
|
-
this.
|
|
2299
|
-
this.startTime = this.holdTime = null;
|
|
2300
|
-
}
|
|
2301
|
-
stopDriver() {
|
|
2302
|
-
if (!this.driver)
|
|
2303
|
-
return;
|
|
2304
|
-
this.driver.stop();
|
|
2277
|
+
this.driver?.stop();
|
|
2305
2278
|
this.driver = undefined;
|
|
2279
|
+
this.startTime = this.holdTime = null;
|
|
2306
2280
|
}
|
|
2307
2281
|
sample(sampleTime) {
|
|
2308
2282
|
this.startTime = 0;
|
|
@@ -2559,23 +2533,15 @@
|
|
|
2559
2533
|
if (!transform || transform === "none") {
|
|
2560
2534
|
return defaultTransformValue(name);
|
|
2561
2535
|
}
|
|
2562
|
-
const
|
|
2563
|
-
let parsers;
|
|
2564
|
-
let match;
|
|
2565
|
-
if (matrix3dMatch) {
|
|
2566
|
-
parsers = matrix3dParsers;
|
|
2567
|
-
match = matrix3dMatch;
|
|
2568
|
-
}
|
|
2569
|
-
else {
|
|
2570
|
-
const matrix2dMatch = transform.match(/^matrix\(([-\d.e\s,]+)\)$/u);
|
|
2571
|
-
parsers = matrix2dParsers;
|
|
2572
|
-
match = matrix2dMatch;
|
|
2573
|
-
}
|
|
2536
|
+
const match = transform.match(/^matrix(3d)?\(([-\d.e\s,]+)\)$/u);
|
|
2574
2537
|
if (!match) {
|
|
2575
2538
|
return defaultTransformValue(name);
|
|
2576
2539
|
}
|
|
2577
|
-
const valueParser =
|
|
2578
|
-
|
|
2540
|
+
const valueParser = (match[1] ? matrix3dParsers : matrix2dParsers)[name];
|
|
2541
|
+
/**
|
|
2542
|
+
* parseFloat skips leading whitespace and stops at trailing whitespace.
|
|
2543
|
+
*/
|
|
2544
|
+
const values = match[2].split(",").map((v) => parseFloat(v));
|
|
2579
2545
|
return typeof valueParser === "function"
|
|
2580
2546
|
? valueParser(values)
|
|
2581
2547
|
: values[valueParser];
|
|
@@ -2584,9 +2550,6 @@
|
|
|
2584
2550
|
const { transform = "none" } = getComputedStyle(instance);
|
|
2585
2551
|
return parseValueFromTransform(transform, name);
|
|
2586
2552
|
};
|
|
2587
|
-
function convertTransformToNumber(value) {
|
|
2588
|
-
return parseFloat(value.trim());
|
|
2589
|
-
}
|
|
2590
2553
|
|
|
2591
2554
|
/**
|
|
2592
2555
|
* Generate a list of every possible transform key.
|
|
@@ -2989,7 +2952,9 @@
|
|
|
2989
2952
|
this.isPseudoElement = Boolean(pseudoElement);
|
|
2990
2953
|
this.allowFlatten = allowFlatten;
|
|
2991
2954
|
this.options = options;
|
|
2992
|
-
|
|
2955
|
+
typeof process !== "undefined" &&
|
|
2956
|
+
"development" !== "production" &&
|
|
2957
|
+
exports.invariant(typeof options.type !== "string", `Mini animate() doesn't support "type" as a string.`, "mini-spring");
|
|
2993
2958
|
const transition = applyGeneratorOptions(options);
|
|
2994
2959
|
this.animation = startWaapiAnimation(element, name, keyframes, transition, pseudoElement);
|
|
2995
2960
|
if (transition.autoplay === false) {
|
|
@@ -2999,9 +2964,7 @@
|
|
|
2999
2964
|
this.finishedTime = this.time;
|
|
3000
2965
|
if (!pseudoElement) {
|
|
3001
2966
|
const keyframe = getFinalKeyframe(keyframes, this.options, finalKeyframe, this.speed);
|
|
3002
|
-
|
|
3003
|
-
this.updateMotionValue(keyframe);
|
|
3004
|
-
}
|
|
2967
|
+
this.updateMotionValue?.(keyframe);
|
|
3005
2968
|
/**
|
|
3006
2969
|
* If we can, we want to commit the final style as set by the user,
|
|
3007
2970
|
* rather than the computed keyframe value supplied by the animation.
|
|
@@ -3322,6 +3285,7 @@
|
|
|
3322
3285
|
if (!isOriginAnimatable || !isTargetAnimatable) {
|
|
3323
3286
|
// Only build the message when we're going to warn
|
|
3324
3287
|
isOriginAnimatable !== isTargetAnimatable &&
|
|
3288
|
+
"development" !== "production" &&
|
|
3325
3289
|
exports.warning(false, `You are trying to animate ${name} from "${originKeyframe}" to "${targetKeyframe}". "${isOriginAnimatable ? targetKeyframe : originKeyframe}" is not an animatable value.`, "value-not-animatable");
|
|
3326
3290
|
return false;
|
|
3327
3291
|
}
|
|
@@ -3336,13 +3300,16 @@
|
|
|
3336
3300
|
|
|
3337
3301
|
/**
|
|
3338
3302
|
* A list of values that can be hardware-accelerated.
|
|
3303
|
+
*
|
|
3304
|
+
* Colors aren't included: WAAPI mixes them in sRGB, but JS animations
|
|
3305
|
+
* (and the sampler that estimates a WAAPI value on interrupt) mix them
|
|
3306
|
+
* in linear color space, so interrupting one would visibly jump.
|
|
3339
3307
|
*/
|
|
3340
3308
|
const acceleratedValues = new Set([
|
|
3341
3309
|
"opacity",
|
|
3342
3310
|
"clipPath",
|
|
3343
3311
|
"filter",
|
|
3344
3312
|
"transform",
|
|
3345
|
-
"backgroundColor",
|
|
3346
3313
|
]);
|
|
3347
3314
|
|
|
3348
3315
|
const browserColorFunctions = /^(?:oklch|oklab|lab|lch|color|color-mix|light-dark)\(/;
|
|
@@ -3434,6 +3401,20 @@
|
|
|
3434
3401
|
transformPerspective: "perspective",
|
|
3435
3402
|
};
|
|
3436
3403
|
const numTransforms = transformPropOrder.length;
|
|
3404
|
+
/**
|
|
3405
|
+
* Cache of `translateX(`-style openers so each transform function is a few
|
|
3406
|
+
* string concatenations rather than a template.
|
|
3407
|
+
*/
|
|
3408
|
+
const openers = {};
|
|
3409
|
+
/**
|
|
3410
|
+
* A transform function, e.g. `translateX(10px)` for `x: 10`.
|
|
3411
|
+
*/
|
|
3412
|
+
const transformFunction = (key, value, valueAsType = getValueAsType(value, transformValueTypes[key])) => (openers[key] || (openers[key] = (translateAlias[key] || key) + "(")) + valueAsType + ")";
|
|
3413
|
+
/**
|
|
3414
|
+
* Whether a transform value is at its default, so can be left out.
|
|
3415
|
+
*/
|
|
3416
|
+
const isDefaultTransform = (key, value) => (typeof value === "number" ? value : parseFloat(value)) ===
|
|
3417
|
+
(key.startsWith("scale") ? 1 : 0);
|
|
3437
3418
|
/**
|
|
3438
3419
|
* Build a CSS transform style from individual x/y/scale etc properties.
|
|
3439
3420
|
*
|
|
@@ -3443,7 +3424,6 @@
|
|
|
3443
3424
|
function buildTransform$1(latestValues, transform, transformTemplate) {
|
|
3444
3425
|
// The transform string we're going to build into.
|
|
3445
3426
|
let transformString = "";
|
|
3446
|
-
let transformIsDefault = true;
|
|
3447
3427
|
/**
|
|
3448
3428
|
* Loop over all possible transforms in order, adding the ones that
|
|
3449
3429
|
* are present to the transform string.
|
|
@@ -3453,24 +3433,11 @@
|
|
|
3453
3433
|
const value = latestValues[key];
|
|
3454
3434
|
if (value === undefined)
|
|
3455
3435
|
continue;
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
const parsed = parseFloat(value);
|
|
3462
|
-
valueIsDefault = key.startsWith("scale") ? parsed === 1 : parsed === 0;
|
|
3463
|
-
}
|
|
3464
|
-
if (!valueIsDefault || transformTemplate) {
|
|
3465
|
-
const valueAsType = getValueAsType(value, numberValueTypes[key]);
|
|
3466
|
-
if (!valueIsDefault) {
|
|
3467
|
-
transformIsDefault = false;
|
|
3468
|
-
const transformName = translateAlias[key] || key;
|
|
3469
|
-
transformString += `${transformName}(${valueAsType}) `;
|
|
3470
|
-
}
|
|
3471
|
-
if (transformTemplate) {
|
|
3472
|
-
transform[key] = valueAsType;
|
|
3473
|
-
}
|
|
3436
|
+
const valueAsType = transformTemplate &&
|
|
3437
|
+
(transform[key] = getValueAsType(value, numberValueTypes[key]));
|
|
3438
|
+
if (!isDefaultTransform(key, value)) {
|
|
3439
|
+
transformString +=
|
|
3440
|
+
transformFunction(key, value, valueAsType || undefined) + " ";
|
|
3474
3441
|
}
|
|
3475
3442
|
}
|
|
3476
3443
|
// `pathRotation` composes onto `rotate` as a separate additive term so
|
|
@@ -3478,19 +3445,14 @@
|
|
|
3478
3445
|
// `transformPropOrder`.
|
|
3479
3446
|
const pathRotation = latestValues.pathRotation;
|
|
3480
3447
|
if (pathRotation) {
|
|
3481
|
-
|
|
3482
|
-
transformString += `rotate(${getValueAsType(pathRotation, numberValueTypes.pathRotation)}) `;
|
|
3448
|
+
transformString += transformFunction("rotate", pathRotation);
|
|
3483
3449
|
}
|
|
3484
3450
|
transformString = transformString.trim();
|
|
3485
3451
|
// If we have a custom `transform` template, pass our transform values and
|
|
3486
3452
|
// generated transformString to that before returning
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
else if (transformIsDefault) {
|
|
3491
|
-
transformString = "none";
|
|
3492
|
-
}
|
|
3493
|
-
return transformString;
|
|
3453
|
+
return transformTemplate
|
|
3454
|
+
? transformTemplate(transform, transformString)
|
|
3455
|
+
: transformString || "none";
|
|
3494
3456
|
}
|
|
3495
3457
|
|
|
3496
3458
|
const transformGroups = new WeakMap();
|
|
@@ -3832,27 +3794,30 @@
|
|
|
3832
3794
|
return values;
|
|
3833
3795
|
};
|
|
3834
3796
|
/**
|
|
3835
|
-
* Build
|
|
3836
|
-
* is written, even at its default, so each keyframe has the
|
|
3837
|
-
* of functions and the browser interpolates them one by one.
|
|
3797
|
+
* Build a transform from values in transformPropOrder. For a keyframe,
|
|
3798
|
+
* every key is written, even at its default, so each keyframe has the
|
|
3799
|
+
* same list of functions and the browser interpolates them one by one.
|
|
3800
|
+
* As an inline style, defaults are left out, as buildTransform does.
|
|
3838
3801
|
*/
|
|
3839
|
-
const
|
|
3802
|
+
const toTransform = (values, isStyle) => {
|
|
3840
3803
|
let transform = "";
|
|
3841
|
-
const values = valuesAt(timestamp, settled);
|
|
3842
3804
|
for (const key in values) {
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
|
|
3847
|
-
") ";
|
|
3805
|
+
const value = values[key];
|
|
3806
|
+
if (!isStyle || !isDefaultTransform(key, value)) {
|
|
3807
|
+
transform += transformFunction(key, value) + " ";
|
|
3808
|
+
}
|
|
3848
3809
|
}
|
|
3849
|
-
return transform || "none";
|
|
3810
|
+
return (isStyle ? transform.trim() : transform) || "none";
|
|
3850
3811
|
};
|
|
3812
|
+
/**
|
|
3813
|
+
* Build the transform at a timestamp.
|
|
3814
|
+
*/
|
|
3815
|
+
const compose = (timestamp, settled) => toTransform(valuesAt(timestamp, settled));
|
|
3851
3816
|
/**
|
|
3852
3817
|
* Write the transform as an inline style, as the renderer would.
|
|
3853
3818
|
*/
|
|
3854
3819
|
const commit = (values = valuesAt()) => {
|
|
3855
|
-
element.style.transform =
|
|
3820
|
+
element.style.transform = toTransform(values, true);
|
|
3856
3821
|
};
|
|
3857
3822
|
/**
|
|
3858
3823
|
* Cancel the WAAPI animations, by default first committing the
|
|
@@ -3863,6 +3828,13 @@
|
|
|
3863
3828
|
segments.forEach(({ animation }) => animation.cancel());
|
|
3864
3829
|
segments = [];
|
|
3865
3830
|
};
|
|
3831
|
+
/**
|
|
3832
|
+
* A WAAPI animation that add() starts by setting its startTime. Playing
|
|
3833
|
+
* it first, as element.animate() does, would leave WebKit holding that
|
|
3834
|
+
* play's ready time once startTime ends the play, and it uses that for
|
|
3835
|
+
* the next updatePlaybackRate(), so the animation would jump.
|
|
3836
|
+
*/
|
|
3837
|
+
const create = (keyframes, options) => new Animation(new KeyframeEffect(element, keyframes, options));
|
|
3866
3838
|
const add = (animation, offset, end = Infinity) => {
|
|
3867
3839
|
const segment = { animation, offset, end };
|
|
3868
3840
|
animation.startTime = time.now() + offset;
|
|
@@ -4055,7 +4027,7 @@
|
|
|
4055
4027
|
commit(valuesAt(from, settled));
|
|
4056
4028
|
keyframes = null;
|
|
4057
4029
|
}
|
|
4058
|
-
return
|
|
4030
|
+
return create(keyframes, {
|
|
4059
4031
|
...options,
|
|
4060
4032
|
duration: to - from,
|
|
4061
4033
|
easing: easing || "linear",
|
|
@@ -4145,9 +4117,7 @@
|
|
|
4145
4117
|
* empty animations that end with them.
|
|
4146
4118
|
*/
|
|
4147
4119
|
ends.forEach((trackEnd) => trackEnd < end - 1 &&
|
|
4148
|
-
add(
|
|
4149
|
-
duration: trackEnd - now,
|
|
4150
|
-
}), 0, trackEnd - now));
|
|
4120
|
+
add(create(null, { duration: trackEnd - now }), 0, trackEnd - now));
|
|
4151
4121
|
}
|
|
4152
4122
|
if (cycle) {
|
|
4153
4123
|
/**
|
|
@@ -4221,12 +4191,29 @@
|
|
|
4221
4191
|
if (isDone)
|
|
4222
4192
|
continue;
|
|
4223
4193
|
const local = elapsed - offset;
|
|
4224
|
-
if (rate
|
|
4194
|
+
if (rate &&
|
|
4195
|
+
animation.updatePlaybackRate &&
|
|
4196
|
+
animation.playState === "running" &&
|
|
4197
|
+
Math.abs(Number(animation.currentTime) - local) < 20) {
|
|
4198
|
+
/**
|
|
4199
|
+
* Only the speed changed. Setting playbackRate and
|
|
4200
|
+
* startTime restarts a running compositor animation, which
|
|
4201
|
+
* can render the underlying value for a frame.
|
|
4202
|
+
*/
|
|
4203
|
+
animation.playbackRate !== rate &&
|
|
4204
|
+
animation.updatePlaybackRate(rate);
|
|
4205
|
+
}
|
|
4206
|
+
else if (rate) {
|
|
4225
4207
|
animation.playbackRate = rate;
|
|
4226
4208
|
animation.startTime = now - local / rate;
|
|
4227
4209
|
}
|
|
4228
4210
|
else {
|
|
4229
|
-
|
|
4211
|
+
/**
|
|
4212
|
+
* Hold it without pause(), whose pending pause setting
|
|
4213
|
+
* currentTime would end at once, leaving WebKit holding its
|
|
4214
|
+
* ready time (see create()).
|
|
4215
|
+
*/
|
|
4216
|
+
animation.startTime = null;
|
|
4230
4217
|
animation.currentTime = local;
|
|
4231
4218
|
}
|
|
4232
4219
|
}
|
|
@@ -4417,9 +4404,8 @@
|
|
|
4417
4404
|
* If we can't animate this value with the resolved keyframes
|
|
4418
4405
|
* then we should complete it immediately.
|
|
4419
4406
|
*/
|
|
4420
|
-
|
|
4421
|
-
if (!
|
|
4422
|
-
canAnimateValue = false;
|
|
4407
|
+
const canAnimateValue = canAnimate(keyframes, name, type, velocity);
|
|
4408
|
+
if (!canAnimateValue) {
|
|
4423
4409
|
if (MotionGlobalConfig.instantAnimations || !delay) {
|
|
4424
4410
|
onUpdate?.(getFinalKeyframe(keyframes, options, finalKeyframe));
|
|
4425
4411
|
}
|
|
@@ -4563,9 +4549,7 @@
|
|
|
4563
4549
|
this.animation.complete();
|
|
4564
4550
|
}
|
|
4565
4551
|
cancel() {
|
|
4566
|
-
|
|
4567
|
-
this.animation.cancel();
|
|
4568
|
-
}
|
|
4552
|
+
this._animation?.cancel();
|
|
4569
4553
|
this.keyframeResolver?.cancel();
|
|
4570
4554
|
}
|
|
4571
4555
|
}
|
|
@@ -4748,7 +4732,7 @@
|
|
|
4748
4732
|
this.setCurrent(v);
|
|
4749
4733
|
// Update update subscribers
|
|
4750
4734
|
if (this.current !== this.prev) {
|
|
4751
|
-
this.
|
|
4735
|
+
this.dirty();
|
|
4752
4736
|
if (this.dependents) {
|
|
4753
4737
|
for (const dependent of this.dependents) {
|
|
4754
4738
|
dependent.dirty();
|
|
@@ -4886,12 +4870,9 @@
|
|
|
4886
4870
|
* @public
|
|
4887
4871
|
*/
|
|
4888
4872
|
set(v) {
|
|
4889
|
-
|
|
4890
|
-
this.
|
|
4891
|
-
|
|
4892
|
-
else {
|
|
4893
|
-
this.passiveEffect(v, this.updateAndNotify);
|
|
4894
|
-
}
|
|
4873
|
+
this.passiveEffect
|
|
4874
|
+
? this.passiveEffect(v, this.updateAndNotify)
|
|
4875
|
+
: this.updateAndNotify(v);
|
|
4895
4876
|
}
|
|
4896
4877
|
setWithVelocity(prev, current, delta) {
|
|
4897
4878
|
this.set(current);
|
|
@@ -4908,13 +4889,9 @@
|
|
|
4908
4889
|
this.prev = v;
|
|
4909
4890
|
this.prevUpdatedAt = this.prevFrameValue = undefined;
|
|
4910
4891
|
endAnimation && this.stop();
|
|
4911
|
-
|
|
4912
|
-
this.stopPassiveEffect();
|
|
4892
|
+
this.stopPassiveEffect?.();
|
|
4913
4893
|
}
|
|
4914
4894
|
dirty() {
|
|
4915
|
-
this.notifyChange();
|
|
4916
|
-
}
|
|
4917
|
-
notifyChange() {
|
|
4918
4895
|
const { current, changeSubscriber } = this;
|
|
4919
4896
|
/**
|
|
4920
4897
|
* One or the other exists. Subscribing during the direct
|
|
@@ -4929,15 +4906,10 @@
|
|
|
4929
4906
|
}
|
|
4930
4907
|
}
|
|
4931
4908
|
addDependent(dependent) {
|
|
4932
|
-
|
|
4933
|
-
this.dependents = new Set();
|
|
4934
|
-
}
|
|
4935
|
-
this.dependents.add(dependent);
|
|
4909
|
+
(this.dependents || (this.dependents = new Set())).add(dependent);
|
|
4936
4910
|
}
|
|
4937
4911
|
removeDependent(dependent) {
|
|
4938
|
-
|
|
4939
|
-
this.dependents.delete(dependent);
|
|
4940
|
-
}
|
|
4912
|
+
this.dependents?.delete(dependent);
|
|
4941
4913
|
}
|
|
4942
4914
|
/**
|
|
4943
4915
|
* Returns the latest state of `MotionValue`
|
|
@@ -4947,9 +4919,7 @@
|
|
|
4947
4919
|
* @public
|
|
4948
4920
|
*/
|
|
4949
4921
|
get() {
|
|
4950
|
-
|
|
4951
|
-
collectMotionValues.current.push(this);
|
|
4952
|
-
}
|
|
4922
|
+
collectMotionValues.current?.push(this);
|
|
4953
4923
|
/**
|
|
4954
4924
|
* A value animating on the compositor isn't updated every frame,
|
|
4955
4925
|
* so read it from its animation.
|
|
@@ -5015,7 +4985,7 @@
|
|
|
5015
4985
|
isComplete = true;
|
|
5016
4986
|
this.events.animationComplete?.notify();
|
|
5017
4987
|
if (this.animation === animation)
|
|
5018
|
-
this.
|
|
4988
|
+
this.animation = undefined;
|
|
5019
4989
|
resolve();
|
|
5020
4990
|
});
|
|
5021
4991
|
if (!isComplete)
|
|
@@ -5031,11 +5001,10 @@
|
|
|
5031
5001
|
stop() {
|
|
5032
5002
|
if (this.animation) {
|
|
5033
5003
|
this.animation.stop();
|
|
5034
|
-
|
|
5035
|
-
this.events.animationCancel.notify();
|
|
5036
|
-
}
|
|
5004
|
+
this.events.animationCancel?.notify();
|
|
5037
5005
|
}
|
|
5038
|
-
|
|
5006
|
+
// Assign rather than delete to keep the object's shape stable
|
|
5007
|
+
this.animation = undefined;
|
|
5039
5008
|
}
|
|
5040
5009
|
/**
|
|
5041
5010
|
* Returns `true` if this value is currently animating.
|
|
@@ -5045,10 +5014,6 @@
|
|
|
5045
5014
|
isAnimating() {
|
|
5046
5015
|
return !!this.animation;
|
|
5047
5016
|
}
|
|
5048
|
-
clearAnimation() {
|
|
5049
|
-
// Assign rather than delete to keep the object's shape stable
|
|
5050
|
-
this.animation = undefined;
|
|
5051
|
-
}
|
|
5052
5017
|
/**
|
|
5053
5018
|
* Destroy and clean up subscribers to this `MotionValue`.
|
|
5054
5019
|
*
|
|
@@ -5063,9 +5028,7 @@
|
|
|
5063
5028
|
this.events.destroy?.notify();
|
|
5064
5029
|
this.clearListeners();
|
|
5065
5030
|
this.stop();
|
|
5066
|
-
|
|
5067
|
-
this.stopPassiveEffect();
|
|
5068
|
-
}
|
|
5031
|
+
this.stopPassiveEffect?.();
|
|
5069
5032
|
}
|
|
5070
5033
|
}
|
|
5071
5034
|
function motionValue(init, options) {
|
|
@@ -5143,6 +5106,7 @@
|
|
|
5143
5106
|
"repeatDelay",
|
|
5144
5107
|
"from",
|
|
5145
5108
|
"elapsed",
|
|
5109
|
+
"out",
|
|
5146
5110
|
]);
|
|
5147
5111
|
/**
|
|
5148
5112
|
* Decide whether a transition is defined on a given Transition.
|
|
@@ -5613,7 +5577,7 @@
|
|
|
5613
5577
|
}
|
|
5614
5578
|
const valueTransition = {
|
|
5615
5579
|
delay,
|
|
5616
|
-
...getValueTransition$1(transition || {}, key),
|
|
5580
|
+
...getValueTransition$1(animationTypeState?.out[key] || transition || {}, key),
|
|
5617
5581
|
};
|
|
5618
5582
|
if (skipAnimations)
|
|
5619
5583
|
valueTransition.skipAnimations = true;
|
|
@@ -5783,8 +5747,9 @@
|
|
|
5783
5747
|
}
|
|
5784
5748
|
|
|
5785
5749
|
class DOMKeyframesResolver extends KeyframeResolver {
|
|
5786
|
-
constructor(
|
|
5787
|
-
super(
|
|
5750
|
+
constructor() {
|
|
5751
|
+
super(...arguments);
|
|
5752
|
+
this.isAsync = true;
|
|
5788
5753
|
}
|
|
5789
5754
|
/**
|
|
5790
5755
|
* Keyframes that don't need reading from the DOM, or measuring,
|
|
@@ -5812,7 +5777,7 @@
|
|
|
5812
5777
|
}
|
|
5813
5778
|
readKeyframes() {
|
|
5814
5779
|
const { unresolvedKeyframes, element, name } = this;
|
|
5815
|
-
if (!element
|
|
5780
|
+
if (!element?.current)
|
|
5816
5781
|
return;
|
|
5817
5782
|
super.readKeyframes();
|
|
5818
5783
|
/**
|
|
@@ -5907,11 +5872,10 @@
|
|
|
5907
5872
|
}
|
|
5908
5873
|
measureInitialState() {
|
|
5909
5874
|
const { element, unresolvedKeyframes, name } = this;
|
|
5910
|
-
if (!element
|
|
5875
|
+
if (!element?.current)
|
|
5911
5876
|
return;
|
|
5912
|
-
if (name === "height")
|
|
5877
|
+
if (name === "height")
|
|
5913
5878
|
this.suspendedScrollY = window.pageYOffset;
|
|
5914
|
-
}
|
|
5915
5879
|
this.measuredOrigin = this.measure();
|
|
5916
5880
|
unresolvedKeyframes[0] = this.measuredOrigin;
|
|
5917
5881
|
// Set final key frame to measure after next render
|
|
@@ -5922,7 +5886,7 @@
|
|
|
5922
5886
|
}
|
|
5923
5887
|
measureEndState() {
|
|
5924
5888
|
const { element, unresolvedKeyframes } = this;
|
|
5925
|
-
if (!element
|
|
5889
|
+
if (!element?.current)
|
|
5926
5890
|
return;
|
|
5927
5891
|
this.motionValue?.jump(this.measuredOrigin, false);
|
|
5928
5892
|
const finalKeyframeIndex = unresolvedKeyframes.length - 1;
|
|
@@ -5931,14 +5895,6 @@
|
|
|
5931
5895
|
if (finalKeyframe !== null && this.finalKeyframe === undefined) {
|
|
5932
5896
|
this.finalKeyframe = finalKeyframe;
|
|
5933
5897
|
}
|
|
5934
|
-
// If we removed transform values, reapply them before the next render
|
|
5935
|
-
if (this.removedTransforms?.length) {
|
|
5936
|
-
this.removedTransforms.forEach(([unsetTransformName, unsetTransformValue]) => {
|
|
5937
|
-
element
|
|
5938
|
-
.getValue(unsetTransformName)
|
|
5939
|
-
.set(unsetTransformValue);
|
|
5940
|
-
});
|
|
5941
|
-
}
|
|
5942
5898
|
this.resolveNoneKeyframes();
|
|
5943
5899
|
}
|
|
5944
5900
|
}
|
|
@@ -6043,7 +5999,8 @@
|
|
|
6043
5999
|
*/
|
|
6044
6000
|
const effects = [];
|
|
6045
6001
|
function addEffect(effect) {
|
|
6046
|
-
exports.invariant(typeof effect.test === "function" &&
|
|
6002
|
+
exports.invariant(typeof effect.test === "function" &&
|
|
6003
|
+
typeof effect.read === "function", "Effects passed to animate.addEffect() need test() and read().", "effect-missing-test");
|
|
6047
6004
|
removeEffect(effect);
|
|
6048
6005
|
effects.unshift(effect);
|
|
6049
6006
|
}
|
|
@@ -6370,11 +6327,6 @@
|
|
|
6370
6327
|
});
|
|
6371
6328
|
}
|
|
6372
6329
|
|
|
6373
|
-
/**
|
|
6374
|
-
* Cache of `translateX(`-style openers so each render is a few string
|
|
6375
|
-
* concatenations rather than a template per transform.
|
|
6376
|
-
*/
|
|
6377
|
-
const openers = {};
|
|
6378
6330
|
function buildTransform(state) {
|
|
6379
6331
|
let transform = "";
|
|
6380
6332
|
const { transformKeys: keys = [], transformValues: values = {} } = state;
|
|
@@ -6387,14 +6339,8 @@
|
|
|
6387
6339
|
const value = values[key].getCurrent();
|
|
6388
6340
|
if (value === undefined)
|
|
6389
6341
|
continue;
|
|
6390
|
-
|
|
6391
|
-
|
|
6392
|
-
transform +=
|
|
6393
|
-
(transform && " ") +
|
|
6394
|
-
(openers[key] ||
|
|
6395
|
-
(openers[key] = (translateAlias[key] || key) + "(")) +
|
|
6396
|
-
getValueAsType(value, transformValueTypes[key]) +
|
|
6397
|
-
")";
|
|
6342
|
+
if (!isDefaultTransform(key, value)) {
|
|
6343
|
+
transform += (transform && " ") + transformFunction(key, value);
|
|
6398
6344
|
}
|
|
6399
6345
|
}
|
|
6400
6346
|
// See build-transform.ts: additive `rotate()` so user `rotate` isn't
|
|
@@ -6402,10 +6348,7 @@
|
|
|
6402
6348
|
const pathRotation = state.get("pathRotation")?.get();
|
|
6403
6349
|
if (pathRotation) {
|
|
6404
6350
|
transform +=
|
|
6405
|
-
(transform && " ") +
|
|
6406
|
-
"rotate(" +
|
|
6407
|
-
getValueAsType(pathRotation, transformValueTypes.pathRotation) +
|
|
6408
|
-
")";
|
|
6351
|
+
(transform && " ") + transformFunction("rotate", pathRotation);
|
|
6409
6352
|
}
|
|
6410
6353
|
return transform || "none";
|
|
6411
6354
|
}
|
|
@@ -6808,10 +6751,24 @@
|
|
|
6808
6751
|
!isIdentityScale(scaleX) ||
|
|
6809
6752
|
!isIdentityScale(scaleY));
|
|
6810
6753
|
}
|
|
6754
|
+
/**
|
|
6755
|
+
* Rotations and skews, which projection can't correct for.
|
|
6756
|
+
*/
|
|
6757
|
+
const angleTransforms = [
|
|
6758
|
+
"rotate",
|
|
6759
|
+
"rotateX",
|
|
6760
|
+
"rotateY",
|
|
6761
|
+
"rotateZ",
|
|
6762
|
+
"skewX",
|
|
6763
|
+
"skewY",
|
|
6764
|
+
];
|
|
6811
6765
|
function hasTransform(values) {
|
|
6812
6766
|
return (hasScale(values) ||
|
|
6813
6767
|
has2DTranslate(values) ||
|
|
6814
|
-
values
|
|
6768
|
+
hasDistortingTransform(values));
|
|
6769
|
+
}
|
|
6770
|
+
function hasDistortingTransform(values) {
|
|
6771
|
+
return (values.z ||
|
|
6815
6772
|
values.rotate ||
|
|
6816
6773
|
values.rotateX ||
|
|
6817
6774
|
values.rotateY ||
|
|
@@ -6864,6 +6821,11 @@
|
|
|
6864
6821
|
* in a tree upon our box before then calculating how to project it into our desired viewport-relative box
|
|
6865
6822
|
*
|
|
6866
6823
|
* This is the final nested loop within updateLayoutDelta for future refactoring
|
|
6824
|
+
*
|
|
6825
|
+
* isSharedTransition is true for a shared layout animation in page space, which
|
|
6826
|
+
* also applies ancestor scroll and transforms. Or it's the layoutRoot a shared
|
|
6827
|
+
* animation is relative to, so only transforms of ancestors not shared with it
|
|
6828
|
+
* (or above it) are applied.
|
|
6867
6829
|
*/
|
|
6868
6830
|
function applyTreeDeltas(box, treeScale, treePath, isSharedTransition = false) {
|
|
6869
6831
|
const treeLength = treePath.length;
|
|
@@ -6880,13 +6842,10 @@
|
|
|
6880
6842
|
* TODO: Prefer to remove this, but currently we have motion components with
|
|
6881
6843
|
* display: contents in Framer.
|
|
6882
6844
|
*/
|
|
6883
|
-
|
|
6884
|
-
if (visualElement &&
|
|
6885
|
-
visualElement.props.style &&
|
|
6886
|
-
visualElement.props.style.display === "contents") {
|
|
6845
|
+
if (node.options.visualElement?.props.style?.display === "contents") {
|
|
6887
6846
|
continue;
|
|
6888
6847
|
}
|
|
6889
|
-
if (isSharedTransition &&
|
|
6848
|
+
if (isSharedTransition === true &&
|
|
6890
6849
|
node.options.layoutScroll &&
|
|
6891
6850
|
node.scroll &&
|
|
6892
6851
|
node !== node.root) {
|
|
@@ -6900,7 +6859,9 @@
|
|
|
6900
6859
|
// Apply each ancestor's calculated delta into this component's recorded layout box
|
|
6901
6860
|
applyBoxDelta(box, delta);
|
|
6902
6861
|
}
|
|
6903
|
-
if (isSharedTransition &&
|
|
6862
|
+
if (isSharedTransition &&
|
|
6863
|
+
(isSharedTransition.path?.[i] || isSharedTransition) !== node &&
|
|
6864
|
+
hasTransform(node.latestValues)) {
|
|
6904
6865
|
transformBox(box, node.latestValues, node.layout?.layoutBox);
|
|
6905
6866
|
}
|
|
6906
6867
|
}
|
|
@@ -7167,15 +7128,6 @@
|
|
|
7167
7128
|
const asNumber = (v) => typeof v === "number" ? v : parseFloat(v);
|
|
7168
7129
|
|
|
7169
7130
|
const numBorders = cornerRadiusProps.length;
|
|
7170
|
-
const mixedTransforms = [
|
|
7171
|
-
"rotate",
|
|
7172
|
-
"rotateX",
|
|
7173
|
-
"rotateY",
|
|
7174
|
-
"rotateZ",
|
|
7175
|
-
"skewX",
|
|
7176
|
-
"skewY",
|
|
7177
|
-
];
|
|
7178
|
-
const numMixedTransforms = mixedTransforms.length;
|
|
7179
7131
|
const isPx = (value) => typeof value === "number" || px.test(value);
|
|
7180
7132
|
function mixValues(target, follow, lead, progress, shouldCrossfadeOpacity, isOnlyMember) {
|
|
7181
7133
|
if (shouldCrossfadeOpacity) {
|
|
@@ -7213,8 +7165,8 @@
|
|
|
7213
7165
|
* Mix rotation and skew. These can't be projected, so mix them every
|
|
7214
7166
|
* frame rather than leaving them frozen at the animation-start snapshot.
|
|
7215
7167
|
*/
|
|
7216
|
-
for (let i = 0; i <
|
|
7217
|
-
const key =
|
|
7168
|
+
for (let i = 0; i < angleTransforms.length; i++) {
|
|
7169
|
+
const key = angleTransforms[i];
|
|
7218
7170
|
if (follow[key] || lead[key]) {
|
|
7219
7171
|
target[key] = mixNumber$1(follow[key] || 0, lead[key] || 0, progress);
|
|
7220
7172
|
}
|
|
@@ -7297,8 +7249,8 @@
|
|
|
7297
7249
|
}
|
|
7298
7250
|
}
|
|
7299
7251
|
function calcBoxDelta(delta, source, target, origin) {
|
|
7300
|
-
calcAxisDelta(delta.x, source.x, target.x, origin
|
|
7301
|
-
calcAxisDelta(delta.y, source.y, target.y, origin
|
|
7252
|
+
calcAxisDelta(delta.x, source.x, target.x, origin?.originX);
|
|
7253
|
+
calcAxisDelta(delta.y, source.y, target.y, origin?.originY);
|
|
7302
7254
|
}
|
|
7303
7255
|
function calcRelativeAxis(target, relative, parent, anchor = 0) {
|
|
7304
7256
|
const anchorPoint = anchor
|
|
@@ -7368,8 +7320,8 @@
|
|
|
7368
7320
|
* and acts as a bridge between motion values and removeAxisDelta
|
|
7369
7321
|
*/
|
|
7370
7322
|
function removeBoxTransforms(box, transforms, originBox, sourceBox) {
|
|
7371
|
-
removeAxisTransforms(box.x, transforms, xKeys, originBox
|
|
7372
|
-
removeAxisTransforms(box.y, transforms, yKeys, originBox
|
|
7323
|
+
removeAxisTransforms(box.x, transforms, xKeys, originBox?.x, sourceBox?.x);
|
|
7324
|
+
removeAxisTransforms(box.y, transforms, yKeys, originBox?.y, sourceBox?.y);
|
|
7373
7325
|
}
|
|
7374
7326
|
|
|
7375
7327
|
const createAxisDelta = () => ({
|
|
@@ -7599,24 +7551,18 @@
|
|
|
7599
7551
|
transform += `scale(${1 / treeScale.x}, ${1 / treeScale.y}) `;
|
|
7600
7552
|
}
|
|
7601
7553
|
if (latestTransform) {
|
|
7602
|
-
const { transformPerspective
|
|
7554
|
+
const { transformPerspective } = latestTransform;
|
|
7603
7555
|
if (transformPerspective)
|
|
7604
7556
|
transform = `perspective(${transformPerspective}px) ${transform}`;
|
|
7605
|
-
|
|
7606
|
-
|
|
7607
|
-
|
|
7608
|
-
|
|
7609
|
-
|
|
7610
|
-
|
|
7611
|
-
|
|
7612
|
-
|
|
7613
|
-
|
|
7614
|
-
if (rotateZ)
|
|
7615
|
-
transform += `rotateZ(${rotateZ}deg) `;
|
|
7616
|
-
if (skewX)
|
|
7617
|
-
transform += `skewX(${skewX}deg) `;
|
|
7618
|
-
if (skewY)
|
|
7619
|
-
transform += `skewY(${skewY}deg) `;
|
|
7557
|
+
for (let i = 0; i < angleTransforms.length; i++) {
|
|
7558
|
+
const key = angleTransforms[i];
|
|
7559
|
+
const value = latestTransform[key];
|
|
7560
|
+
if (value)
|
|
7561
|
+
transform += `${key}(${value}deg) `;
|
|
7562
|
+
// Additive `rotate()` so user `rotate` isn't clobbered.
|
|
7563
|
+
if (!i && latestTransform.pathRotation)
|
|
7564
|
+
transform += `rotate(${latestTransform.pathRotation}deg) `;
|
|
7565
|
+
}
|
|
7620
7566
|
}
|
|
7621
7567
|
/**
|
|
7622
7568
|
* Apply scale to match the size of the element to the size we want it.
|
|
@@ -7679,7 +7625,10 @@
|
|
|
7679
7625
|
calculatedTargetDeltas: 0,
|
|
7680
7626
|
calculatedProjections: 0,
|
|
7681
7627
|
};
|
|
7682
|
-
|
|
7628
|
+
/**
|
|
7629
|
+
* Transforms that distort an element's layout, so must be reset before measuring.
|
|
7630
|
+
*/
|
|
7631
|
+
const distortingTransforms = ["z", "skew", "skewZ"];
|
|
7683
7632
|
/**
|
|
7684
7633
|
* We use 1000 as the animation target as 0-1000 maps better to pixels than 0-1
|
|
7685
7634
|
* which has a noticeable difference in spring animations
|
|
@@ -7887,8 +7836,7 @@
|
|
|
7887
7836
|
return this.eventHandlers.get(name).add(handler);
|
|
7888
7837
|
}
|
|
7889
7838
|
notifyListeners(name, ...args) {
|
|
7890
|
-
|
|
7891
|
-
subscriptionManager && subscriptionManager.notify(...args);
|
|
7839
|
+
this.eventHandlers.get(name)?.notify(...args);
|
|
7892
7840
|
}
|
|
7893
7841
|
hasListeners(name) {
|
|
7894
7842
|
return this.eventHandlers.has(name);
|
|
@@ -7906,7 +7854,7 @@
|
|
|
7906
7854
|
visualElement.mount(instance);
|
|
7907
7855
|
}
|
|
7908
7856
|
this.root.nodes.add(this);
|
|
7909
|
-
this.parent
|
|
7857
|
+
this.parent?.children.add(this);
|
|
7910
7858
|
if (this.root.hasTreeAnimated && (layout || layoutId)) {
|
|
7911
7859
|
this.isLayoutDirty = true;
|
|
7912
7860
|
}
|
|
@@ -7924,7 +7872,7 @@
|
|
|
7924
7872
|
return;
|
|
7925
7873
|
innerWidth = newInnerWidth;
|
|
7926
7874
|
this.root.updateBlockedByResize = true;
|
|
7927
|
-
cancelDelay
|
|
7875
|
+
cancelDelay?.();
|
|
7928
7876
|
cancelDelay = delay(resizeUnblockUpdate, 250);
|
|
7929
7877
|
if (globalProjectionState.hasAnimatedSinceResize) {
|
|
7930
7878
|
globalProjectionState.hasAnimatedSinceResize = false;
|
|
@@ -8015,9 +7963,7 @@
|
|
|
8015
7963
|
if (!hasLayoutChanged) {
|
|
8016
7964
|
finishAnimation(this);
|
|
8017
7965
|
}
|
|
8018
|
-
|
|
8019
|
-
this.options.onExitComplete();
|
|
8020
|
-
}
|
|
7966
|
+
this.isLead() && this.options.onExitComplete?.();
|
|
8021
7967
|
}
|
|
8022
7968
|
this.targetLayout = newLayout;
|
|
8023
7969
|
});
|
|
@@ -8025,9 +7971,8 @@
|
|
|
8025
7971
|
unmount() {
|
|
8026
7972
|
this.options.layoutId && this.willUpdate();
|
|
8027
7973
|
this.root.nodes.remove(this);
|
|
8028
|
-
|
|
8029
|
-
|
|
8030
|
-
this.parent && this.parent.children.delete(this);
|
|
7974
|
+
this.getStack()?.remove(this);
|
|
7975
|
+
this.parent?.children.delete(this);
|
|
8031
7976
|
this.instance = undefined;
|
|
8032
7977
|
this.eventHandlers.clear();
|
|
8033
7978
|
this.hasLayoutListener = false;
|
|
@@ -8048,7 +7993,7 @@
|
|
|
8048
7993
|
if (this.isUpdateBlocked())
|
|
8049
7994
|
return;
|
|
8050
7995
|
this.isUpdating = true;
|
|
8051
|
-
this.nodes
|
|
7996
|
+
this.nodes?.forEach(resetSkewAndRotation);
|
|
8052
7997
|
this.animationId++;
|
|
8053
7998
|
}
|
|
8054
7999
|
getTransformTemplate() {
|
|
@@ -8058,7 +8003,7 @@
|
|
|
8058
8003
|
willUpdate(shouldNotifyListeners = true) {
|
|
8059
8004
|
this.root.hasTreeAnimated = true;
|
|
8060
8005
|
if (this.root.isUpdateBlocked()) {
|
|
8061
|
-
this.options.onExitComplete
|
|
8006
|
+
this.options.onExitComplete?.();
|
|
8062
8007
|
return;
|
|
8063
8008
|
}
|
|
8064
8009
|
/**
|
|
@@ -8275,24 +8220,23 @@
|
|
|
8275
8220
|
this.projectionDelta = undefined;
|
|
8276
8221
|
this.notifyListeners("measure", this.layout.layoutBox);
|
|
8277
8222
|
const { visualElement } = this.options;
|
|
8278
|
-
visualElement
|
|
8279
|
-
visualElement.notify("LayoutMeasure", this.layout.layoutBox, prevLayout ? prevLayout.layoutBox : undefined);
|
|
8223
|
+
visualElement?.notify("LayoutMeasure", this.layout.layoutBox, prevLayout?.layoutBox);
|
|
8280
8224
|
}
|
|
8281
8225
|
updateScroll(phase = "measure") {
|
|
8282
|
-
|
|
8283
|
-
|
|
8284
|
-
|
|
8285
|
-
|
|
8286
|
-
|
|
8287
|
-
|
|
8288
|
-
|
|
8289
|
-
const isRoot = checkIsScrollRoot(
|
|
8226
|
+
const { scroll, instance } = this;
|
|
8227
|
+
const { animationId } = this.root;
|
|
8228
|
+
if (this.options.layoutScroll &&
|
|
8229
|
+
instance &&
|
|
8230
|
+
!(scroll &&
|
|
8231
|
+
scroll.animationId === animationId &&
|
|
8232
|
+
scroll.phase === phase)) {
|
|
8233
|
+
const isRoot = checkIsScrollRoot(instance);
|
|
8290
8234
|
this.scroll = {
|
|
8291
|
-
animationId
|
|
8235
|
+
animationId,
|
|
8292
8236
|
phase,
|
|
8293
8237
|
isRoot,
|
|
8294
|
-
offset: measureScroll(
|
|
8295
|
-
wasRoot:
|
|
8238
|
+
offset: measureScroll(instance),
|
|
8239
|
+
wasRoot: scroll ? scroll.isRoot : isRoot,
|
|
8296
8240
|
};
|
|
8297
8241
|
}
|
|
8298
8242
|
}
|
|
@@ -8382,10 +8326,10 @@
|
|
|
8382
8326
|
}
|
|
8383
8327
|
return boxWithoutScroll;
|
|
8384
8328
|
}
|
|
8385
|
-
applyTransform(box, transformOnly = false, output, skipOwnTransform) {
|
|
8329
|
+
applyTransform(box, transformOnly = false, output, skipOwnTransform, start = 0) {
|
|
8386
8330
|
const withTransforms = output || createBox();
|
|
8387
8331
|
copyBoxInto(withTransforms, box);
|
|
8388
|
-
for (let i =
|
|
8332
|
+
for (let i = start; i < this.path.length; i++) {
|
|
8389
8333
|
const node = this.path[i];
|
|
8390
8334
|
if (!transformOnly &&
|
|
8391
8335
|
node.options.layoutScroll &&
|
|
@@ -8557,8 +8501,13 @@
|
|
|
8557
8501
|
this.attemptToResolveRelativeTarget = false;
|
|
8558
8502
|
if (this.options.layoutAnchor !== false &&
|
|
8559
8503
|
relativeParent &&
|
|
8560
|
-
|
|
8561
|
-
|
|
8504
|
+
(!relativeParent.resumingFrom === !this.resumingFrom ||
|
|
8505
|
+
/**
|
|
8506
|
+
* Shared layout animations within a layoutRoot
|
|
8507
|
+
* animate relative to it.
|
|
8508
|
+
*/
|
|
8509
|
+
(relativeParent.options.layoutRoot &&
|
|
8510
|
+
!relativeParent.resumingFrom)) &&
|
|
8562
8511
|
!relativeParent.options.layoutScroll &&
|
|
8563
8512
|
relativeParent.target &&
|
|
8564
8513
|
this.animationProgress !== 1) {
|
|
@@ -8638,7 +8587,7 @@
|
|
|
8638
8587
|
* If this section of the tree isn't animating we can
|
|
8639
8588
|
* delete our target sources for the following frame.
|
|
8640
8589
|
*/
|
|
8641
|
-
this.isTreeAnimating = Boolean(
|
|
8590
|
+
this.isTreeAnimating = Boolean(this.parent?.isTreeAnimating ||
|
|
8642
8591
|
this.currentAnimation ||
|
|
8643
8592
|
this.pendingAnimation);
|
|
8644
8593
|
if (!this.isTreeAnimating) {
|
|
@@ -8659,8 +8608,17 @@
|
|
|
8659
8608
|
/**
|
|
8660
8609
|
* Apply all the parent deltas to this box to produce the corrected box. This
|
|
8661
8610
|
* is the layout box, as it will appear on screen as a result of the transforms of its parents.
|
|
8611
|
+
*
|
|
8612
|
+
* A shared target is usually in page space, so ancestor transforms are applied
|
|
8613
|
+
* too. But a target relative to a parent that isn't itself shared (like a
|
|
8614
|
+
* layoutRoot) is in that parent's layout space. So the transforms of the
|
|
8615
|
+
* parent and its ancestors, which move both elements alike, are skipped,
|
|
8616
|
+
* but those of other ancestors (between it and either element) apply.
|
|
8662
8617
|
*/
|
|
8663
|
-
applyTreeDeltas(this.layoutCorrected, this.treeScale, this.path, isShared
|
|
8618
|
+
applyTreeDeltas(this.layoutCorrected, this.treeScale, this.path, isShared &&
|
|
8619
|
+
(lead.relativeTarget && !lead.relativeParent.resumingFrom
|
|
8620
|
+
? lead.relativeParent
|
|
8621
|
+
: true));
|
|
8664
8622
|
/**
|
|
8665
8623
|
* If this layer needs to perform scale correction but doesn't have a target,
|
|
8666
8624
|
* use the layout as the target.
|
|
@@ -8727,8 +8685,7 @@
|
|
|
8727
8685
|
scheduleRender(notifyAll = true) {
|
|
8728
8686
|
this.options.visualElement?.scheduleRender();
|
|
8729
8687
|
if (notifyAll) {
|
|
8730
|
-
|
|
8731
|
-
stack && stack.scheduleRender();
|
|
8688
|
+
this.getStack()?.scheduleRender();
|
|
8732
8689
|
}
|
|
8733
8690
|
if (this.resumingFrom && !this.resumingFrom.instance) {
|
|
8734
8691
|
this.resumingFrom = undefined;
|
|
@@ -8750,9 +8707,7 @@
|
|
|
8750
8707
|
}
|
|
8751
8708
|
this.attemptToResolveRelativeTarget = !hasOnlyRelativeTargetChanged;
|
|
8752
8709
|
const relativeLayout = createBox();
|
|
8753
|
-
const
|
|
8754
|
-
const layoutSource = this.layout ? this.layout.source : undefined;
|
|
8755
|
-
const isSharedLayoutAnimation = snapshotSource !== layoutSource;
|
|
8710
|
+
const isSharedLayoutAnimation = snapshot?.source !== this.layout?.source;
|
|
8756
8711
|
const stack = this.getStack();
|
|
8757
8712
|
const isOnlyMember = !stack || stack.members.length <= 1;
|
|
8758
8713
|
const shouldCrossfadeOpacity = Boolean(isSharedLayoutAnimation &&
|
|
@@ -8767,19 +8722,11 @@
|
|
|
8767
8722
|
this.mixTargetDelta = (latest) => {
|
|
8768
8723
|
const progress = latest / 1000;
|
|
8769
8724
|
const point = interpolate?.(progress);
|
|
8725
|
+
mixAxisDeltaLinear(targetDelta.x, delta.x, progress);
|
|
8726
|
+
mixAxisDeltaLinear(targetDelta.y, delta.y, progress);
|
|
8770
8727
|
if (point) {
|
|
8771
8728
|
targetDelta.x.translate = point.x;
|
|
8772
|
-
targetDelta.x.scale = mixNumber$1(delta.x.scale, 1, progress);
|
|
8773
|
-
targetDelta.x.origin = delta.x.origin;
|
|
8774
|
-
targetDelta.x.originPoint = delta.x.originPoint;
|
|
8775
8729
|
targetDelta.y.translate = point.y;
|
|
8776
|
-
targetDelta.y.scale = mixNumber$1(delta.y.scale, 1, progress);
|
|
8777
|
-
targetDelta.y.origin = delta.y.origin;
|
|
8778
|
-
targetDelta.y.originPoint = delta.y.originPoint;
|
|
8779
|
-
}
|
|
8780
|
-
else {
|
|
8781
|
-
mixAxisDeltaLinear(targetDelta.x, delta.x, progress);
|
|
8782
|
-
mixAxisDeltaLinear(targetDelta.y, delta.y, progress);
|
|
8783
8730
|
}
|
|
8784
8731
|
this.setTargetDelta(targetDelta);
|
|
8785
8732
|
if (this.relativeTarget &&
|
|
@@ -8805,7 +8752,7 @@
|
|
|
8805
8752
|
this.animationValues = mixedValues;
|
|
8806
8753
|
mixValues(mixedValues, snapshotLatestValues, this.latestValues, progress, shouldCrossfadeOpacity, isOnlyMember);
|
|
8807
8754
|
}
|
|
8808
|
-
if (point
|
|
8755
|
+
if (point?.rotate !== undefined) {
|
|
8809
8756
|
// Dedicated `pathRotation` channel, not `rotate`, so an
|
|
8810
8757
|
// animating `rotate` is composed with, never clobbered.
|
|
8811
8758
|
if (!this.animationValues)
|
|
@@ -8870,7 +8817,7 @@
|
|
|
8870
8817
|
}
|
|
8871
8818
|
finishAnimation() {
|
|
8872
8819
|
if (this.currentAnimation) {
|
|
8873
|
-
this.mixTargetDelta
|
|
8820
|
+
this.mixTargetDelta?.(animationTarget);
|
|
8874
8821
|
this.currentAnimation.stop();
|
|
8875
8822
|
}
|
|
8876
8823
|
this.completeAnimation();
|
|
@@ -8890,13 +8837,11 @@
|
|
|
8890
8837
|
this.layout &&
|
|
8891
8838
|
layout &&
|
|
8892
8839
|
shouldAnimatePositionOnly(this.options.animationType, this.layout.layoutBox, layout.layoutBox)) {
|
|
8840
|
+
const { layoutBox } = this.layout;
|
|
8841
|
+
const leadTarget = target;
|
|
8893
8842
|
target = this.target || createBox();
|
|
8894
|
-
|
|
8895
|
-
target.
|
|
8896
|
-
target.x.max = target.x.min + xLength;
|
|
8897
|
-
const yLength = calcLength(this.layout.layoutBox.y);
|
|
8898
|
-
target.y.min = lead.target.y.min;
|
|
8899
|
-
target.y.max = target.y.min + yLength;
|
|
8843
|
+
alignAxis(target.x, leadTarget.x, layoutBox.x);
|
|
8844
|
+
alignAxis(target.y, leadTarget.y, layoutBox.y);
|
|
8900
8845
|
}
|
|
8901
8846
|
copyBoxInto(targetWithTransforms, target);
|
|
8902
8847
|
/**
|
|
@@ -8921,8 +8866,8 @@
|
|
|
8921
8866
|
stack.add(node);
|
|
8922
8867
|
const config = node.options.initialPromotionConfig;
|
|
8923
8868
|
node.promote({
|
|
8924
|
-
transition: config
|
|
8925
|
-
preserveFollowOpacity: config
|
|
8869
|
+
transition: config?.transition,
|
|
8870
|
+
preserveFollowOpacity: config?.shouldPreserveFollowOpacity
|
|
8926
8871
|
? config.shouldPreserveFollowOpacity(node)
|
|
8927
8872
|
: undefined,
|
|
8928
8873
|
});
|
|
@@ -8945,9 +8890,7 @@
|
|
|
8945
8890
|
return this.root.sharedNodes.get(layoutId);
|
|
8946
8891
|
}
|
|
8947
8892
|
promote({ needsReset, transition, preserveFollowOpacity, } = {}) {
|
|
8948
|
-
|
|
8949
|
-
if (stack)
|
|
8950
|
-
stack.promote(this, preserveFollowOpacity);
|
|
8893
|
+
this.getStack()?.promote(this, preserveFollowOpacity);
|
|
8951
8894
|
if (needsReset) {
|
|
8952
8895
|
this.projectionDelta = undefined;
|
|
8953
8896
|
this.needsReset = true;
|
|
@@ -8956,45 +8899,27 @@
|
|
|
8956
8899
|
this.setOptions({ transition });
|
|
8957
8900
|
}
|
|
8958
8901
|
relegate() {
|
|
8959
|
-
|
|
8960
|
-
if (stack) {
|
|
8961
|
-
return stack.relegate(this);
|
|
8962
|
-
}
|
|
8963
|
-
else {
|
|
8964
|
-
return false;
|
|
8965
|
-
}
|
|
8902
|
+
return this.getStack()?.relegate(this) || false;
|
|
8966
8903
|
}
|
|
8967
8904
|
resetSkewAndRotation() {
|
|
8968
8905
|
const { visualElement } = this.options;
|
|
8969
8906
|
if (!visualElement)
|
|
8970
8907
|
return;
|
|
8971
|
-
// If there's no detected skew or rotation values, we can early return without a forced render.
|
|
8972
|
-
let hasDistortingTransform = false;
|
|
8973
8908
|
/**
|
|
8974
8909
|
* An unrolled check for rotation values. Most elements don't have any rotation and
|
|
8975
|
-
* skipping the
|
|
8910
|
+
* skipping the loop and new object creation is 50% faster.
|
|
8911
|
+
* If there's no distorting values, we don't need to do any more.
|
|
8976
8912
|
*/
|
|
8977
8913
|
const { latestValues } = visualElement;
|
|
8978
|
-
if (latestValues
|
|
8979
|
-
latestValues.rotate ||
|
|
8980
|
-
latestValues.rotateX ||
|
|
8981
|
-
latestValues.rotateY ||
|
|
8982
|
-
latestValues.rotateZ ||
|
|
8983
|
-
latestValues.skewX ||
|
|
8984
|
-
latestValues.skewY) {
|
|
8985
|
-
hasDistortingTransform = true;
|
|
8986
|
-
}
|
|
8987
|
-
// If there's no distorting values, we don't need to do any more.
|
|
8988
|
-
if (!hasDistortingTransform)
|
|
8914
|
+
if (!hasDistortingTransform(latestValues))
|
|
8989
8915
|
return;
|
|
8990
8916
|
const resetValues = {};
|
|
8991
|
-
if (latestValues.z) {
|
|
8992
|
-
resetDistortingTransform("z", visualElement, resetValues, this.animationValues);
|
|
8993
|
-
}
|
|
8994
8917
|
// Check the skew and rotate value of all axes and reset to 0
|
|
8995
|
-
for (let i = 0; i <
|
|
8996
|
-
resetDistortingTransform(
|
|
8997
|
-
|
|
8918
|
+
for (let i = 0; i < distortingTransforms.length; i++) {
|
|
8919
|
+
resetDistortingTransform(distortingTransforms[i], visualElement, resetValues, this.animationValues);
|
|
8920
|
+
}
|
|
8921
|
+
for (let i = 0; i < angleTransforms.length; i++) {
|
|
8922
|
+
resetDistortingTransform(angleTransforms[i], visualElement, resetValues, this.animationValues);
|
|
8998
8923
|
}
|
|
8999
8924
|
// Force a render of this element to apply the transform with all skews and rotations
|
|
9000
8925
|
// set to 0.
|
|
@@ -9014,7 +8939,13 @@
|
|
|
9014
8939
|
styleProp) {
|
|
9015
8940
|
if (!this.instance || this.isSVG)
|
|
9016
8941
|
return;
|
|
9017
|
-
|
|
8942
|
+
const lead = this.getLead();
|
|
8943
|
+
/**
|
|
8944
|
+
* Without crossfade, a new lead hides the previous one, as it's
|
|
8945
|
+
* usually unmounting. One that gave its layoutId away keeps
|
|
8946
|
+
* rendering, so only hide nodes that follow another.
|
|
8947
|
+
*/
|
|
8948
|
+
if (!this.isVisible && lead !== this) {
|
|
9018
8949
|
targetStyle.visibility = "hidden";
|
|
9019
8950
|
return;
|
|
9020
8951
|
}
|
|
@@ -9030,9 +8961,10 @@
|
|
|
9030
8961
|
: "none";
|
|
9031
8962
|
return;
|
|
9032
8963
|
}
|
|
9033
|
-
const lead = this.getLead();
|
|
9034
8964
|
if (!this.projectionDelta || !this.layout || !lead.target) {
|
|
9035
|
-
if (this.options.layoutId) {
|
|
8965
|
+
if (this.options.layoutId || this.needsShow) {
|
|
8966
|
+
this.needsShow = false;
|
|
8967
|
+
targetStyle.visibility = "";
|
|
9036
8968
|
targetStyle.opacity =
|
|
9037
8969
|
this.latestValues.opacity !== undefined
|
|
9038
8970
|
? this.latestValues.opacity
|
|
@@ -9166,52 +9098,49 @@
|
|
|
9166
9098
|
const isShared = snapshot.source !== node.layout.source;
|
|
9167
9099
|
// TODO Maybe we want to also resize the layout snapshot so we don't trigger
|
|
9168
9100
|
// animations for instance if layout="size" and an element has only changed position
|
|
9169
|
-
|
|
9170
|
-
|
|
9171
|
-
|
|
9172
|
-
|
|
9173
|
-
|
|
9174
|
-
|
|
9175
|
-
|
|
9176
|
-
|
|
9177
|
-
|
|
9178
|
-
|
|
9179
|
-
|
|
9180
|
-
|
|
9181
|
-
|
|
9182
|
-
|
|
9183
|
-
|
|
9184
|
-
|
|
9185
|
-
|
|
9186
|
-
|
|
9187
|
-
|
|
9188
|
-
|
|
9189
|
-
|
|
9190
|
-
|
|
9191
|
-
|
|
9192
|
-
|
|
9193
|
-
|
|
9194
|
-
|
|
9195
|
-
|
|
9196
|
-
|
|
9197
|
-
|
|
9198
|
-
node.relativeTarget[axis].min + length;
|
|
9199
|
-
}
|
|
9200
|
-
});
|
|
9201
|
-
}
|
|
9101
|
+
const resizeSnapshot = (box, to) => {
|
|
9102
|
+
if (animationType === "size") {
|
|
9103
|
+
eachAxis((axis) => {
|
|
9104
|
+
const length = calcLength(box[axis]);
|
|
9105
|
+
box[axis].min = to[axis].min;
|
|
9106
|
+
box[axis].max = box[axis].min + length;
|
|
9107
|
+
});
|
|
9108
|
+
}
|
|
9109
|
+
else if (animationType === "x" || animationType === "y") {
|
|
9110
|
+
const snapAxis = animationType === "x" ? "y" : "x";
|
|
9111
|
+
copyAxisInto(box[snapAxis], to[snapAxis]);
|
|
9112
|
+
}
|
|
9113
|
+
else if (shouldAnimatePositionOnly(animationType, snapshot.layoutBox, layout)) {
|
|
9114
|
+
eachAxis((axis) => {
|
|
9115
|
+
const length = calcLength(to[axis]);
|
|
9116
|
+
box[axis].max = box[axis].min + length;
|
|
9117
|
+
/**
|
|
9118
|
+
* Ensure relative target gets resized and rerendererd
|
|
9119
|
+
*/
|
|
9120
|
+
if (node.relativeTarget && !node.currentAnimation) {
|
|
9121
|
+
node.isProjectionDirty = true;
|
|
9122
|
+
node.relativeTarget[axis].max =
|
|
9123
|
+
node.relativeTarget[axis].min + length;
|
|
9124
|
+
}
|
|
9125
|
+
});
|
|
9126
|
+
}
|
|
9127
|
+
};
|
|
9128
|
+
const snapshotBox = isShared ? snapshot.measuredBox : snapshot.layoutBox;
|
|
9129
|
+
resizeSnapshot(snapshotBox, layout);
|
|
9202
9130
|
const layoutDelta = createDelta();
|
|
9203
9131
|
calcBoxDelta(layoutDelta, layout, snapshot.layoutBox);
|
|
9204
9132
|
const visualDelta = createDelta();
|
|
9205
|
-
|
|
9206
|
-
calcBoxDelta(visualDelta, node.applyTransform(measuredLayout, true), snapshot.measuredBox);
|
|
9207
|
-
}
|
|
9208
|
-
else {
|
|
9209
|
-
calcBoxDelta(visualDelta, layout, snapshot.layoutBox);
|
|
9210
|
-
}
|
|
9133
|
+
calcBoxDelta(visualDelta, isShared ? node.applyTransform(measuredLayout, true) : layout, snapshotBox);
|
|
9211
9134
|
const hasLayoutChanged = !isDeltaZero(layoutDelta);
|
|
9212
9135
|
let hasRelativeLayoutChanged = false;
|
|
9213
|
-
|
|
9214
|
-
|
|
9136
|
+
const relativeParent = node.getClosestProjectingParent();
|
|
9137
|
+
/**
|
|
9138
|
+
* A snapshot resumed from another element is only relative to this
|
|
9139
|
+
* node's relativeParent if it's a layoutRoot that contains both.
|
|
9140
|
+
*/
|
|
9141
|
+
if (!node.resumeFrom ||
|
|
9142
|
+
(relativeParent?.options.layoutRoot &&
|
|
9143
|
+
node.resumeFrom.path.includes(relativeParent))) {
|
|
9215
9144
|
/**
|
|
9216
9145
|
* If the relativeParent is itself resuming from a different element then
|
|
9217
9146
|
* the relative snapshot is not relavent
|
|
@@ -9220,10 +9149,22 @@
|
|
|
9220
9149
|
const { snapshot: parentSnapshot, layout: parentLayout } = relativeParent;
|
|
9221
9150
|
if (parentSnapshot && parentLayout) {
|
|
9222
9151
|
const anchor = node.options.layoutAnchor || undefined;
|
|
9152
|
+
/**
|
|
9153
|
+
* A shared snapshot includes the transforms of the old
|
|
9154
|
+
* element's ancestors below the layoutRoot, as they
|
|
9155
|
+
* aren't shared with the new element.
|
|
9156
|
+
*/
|
|
9223
9157
|
const relativeSnapshot = createBox();
|
|
9224
|
-
calcRelativePosition(relativeSnapshot,
|
|
9158
|
+
calcRelativePosition(relativeSnapshot, isShared
|
|
9159
|
+
? node.resumeFrom.applyTransform(snapshot.layoutBox, true, undefined, true, relativeParent.depth + 1)
|
|
9160
|
+
: snapshot.layoutBox, parentSnapshot.layoutBox, anchor);
|
|
9225
9161
|
const relativeLayout = createBox();
|
|
9226
9162
|
calcRelativePosition(relativeLayout, layout, parentLayout.layoutBox, anchor);
|
|
9163
|
+
/**
|
|
9164
|
+
* A shared snapshot is resized in page space, above, so
|
|
9165
|
+
* resize it relative to the layoutRoot too.
|
|
9166
|
+
*/
|
|
9167
|
+
isShared && resizeSnapshot(relativeSnapshot, relativeLayout);
|
|
9227
9168
|
if (!boxEqualsRounded(relativeSnapshot, relativeLayout)) {
|
|
9228
9169
|
hasRelativeLayoutChanged = true;
|
|
9229
9170
|
}
|
|
@@ -9245,8 +9186,7 @@
|
|
|
9245
9186
|
});
|
|
9246
9187
|
}
|
|
9247
9188
|
else if (node.isLead()) {
|
|
9248
|
-
|
|
9249
|
-
onExitComplete && onExitComplete();
|
|
9189
|
+
node.options.onExitComplete?.();
|
|
9250
9190
|
}
|
|
9251
9191
|
/**
|
|
9252
9192
|
* Clearing transition
|
|
@@ -9352,7 +9292,7 @@
|
|
|
9352
9292
|
}
|
|
9353
9293
|
function resetTransformStyle(node) {
|
|
9354
9294
|
const { visualElement } = node.options;
|
|
9355
|
-
if (visualElement
|
|
9295
|
+
if (visualElement?.getProps().onBeforeLayoutMeasure) {
|
|
9356
9296
|
visualElement.notify("BeforeLayoutMeasure");
|
|
9357
9297
|
}
|
|
9358
9298
|
node.resetTransform();
|
|
@@ -9386,7 +9326,7 @@
|
|
|
9386
9326
|
mixAxis(output.y, from.y, to.y, p);
|
|
9387
9327
|
}
|
|
9388
9328
|
function hasOpacityCrossfade(node) {
|
|
9389
|
-
return
|
|
9329
|
+
return node.animationValues?.opacityExit !== undefined;
|
|
9390
9330
|
}
|
|
9391
9331
|
const defaultLayoutTransition = {
|
|
9392
9332
|
duration: 0.45,
|
|
@@ -9412,6 +9352,13 @@
|
|
|
9412
9352
|
roundAxis(box.x);
|
|
9413
9353
|
roundAxis(box.y);
|
|
9414
9354
|
}
|
|
9355
|
+
/**
|
|
9356
|
+
* Align an axis to the start of another, keeping the length of a layout axis.
|
|
9357
|
+
* The length is read first, as output can be the layout axis itself.
|
|
9358
|
+
*/
|
|
9359
|
+
function alignAxis(output, from, layout) {
|
|
9360
|
+
output.max = calcLength(layout) + (output.min = from.min);
|
|
9361
|
+
}
|
|
9415
9362
|
function shouldAnimatePositionOnly(animationType, snapshot, layout) {
|
|
9416
9363
|
return (animationType === "position" ||
|
|
9417
9364
|
(animationType === "preserve-aspect" &&
|
|
@@ -9462,7 +9409,7 @@
|
|
|
9462
9409
|
resetTransform: (instance, value) => {
|
|
9463
9410
|
instance.style.transform = value !== undefined ? value : "none";
|
|
9464
9411
|
},
|
|
9465
|
-
checkIsScrollRoot: (instance) =>
|
|
9412
|
+
checkIsScrollRoot: (instance) => window.getComputedStyle(instance).position === "fixed",
|
|
9466
9413
|
});
|
|
9467
9414
|
|
|
9468
9415
|
const visualElementStore = new WeakMap();
|
|
@@ -9748,9 +9695,7 @@
|
|
|
9748
9695
|
}
|
|
9749
9696
|
this.current = instance;
|
|
9750
9697
|
visualElementStore.set(instance, this);
|
|
9751
|
-
|
|
9752
|
-
this.projection.mount(instance);
|
|
9753
|
-
}
|
|
9698
|
+
this.projection?.instance || this.projection?.mount(instance);
|
|
9754
9699
|
/**
|
|
9755
9700
|
* Join the closest variant node above, so its variant changes
|
|
9756
9701
|
* propagate here, unless inherit={false} cuts the chain.
|
|
@@ -9796,12 +9741,12 @@
|
|
|
9796
9741
|
this.hasBeenMounted = true;
|
|
9797
9742
|
}
|
|
9798
9743
|
unmount() {
|
|
9799
|
-
this.projection
|
|
9744
|
+
this.projection?.unmount();
|
|
9800
9745
|
cancelFrame(this.notifyUpdate);
|
|
9801
9746
|
cancelFrame(this.render);
|
|
9802
9747
|
this.valueSubscriptions.forEach((remove) => remove());
|
|
9803
9748
|
this.valueSubscriptions.clear();
|
|
9804
|
-
this.removeFromVariantTree
|
|
9749
|
+
this.removeFromVariantTree?.();
|
|
9805
9750
|
this.parent?.removeChild(this);
|
|
9806
9751
|
for (const key in this.events) {
|
|
9807
9752
|
this.events[key].clear();
|
|
@@ -9817,17 +9762,14 @@
|
|
|
9817
9762
|
}
|
|
9818
9763
|
addChild(child) {
|
|
9819
9764
|
this.children.add(child);
|
|
9820
|
-
this.enteringChildren
|
|
9821
|
-
this.enteringChildren.add(child);
|
|
9765
|
+
(this.enteringChildren || (this.enteringChildren = new Set())).add(child);
|
|
9822
9766
|
}
|
|
9823
9767
|
removeChild(child) {
|
|
9824
9768
|
this.children.delete(child);
|
|
9825
|
-
this.enteringChildren
|
|
9769
|
+
this.enteringChildren?.delete(child);
|
|
9826
9770
|
}
|
|
9827
9771
|
bindToMotionValue(key, value) {
|
|
9828
|
-
|
|
9829
|
-
this.valueSubscriptions.get(key)();
|
|
9830
|
-
}
|
|
9772
|
+
this.valueSubscriptions.get(key)?.();
|
|
9831
9773
|
if (value.accelerate &&
|
|
9832
9774
|
acceleratedValues.has(key) &&
|
|
9833
9775
|
this.current instanceof HTMLElement) {
|
|
@@ -9869,8 +9811,7 @@
|
|
|
9869
9811
|
}
|
|
9870
9812
|
this.valueSubscriptions.set(key, () => {
|
|
9871
9813
|
removeOnChange();
|
|
9872
|
-
|
|
9873
|
-
removeSyncCheck();
|
|
9814
|
+
removeSyncCheck?.();
|
|
9874
9815
|
// Defer to MotionValue.on("change") auto-stop so React 19 remounts
|
|
9875
9816
|
// can resubscribe before the animation is cancelled (#3315).
|
|
9876
9817
|
});
|
|
@@ -9963,10 +9904,8 @@
|
|
|
9963
9904
|
*/
|
|
9964
9905
|
for (let i = 0; i < propEventHandlers.length; i++) {
|
|
9965
9906
|
const key = propEventHandlers[i];
|
|
9966
|
-
|
|
9967
|
-
|
|
9968
|
-
delete this.propEventSubscriptions[key];
|
|
9969
|
-
}
|
|
9907
|
+
this.propEventSubscriptions[key]?.();
|
|
9908
|
+
delete this.propEventSubscriptions[key];
|
|
9970
9909
|
const listenerName = ("on" + key);
|
|
9971
9910
|
const listener = props[listenerName];
|
|
9972
9911
|
if (listener) {
|
|
@@ -9974,9 +9913,7 @@
|
|
|
9974
9913
|
}
|
|
9975
9914
|
}
|
|
9976
9915
|
this.prevMotionValues = updateMotionValuesFromProps(this, this.scrapeMotionValuesFromProps(props, this.prevProps || {}, this), this.prevMotionValues);
|
|
9977
|
-
|
|
9978
|
-
this.handleChildMotionValue();
|
|
9979
|
-
}
|
|
9916
|
+
this.handleChildMotionValue?.();
|
|
9980
9917
|
}
|
|
9981
9918
|
getProps() {
|
|
9982
9919
|
return this.props;
|
|
@@ -9985,7 +9922,7 @@
|
|
|
9985
9922
|
* Returns the variant definition with a given name.
|
|
9986
9923
|
*/
|
|
9987
9924
|
getVariant(name) {
|
|
9988
|
-
return this.props.variants
|
|
9925
|
+
return this.props.variants?.[name];
|
|
9989
9926
|
}
|
|
9990
9927
|
/**
|
|
9991
9928
|
* Returns the defined default transition on this component.
|
|
@@ -10023,11 +9960,8 @@
|
|
|
10023
9960
|
*/
|
|
10024
9961
|
removeValue(key) {
|
|
10025
9962
|
this.values.delete(key);
|
|
10026
|
-
|
|
10027
|
-
|
|
10028
|
-
unsubscribe();
|
|
10029
|
-
this.valueSubscriptions.delete(key);
|
|
10030
|
-
}
|
|
9963
|
+
this.valueSubscriptions.get(key)?.();
|
|
9964
|
+
this.valueSubscriptions.delete(key);
|
|
10031
9965
|
delete this.latestValues[key];
|
|
10032
9966
|
this.removeValueFromRenderState(key, this.renderState);
|
|
10033
9967
|
}
|
|
@@ -10038,10 +9972,10 @@
|
|
|
10038
9972
|
return this.values.has(key);
|
|
10039
9973
|
}
|
|
10040
9974
|
getValue(key, defaultValue) {
|
|
10041
|
-
|
|
10042
|
-
|
|
10043
|
-
|
|
10044
|
-
|
|
9975
|
+
let value = this.props.values?.[key];
|
|
9976
|
+
if (value)
|
|
9977
|
+
return value;
|
|
9978
|
+
value = this.values.get(key);
|
|
10045
9979
|
if (value === undefined && defaultValue !== undefined) {
|
|
10046
9980
|
value = motionValue(defaultValue ?? this.getDefaultValue?.(key), {
|
|
10047
9981
|
owner: this,
|
|
@@ -10091,15 +10025,11 @@
|
|
|
10091
10025
|
this.baseTarget[key] = value;
|
|
10092
10026
|
}
|
|
10093
10027
|
on(eventName, callback) {
|
|
10094
|
-
|
|
10095
|
-
this.events[eventName] = new SubscriptionManager();
|
|
10096
|
-
}
|
|
10097
|
-
return this.events[eventName].add(callback);
|
|
10028
|
+
return (this.events[eventName] ||
|
|
10029
|
+
(this.events[eventName] = new SubscriptionManager())).add(callback);
|
|
10098
10030
|
}
|
|
10099
10031
|
notify(eventName, ...args) {
|
|
10100
|
-
|
|
10101
|
-
this.events[eventName].notify(...args);
|
|
10102
|
-
}
|
|
10032
|
+
this.events[eventName]?.notify(...args);
|
|
10103
10033
|
}
|
|
10104
10034
|
scheduleRenderMicrotask() {
|
|
10105
10035
|
microtask.render(this.render);
|
|
@@ -10272,7 +10202,7 @@
|
|
|
10272
10202
|
let ancestor = element.parentElement;
|
|
10273
10203
|
while (ancestor) {
|
|
10274
10204
|
const node = layoutNodes.get(ancestor);
|
|
10275
|
-
if (node
|
|
10205
|
+
if (node?.instance)
|
|
10276
10206
|
return node;
|
|
10277
10207
|
ancestor = ancestor.parentElement;
|
|
10278
10208
|
}
|
|
@@ -10538,7 +10468,7 @@
|
|
|
10538
10468
|
hasSurvivor &&
|
|
10539
10469
|
!newMemberIds.has(layoutId)) {
|
|
10540
10470
|
if (restore && restore.parent.isConnected) {
|
|
10541
|
-
restore.parent.insertBefore(element, restore.next
|
|
10471
|
+
restore.parent.insertBefore(element, restore.next?.parentNode === restore.parent
|
|
10542
10472
|
? restore.next
|
|
10543
10473
|
: null);
|
|
10544
10474
|
node.isPresent = false;
|
|
@@ -10743,10 +10673,6 @@
|
|
|
10743
10673
|
* Scroll positions of scrollable ancestors and window.
|
|
10744
10674
|
*/
|
|
10745
10675
|
this.scrollPositions = new Map();
|
|
10746
|
-
/**
|
|
10747
|
-
* Cleanup function for scroll listeners.
|
|
10748
|
-
*/
|
|
10749
|
-
this.removeScrollListeners = null;
|
|
10750
10676
|
this.onElementScroll = (event) => {
|
|
10751
10677
|
this.handleScroll(event.target);
|
|
10752
10678
|
};
|
|
@@ -10774,10 +10700,10 @@
|
|
|
10774
10700
|
this.history.push({ ...point, timestamp: time.now() });
|
|
10775
10701
|
const { onStart, onMove } = this.handlers;
|
|
10776
10702
|
if (!isPanStarted) {
|
|
10777
|
-
onStart
|
|
10703
|
+
onStart?.(this.lastMoveEvent, info);
|
|
10778
10704
|
this.startEvent = this.lastMoveEvent;
|
|
10779
10705
|
}
|
|
10780
|
-
onMove
|
|
10706
|
+
onMove?.(this.lastMoveEvent, info);
|
|
10781
10707
|
};
|
|
10782
10708
|
this.handlePointerMove = (event, info) => {
|
|
10783
10709
|
/**
|
|
@@ -10802,17 +10728,15 @@
|
|
|
10802
10728
|
// Resume animation if dragSnapToOrigin is set OR if no drag started (user just clicked)
|
|
10803
10729
|
// This ensures constraint animations continue when interrupted by a click
|
|
10804
10730
|
if (this.dragSnapToOrigin || !this.startEvent) {
|
|
10805
|
-
resumeAnimation
|
|
10731
|
+
resumeAnimation?.();
|
|
10806
10732
|
}
|
|
10807
10733
|
if (!(this.lastMoveEvent && this.lastMoveEventInfo))
|
|
10808
10734
|
return;
|
|
10809
10735
|
const panInfo = getPanInfo(event.type === "pointercancel"
|
|
10810
10736
|
? this.lastMoveEventInfo
|
|
10811
10737
|
: transformPoint(info, this.transformPagePoint), this.history);
|
|
10812
|
-
|
|
10813
|
-
|
|
10814
|
-
}
|
|
10815
|
-
onSessionEnd && onSessionEnd(event, panInfo);
|
|
10738
|
+
this.startEvent && onEnd?.(event, panInfo);
|
|
10739
|
+
onSessionEnd?.(event, panInfo);
|
|
10816
10740
|
};
|
|
10817
10741
|
// If we have more than one touch, don't start detecting this gesture
|
|
10818
10742
|
if (!isPrimaryPointer(event))
|
|
@@ -10825,8 +10749,13 @@
|
|
|
10825
10749
|
const info = extractEventInfo(event);
|
|
10826
10750
|
const initialInfo = transformPoint(info, this.transformPagePoint);
|
|
10827
10751
|
const { point } = initialInfo;
|
|
10828
|
-
|
|
10829
|
-
|
|
10752
|
+
/**
|
|
10753
|
+
* Stamp the origin with time.now(), not frameData.timestamp: when
|
|
10754
|
+
* the frameloop is idle, frameData.timestamp is from the last frame
|
|
10755
|
+
* that ran, which can be seconds old, and a fast flick from a
|
|
10756
|
+
* standstill would then report near-zero velocity. See #1747.
|
|
10757
|
+
*/
|
|
10758
|
+
this.history = [{ ...point, timestamp: time.now() }];
|
|
10830
10759
|
const { onSessionStart } = handlers;
|
|
10831
10760
|
onSessionStart &&
|
|
10832
10761
|
onSessionStart(event, getPanInfo(initialInfo, this.history));
|
|
@@ -10850,30 +10779,19 @@
|
|
|
10850
10779
|
const style = getComputedStyle(current);
|
|
10851
10780
|
if (overflowStyles.has(style.overflowX) ||
|
|
10852
10781
|
overflowStyles.has(style.overflowY)) {
|
|
10853
|
-
this.scrollPositions.set(current,
|
|
10854
|
-
x: current.scrollLeft,
|
|
10855
|
-
y: current.scrollTop,
|
|
10856
|
-
});
|
|
10782
|
+
this.scrollPositions.set(current, getScroll(current));
|
|
10857
10783
|
}
|
|
10858
10784
|
current = current.parentElement;
|
|
10859
10785
|
}
|
|
10860
10786
|
// Track window scroll
|
|
10861
|
-
this.scrollPositions.set(window,
|
|
10862
|
-
|
|
10863
|
-
y: window.scrollY,
|
|
10864
|
-
});
|
|
10787
|
+
this.scrollPositions.set(window, getScroll(window));
|
|
10788
|
+
this.removeScrollListeners = pipe(
|
|
10865
10789
|
// Capture listener catches element scroll events as they bubble
|
|
10866
|
-
window
|
|
10790
|
+
addDomEvent(window, "scroll", this.onElementScroll, {
|
|
10867
10791
|
capture: true,
|
|
10868
|
-
})
|
|
10792
|
+
}),
|
|
10869
10793
|
// Direct window scroll listener (window scroll doesn't bubble)
|
|
10870
|
-
window
|
|
10871
|
-
this.removeScrollListeners = () => {
|
|
10872
|
-
window.removeEventListener("scroll", this.onElementScroll, {
|
|
10873
|
-
capture: true,
|
|
10874
|
-
});
|
|
10875
|
-
window.removeEventListener("scroll", this.onWindowScroll);
|
|
10876
|
-
};
|
|
10794
|
+
addDomEvent(window, "scroll", this.onWindowScroll, {}));
|
|
10877
10795
|
}
|
|
10878
10796
|
/**
|
|
10879
10797
|
* Handle scroll compensation during drag.
|
|
@@ -10886,12 +10804,7 @@
|
|
|
10886
10804
|
if (!initial)
|
|
10887
10805
|
return;
|
|
10888
10806
|
const isWindow = target === window;
|
|
10889
|
-
const current =
|
|
10890
|
-
? { x: window.scrollX, y: window.scrollY }
|
|
10891
|
-
: {
|
|
10892
|
-
x: target.scrollLeft,
|
|
10893
|
-
y: target.scrollTop,
|
|
10894
|
-
};
|
|
10807
|
+
const current = getScroll(target);
|
|
10895
10808
|
const delta = { x: current.x - initial.x, y: current.y - initial.y };
|
|
10896
10809
|
if (delta.x === 0 && delta.y === 0)
|
|
10897
10810
|
return;
|
|
@@ -10916,12 +10829,18 @@
|
|
|
10916
10829
|
this.handlers = handlers;
|
|
10917
10830
|
}
|
|
10918
10831
|
end() {
|
|
10919
|
-
this.removeListeners
|
|
10920
|
-
this.removeScrollListeners
|
|
10832
|
+
this.removeListeners?.();
|
|
10833
|
+
this.removeScrollListeners?.();
|
|
10921
10834
|
this.scrollPositions.clear();
|
|
10922
10835
|
cancelFrame(this.updatePoint);
|
|
10923
10836
|
}
|
|
10924
10837
|
}
|
|
10838
|
+
const getScroll = (target) => target === window
|
|
10839
|
+
? { x: window.scrollX, y: window.scrollY }
|
|
10840
|
+
: {
|
|
10841
|
+
x: target.scrollLeft,
|
|
10842
|
+
y: target.scrollTop,
|
|
10843
|
+
};
|
|
10925
10844
|
function transformPoint(info, transformPagePoint) {
|
|
10926
10845
|
return transformPagePoint ? { point: transformPagePoint(info.point) } : info;
|
|
10927
10846
|
}
|
|
@@ -11178,6 +11097,25 @@
|
|
|
11178
11097
|
: dragElastic[label] || 0;
|
|
11179
11098
|
}
|
|
11180
11099
|
|
|
11100
|
+
/**
|
|
11101
|
+
* Re-read the scroll of the projection's ancestors, and return that of the
|
|
11102
|
+
* `layoutScroll` ones as a key that changes when any of them scroll.
|
|
11103
|
+
*
|
|
11104
|
+
* Clear the cached scroll first so `updateScroll` bypasses its
|
|
11105
|
+
* per-animationId cache and re-reads the live value.
|
|
11106
|
+
*/
|
|
11107
|
+
function measureScroll(projection) {
|
|
11108
|
+
let key = "";
|
|
11109
|
+
for (const node of projection.path) {
|
|
11110
|
+
node.scroll = undefined;
|
|
11111
|
+
node.updateScroll();
|
|
11112
|
+
const { scroll } = node;
|
|
11113
|
+
if (node !== projection.root && scroll) {
|
|
11114
|
+
key += scroll.offset.x + "," + scroll.offset.y + ",";
|
|
11115
|
+
}
|
|
11116
|
+
}
|
|
11117
|
+
return key;
|
|
11118
|
+
}
|
|
11181
11119
|
const elementDragControls = new WeakMap();
|
|
11182
11120
|
class VisualElementDragControls {
|
|
11183
11121
|
constructor(visualElement, readProps = () => visualElement.getProps()) {
|
|
@@ -11209,8 +11147,7 @@
|
|
|
11209
11147
|
/**
|
|
11210
11148
|
* Don't start dragging if this component is exiting
|
|
11211
11149
|
*/
|
|
11212
|
-
|
|
11213
|
-
if (presenceContext && presenceContext.isPresent === false)
|
|
11150
|
+
if (this.visualElement.presenceContext?.isPresent === false)
|
|
11214
11151
|
return;
|
|
11215
11152
|
const onSessionStart = (event) => {
|
|
11216
11153
|
if (snapToCursor)
|
|
@@ -11221,8 +11158,7 @@
|
|
|
11221
11158
|
// Attempt to grab the global drag gesture lock - maybe make this part of PanSession
|
|
11222
11159
|
const { drag, dragPropagation, onDragStart } = this.getProps();
|
|
11223
11160
|
if (drag && !dragPropagation) {
|
|
11224
|
-
|
|
11225
|
-
this.openDragLock();
|
|
11161
|
+
this.openDragLock?.();
|
|
11226
11162
|
this.openDragLock = setDragLock(drag);
|
|
11227
11163
|
// If we don 't have the lock, don't start dragging
|
|
11228
11164
|
if (!this.openDragLock)
|
|
@@ -11233,9 +11169,10 @@
|
|
|
11233
11169
|
this.isDragging = true;
|
|
11234
11170
|
this.currentDirection = null;
|
|
11235
11171
|
this.resolveConstraints();
|
|
11236
|
-
|
|
11237
|
-
|
|
11238
|
-
|
|
11172
|
+
const { projection } = this.visualElement;
|
|
11173
|
+
if (projection) {
|
|
11174
|
+
projection.isAnimationBlocked = true;
|
|
11175
|
+
projection.target = undefined;
|
|
11239
11176
|
}
|
|
11240
11177
|
/**
|
|
11241
11178
|
* Record gesture origin and pointer offset
|
|
@@ -11257,13 +11194,11 @@
|
|
|
11257
11194
|
current = parseFloat(current);
|
|
11258
11195
|
}
|
|
11259
11196
|
else if (percent.test(current)) {
|
|
11260
|
-
const
|
|
11261
|
-
if (
|
|
11262
|
-
|
|
11263
|
-
|
|
11264
|
-
|
|
11265
|
-
current = length * (parseFloat(current) / 100);
|
|
11266
|
-
}
|
|
11197
|
+
const measuredAxis = projection?.layout?.layoutBox[axis];
|
|
11198
|
+
if (measuredAxis) {
|
|
11199
|
+
current =
|
|
11200
|
+
calcLength(measuredAxis) *
|
|
11201
|
+
(parseFloat(current) / 100);
|
|
11267
11202
|
}
|
|
11268
11203
|
}
|
|
11269
11204
|
this.originPoint[axis] = current;
|
|
@@ -11273,8 +11208,7 @@
|
|
|
11273
11208
|
frame.update(() => onDragStart(event, info), false, true);
|
|
11274
11209
|
}
|
|
11275
11210
|
addValueToWillChange(this.visualElement, "transform");
|
|
11276
|
-
|
|
11277
|
-
animationState && animationState.setActive("whileDrag", true);
|
|
11211
|
+
this.visualElement.animationState?.setActive("whileDrag", true);
|
|
11278
11212
|
};
|
|
11279
11213
|
const onMove = (event, info) => {
|
|
11280
11214
|
this.latestPointerEvent = event;
|
|
@@ -11297,8 +11231,8 @@
|
|
|
11297
11231
|
onDirectionLock && onDirectionLock(this.currentDirection);
|
|
11298
11232
|
}
|
|
11299
11233
|
// Update each point with the latest position
|
|
11300
|
-
this.updateAxis("x",
|
|
11301
|
-
this.updateAxis("y",
|
|
11234
|
+
this.updateAxis("x", offset);
|
|
11235
|
+
this.updateAxis("y", offset);
|
|
11302
11236
|
/**
|
|
11303
11237
|
* Ideally we would leave the renderer to fire naturally at the end of
|
|
11304
11238
|
* this frame but if the element is about to change layout as the result
|
|
@@ -11367,12 +11301,11 @@
|
|
|
11367
11301
|
projection.isAnimationBlocked = false;
|
|
11368
11302
|
}
|
|
11369
11303
|
this.endPanSession();
|
|
11370
|
-
|
|
11371
|
-
if (!dragPropagation && this.openDragLock) {
|
|
11304
|
+
if (!this.getProps().dragPropagation && this.openDragLock) {
|
|
11372
11305
|
this.openDragLock();
|
|
11373
11306
|
this.openDragLock = null;
|
|
11374
11307
|
}
|
|
11375
|
-
animationState
|
|
11308
|
+
animationState?.setActive("whileDrag", false);
|
|
11376
11309
|
}
|
|
11377
11310
|
/**
|
|
11378
11311
|
* Clean up the pan session without modifying other drag state.
|
|
@@ -11380,31 +11313,42 @@
|
|
|
11380
11313
|
* without affecting projection animations or drag locks.
|
|
11381
11314
|
*/
|
|
11382
11315
|
endPanSession() {
|
|
11383
|
-
this.panSession
|
|
11316
|
+
this.panSession?.end();
|
|
11384
11317
|
this.panSession = undefined;
|
|
11385
11318
|
}
|
|
11386
|
-
updateAxis(axis,
|
|
11387
|
-
|
|
11319
|
+
updateAxis(axis, offset) {
|
|
11320
|
+
/**
|
|
11321
|
+
* Read drag per axis: a callback fired while updating the x axis
|
|
11322
|
+
* can synchronously re-render with new props.
|
|
11323
|
+
*/
|
|
11324
|
+
const { drag } = this.readProps();
|
|
11388
11325
|
// If we're not dragging this axis, do an early return.
|
|
11389
11326
|
if (!offset || !shouldDrag(axis, drag, this.currentDirection))
|
|
11390
11327
|
return;
|
|
11391
11328
|
const axisValue = this.getAxisMotionValue(axis);
|
|
11392
11329
|
let next = this.originPoint[axis] + offset[axis];
|
|
11393
11330
|
// Apply constraints
|
|
11394
|
-
|
|
11395
|
-
|
|
11331
|
+
const constraints = this.constraints && this.constraints[axis];
|
|
11332
|
+
if (constraints) {
|
|
11333
|
+
next = applyConstraints(next, constraints, this.elastic[axis]);
|
|
11396
11334
|
}
|
|
11397
11335
|
axisValue.set(next);
|
|
11398
11336
|
}
|
|
11399
11337
|
resolveConstraints() {
|
|
11400
11338
|
const { dragConstraints, dragElastic } = this.getProps();
|
|
11401
|
-
const
|
|
11402
|
-
|
|
11403
|
-
? this.visualElement.projection.measure(false)
|
|
11404
|
-
: this.visualElement.projection?.layout;
|
|
11339
|
+
const { projection } = this.visualElement;
|
|
11340
|
+
const layout = projection && (projection.layout || projection.measure(false));
|
|
11405
11341
|
const prevConstraints = this.constraints;
|
|
11406
11342
|
if (isElementConstraints(dragConstraints)) {
|
|
11407
|
-
|
|
11343
|
+
/**
|
|
11344
|
+
* Ref constraints are measured with the layout. Scrolling a
|
|
11345
|
+
* `layoutScroll` ancestor since can move the element relative
|
|
11346
|
+
* to them without a layout measurement, so measure them again
|
|
11347
|
+
* (#2024). Otherwise keep them: they and the layout can both
|
|
11348
|
+
* have moved on the page since, e.g. when content above grows.
|
|
11349
|
+
*/
|
|
11350
|
+
if (!this.constraints ||
|
|
11351
|
+
this.scroll !== measureScroll(projection)) {
|
|
11408
11352
|
this.constraints = this.resolveRefConstraints();
|
|
11409
11353
|
}
|
|
11410
11354
|
}
|
|
@@ -11447,21 +11391,20 @@
|
|
|
11447
11391
|
if (!projection || !projection.layout)
|
|
11448
11392
|
return false;
|
|
11449
11393
|
/**
|
|
11450
|
-
* Refresh the
|
|
11451
|
-
*
|
|
11452
|
-
*
|
|
11453
|
-
*
|
|
11454
|
-
*
|
|
11455
|
-
*
|
|
11456
|
-
* Clear the cached scroll first so `updateScroll` bypasses its
|
|
11457
|
-
* per-animationId cache and re-reads the live value.
|
|
11394
|
+
* Refresh the scroll offsets of the root and `layoutScroll`
|
|
11395
|
+
* ancestors so the constraint's viewport box translates to the
|
|
11396
|
+
* layout's coordinates. The scroll captured when the layout was
|
|
11397
|
+
* measured can be stale if they've scrolled since — e.g. via the
|
|
11398
|
+
* browser restoring scroll on refresh, or an ancestor layout effect
|
|
11399
|
+
* running after this element's mount (#2829).
|
|
11458
11400
|
*/
|
|
11459
|
-
|
|
11460
|
-
projection.root.scroll = undefined;
|
|
11461
|
-
projection.root.updateScroll();
|
|
11462
|
-
}
|
|
11401
|
+
this.scroll = measureScroll(projection);
|
|
11463
11402
|
const constraintsBox = measurePageBox(constraintsElement, projection.root, this.visualElement.getTransformPagePoint());
|
|
11464
|
-
|
|
11403
|
+
/**
|
|
11404
|
+
* layoutBox removes the scroll of `layoutScroll` ancestors, so
|
|
11405
|
+
* remove it from the constraints box too.
|
|
11406
|
+
*/
|
|
11407
|
+
let measuredConstraints = calcViewportConstraints(projection.layout.layoutBox, projection.removeElementScroll(constraintsBox));
|
|
11465
11408
|
/**
|
|
11466
11409
|
* If there's an onMeasureDragConstraints listener we call it and
|
|
11467
11410
|
* if different constraints are returned, set constraints to that
|
|
@@ -11482,7 +11425,7 @@
|
|
|
11482
11425
|
if (!shouldDrag(axis, dragAxes || drag, this.currentDirection)) {
|
|
11483
11426
|
return;
|
|
11484
11427
|
}
|
|
11485
|
-
let transition =
|
|
11428
|
+
let transition = constraints[axis] || {};
|
|
11486
11429
|
if (dragSnapToOrigin === true ||
|
|
11487
11430
|
dragSnapToOrigin === axis)
|
|
11488
11431
|
transition = { min: 0, max: 0 };
|
|
@@ -11588,7 +11531,7 @@
|
|
|
11588
11531
|
this.visualElement.current.style.transform = transformTemplate
|
|
11589
11532
|
? transformTemplate({}, "")
|
|
11590
11533
|
: "none";
|
|
11591
|
-
projection.root
|
|
11534
|
+
projection.root?.updateScroll();
|
|
11592
11535
|
projection.updateLayout();
|
|
11593
11536
|
/**
|
|
11594
11537
|
* Reset constraints so resolveConstraints() will recalculate them
|
|
@@ -11665,8 +11608,8 @@
|
|
|
11665
11608
|
};
|
|
11666
11609
|
const { projection } = this.visualElement;
|
|
11667
11610
|
const stopMeasureLayoutListener = projection.addEventListener("measure", measureDragConstraints);
|
|
11668
|
-
if (
|
|
11669
|
-
projection.root
|
|
11611
|
+
if (!projection.layout) {
|
|
11612
|
+
projection.root?.updateScroll();
|
|
11670
11613
|
projection.updateLayout();
|
|
11671
11614
|
}
|
|
11672
11615
|
frame.read(measureDragConstraints);
|
|
@@ -11701,22 +11644,14 @@
|
|
|
11701
11644
|
stopResizeListener();
|
|
11702
11645
|
stopPointerListener();
|
|
11703
11646
|
stopMeasureLayoutListener();
|
|
11704
|
-
stopLayoutUpdateListener
|
|
11705
|
-
stopResizeObservers
|
|
11647
|
+
stopLayoutUpdateListener();
|
|
11648
|
+
stopResizeObservers?.();
|
|
11706
11649
|
};
|
|
11707
11650
|
}
|
|
11708
11651
|
getProps() {
|
|
11709
11652
|
const props = this.readProps();
|
|
11710
|
-
const {
|
|
11711
|
-
return {
|
|
11712
|
-
...props,
|
|
11713
|
-
drag,
|
|
11714
|
-
dragDirectionLock,
|
|
11715
|
-
dragPropagation,
|
|
11716
|
-
dragConstraints,
|
|
11717
|
-
dragElastic,
|
|
11718
|
-
dragMomentum,
|
|
11719
|
-
};
|
|
11653
|
+
const { dragElastic = defaultElastic, dragMomentum = true } = props;
|
|
11654
|
+
return { ...props, dragElastic, dragMomentum };
|
|
11720
11655
|
}
|
|
11721
11656
|
}
|
|
11722
11657
|
/**
|
|
@@ -14051,6 +13986,12 @@
|
|
|
14051
13986
|
}
|
|
14052
13987
|
|
|
14053
13988
|
const reversePriorityOrder = [...variantPriorityOrder].reverse();
|
|
13989
|
+
/**
|
|
13990
|
+
* The transition a value uses when it leaves a state (#1725): the state's
|
|
13991
|
+
* own transition when `out: true`, or the `out` transition itself.
|
|
13992
|
+
* Value-specific keys are resolved by animateTarget.
|
|
13993
|
+
*/
|
|
13994
|
+
const getOutTransition = (transition) => transition?.out === true ? transition : transition?.out;
|
|
14054
13995
|
/**
|
|
14055
13996
|
* Diffs the element's animation props (one layer per AnimationType, highest
|
|
14056
13997
|
* priority wins each value) against the previous call and starts animations
|
|
@@ -14072,13 +14013,18 @@
|
|
|
14072
14013
|
let wasReset = false;
|
|
14073
14014
|
function animateChanges(changedActiveType) {
|
|
14074
14015
|
const { props, parent, manuallyAnimateOnMount } = visualElement;
|
|
14016
|
+
const { initial } = props;
|
|
14075
14017
|
const isMounting = isInitialRender || wasReset;
|
|
14076
14018
|
const animations = [];
|
|
14019
|
+
const resolveInitial = (definition = initial) => (typeof definition !== "boolean" &&
|
|
14020
|
+
resolveVariant(visualElement, Array.isArray(definition) ? definition[0] : definition, visualElement.presenceContext?.custom)) ||
|
|
14021
|
+
undefined;
|
|
14077
14022
|
/**
|
|
14078
|
-
* Values removed from a type
|
|
14079
|
-
*
|
|
14023
|
+
* Values removed from a type, mapped to the out transition of the
|
|
14024
|
+
* state they're leaving. A lower-priority type that defines them
|
|
14025
|
+
* animates them back, otherwise they fall back to a base target.
|
|
14080
14026
|
*/
|
|
14081
|
-
const removedKeys = new
|
|
14027
|
+
const removedKeys = new Map();
|
|
14082
14028
|
/**
|
|
14083
14029
|
* Values claimed by active, higher-priority types.
|
|
14084
14030
|
*/
|
|
@@ -14096,6 +14042,13 @@
|
|
|
14096
14042
|
do
|
|
14097
14043
|
source = source.props.inherit !== false && source.parent;
|
|
14098
14044
|
while (source && !source.isControllingVariants);
|
|
14045
|
+
/**
|
|
14046
|
+
* Mounting leaves initial, so values use its out transition. Like the
|
|
14047
|
+
* other types, variant children inherit initial's label.
|
|
14048
|
+
*/
|
|
14049
|
+
const initialOut = isMounting &&
|
|
14050
|
+
getOutTransition(resolveInitial(initial ?? (source && source.props.initial))
|
|
14051
|
+
?.transition);
|
|
14099
14052
|
reversePriorityOrder.forEach((type, i) => {
|
|
14100
14053
|
const typeState = state[type];
|
|
14101
14054
|
const ownProp = props[type];
|
|
@@ -14107,8 +14060,6 @@
|
|
|
14107
14060
|
: undefined;
|
|
14108
14061
|
const propIsVariant = isVariantLabel(prop);
|
|
14109
14062
|
const activeDelta = type === changedActiveType ? typeState.isActive : null;
|
|
14110
|
-
if (activeDelta === false)
|
|
14111
|
-
removedVariantIndex = i;
|
|
14112
14063
|
/**
|
|
14113
14064
|
* Inherited labels are animated by the parent through
|
|
14114
14065
|
* variantChildren, unless this element mounted after its parent.
|
|
@@ -14123,6 +14074,13 @@
|
|
|
14123
14074
|
typeof prop === "boolean") {
|
|
14124
14075
|
return;
|
|
14125
14076
|
}
|
|
14077
|
+
/**
|
|
14078
|
+
* Set after the skip above, so deactivating a type that has nothing
|
|
14079
|
+
* to animate (e.g. whileTap with no prop) doesn't re-run the variants
|
|
14080
|
+
* below it.
|
|
14081
|
+
*/
|
|
14082
|
+
if (activeDelta === false)
|
|
14083
|
+
removedVariantIndex = i;
|
|
14126
14084
|
/**
|
|
14127
14085
|
* Don't re-resolve a running exit: a changed custom would start
|
|
14128
14086
|
* new value animations that stop the originals, leaving the exit
|
|
@@ -14142,6 +14100,7 @@
|
|
|
14142
14100
|
let handledRemovedValues = false;
|
|
14143
14101
|
const definitionList = Array.isArray(prop) ? prop : [prop];
|
|
14144
14102
|
let resolvedValues = {};
|
|
14103
|
+
let out;
|
|
14145
14104
|
if (activeDelta !== false) {
|
|
14146
14105
|
for (const definition of definitionList) {
|
|
14147
14106
|
const { transition, transitionEnd, ...target } = resolveVariant(visualElement, definition, getTypeCustom(visualElement, type)) || {};
|
|
@@ -14150,6 +14109,11 @@
|
|
|
14150
14109
|
...target,
|
|
14151
14110
|
...transitionEnd,
|
|
14152
14111
|
};
|
|
14112
|
+
/**
|
|
14113
|
+
* One out transition per type. With a list of labels,
|
|
14114
|
+
* the last one that sets out applies to all its values.
|
|
14115
|
+
*/
|
|
14116
|
+
out = getOutTransition(transition) || out;
|
|
14153
14117
|
}
|
|
14154
14118
|
}
|
|
14155
14119
|
const { prevResolvedValues } = typeState;
|
|
@@ -14165,12 +14129,18 @@
|
|
|
14165
14129
|
const valueHasChanged = Array.isArray(next) && Array.isArray(prev)
|
|
14166
14130
|
? !shallowCompare(next, prev) || variantDidChange
|
|
14167
14131
|
: next !== prev;
|
|
14132
|
+
let outTransition;
|
|
14168
14133
|
if (valueHasChanged && (next === undefined || next === null)) {
|
|
14169
|
-
|
|
14134
|
+
/**
|
|
14135
|
+
* The highest-priority state a value leaves wins.
|
|
14136
|
+
*/
|
|
14137
|
+
removedKeys.has(key) ||
|
|
14138
|
+
removedKeys.set(key, typeState.prevOut);
|
|
14170
14139
|
}
|
|
14171
14140
|
else if (valueHasChanged ||
|
|
14172
14141
|
(next !== undefined && removedKeys.has(key))) {
|
|
14173
14142
|
shouldAnimateType = true;
|
|
14143
|
+
outTransition = removedKeys.get(key) || initialOut;
|
|
14174
14144
|
if (removedKeys.delete(key))
|
|
14175
14145
|
handledRemovedValues = true;
|
|
14176
14146
|
typeState.needsAnimating[key] = true;
|
|
@@ -14181,9 +14151,11 @@
|
|
|
14181
14151
|
else {
|
|
14182
14152
|
typeState.protectedKeys[key] = true;
|
|
14183
14153
|
}
|
|
14154
|
+
typeState.out[key] = outTransition;
|
|
14184
14155
|
}
|
|
14185
14156
|
typeState.prevProp = prop;
|
|
14186
14157
|
typeState.prevResolvedValues = resolvedValues;
|
|
14158
|
+
typeState.prevOut = out;
|
|
14187
14159
|
if (typeState.isActive) {
|
|
14188
14160
|
encounteredKeys = { ...encounteredKeys, ...resolvedValues };
|
|
14189
14161
|
}
|
|
@@ -14214,14 +14186,17 @@
|
|
|
14214
14186
|
* longer defines a value it once did, it stays where it is.
|
|
14215
14187
|
*/
|
|
14216
14188
|
if (removedKeys.size) {
|
|
14217
|
-
const { initial } = props;
|
|
14218
14189
|
const fallbackAnimation = {};
|
|
14219
|
-
const resolvedInitial =
|
|
14220
|
-
|
|
14221
|
-
|
|
14222
|
-
|
|
14223
|
-
|
|
14224
|
-
|
|
14190
|
+
const resolvedInitial = resolveInitial();
|
|
14191
|
+
let fallbackTransition = resolvedInitial?.transition;
|
|
14192
|
+
removedKeys.forEach((outTransition, key) => {
|
|
14193
|
+
if (outTransition) {
|
|
14194
|
+
fallbackTransition = {
|
|
14195
|
+
...(fallbackTransition ||
|
|
14196
|
+
visualElement.getDefaultTransition()),
|
|
14197
|
+
[key]: outTransition,
|
|
14198
|
+
};
|
|
14199
|
+
}
|
|
14225
14200
|
const motionValue = visualElement.getValue(key);
|
|
14226
14201
|
if (motionValue)
|
|
14227
14202
|
motionValue.liveStyle = true;
|
|
@@ -14252,6 +14227,9 @@
|
|
|
14252
14227
|
visualElement.baseTarget[key] = motionValue?.get();
|
|
14253
14228
|
}
|
|
14254
14229
|
});
|
|
14230
|
+
if (fallbackTransition) {
|
|
14231
|
+
fallbackAnimation.transition = fallbackTransition;
|
|
14232
|
+
}
|
|
14255
14233
|
animations.push({ animation: fallbackAnimation });
|
|
14256
14234
|
}
|
|
14257
14235
|
const blockAnimation = (isMounting && visualElement.blockInitialAnimation) ||
|
|
@@ -14311,6 +14289,7 @@
|
|
|
14311
14289
|
protectedKeys: {},
|
|
14312
14290
|
needsAnimating: {},
|
|
14313
14291
|
prevResolvedValues: {},
|
|
14292
|
+
out: {},
|
|
14314
14293
|
};
|
|
14315
14294
|
}
|
|
14316
14295
|
return state;
|
|
@@ -14429,6 +14408,18 @@
|
|
|
14429
14408
|
return duration * (repeat + 1) + repeatDelay * repeat;
|
|
14430
14409
|
}
|
|
14431
14410
|
|
|
14411
|
+
/**
|
|
14412
|
+
* The time of a label, or a label with an offset like "label+1" or
|
|
14413
|
+
* "label-0.5". An exact label name wins over an offset.
|
|
14414
|
+
*/
|
|
14415
|
+
function calcLabelTime(position, labels) {
|
|
14416
|
+
if (labels.has(position))
|
|
14417
|
+
return labels.get(position);
|
|
14418
|
+
const [, name, offset] = position.match(/^(.+?)([+-][\d.]+)$/) || [];
|
|
14419
|
+
return labels.has(name)
|
|
14420
|
+
? Math.max(0, labels.get(name) + parseFloat(offset))
|
|
14421
|
+
: undefined;
|
|
14422
|
+
}
|
|
14432
14423
|
/**
|
|
14433
14424
|
* Given a absolute or relative time definition and current/prev time state of the sequence,
|
|
14434
14425
|
* calculate an absolute time for the next keyframes.
|
|
@@ -14447,7 +14438,7 @@
|
|
|
14447
14438
|
return Math.max(0, prev + parseFloat(next.slice(1)));
|
|
14448
14439
|
}
|
|
14449
14440
|
else {
|
|
14450
|
-
return
|
|
14441
|
+
return calcLabelTime(next, labels) ?? current;
|
|
14451
14442
|
}
|
|
14452
14443
|
}
|
|
14453
14444
|
|
|
@@ -14508,7 +14499,7 @@
|
|
|
14508
14499
|
|
|
14509
14500
|
const defaultSegmentEasing = "easeInOut";
|
|
14510
14501
|
const MAX_REPEAT = 20;
|
|
14511
|
-
function createAnimationsFromSequence(sequence, { defaultTransition = {}, ...sequenceTransition } = {}, scope, generators) {
|
|
14502
|
+
function createAnimationsFromSequence(sequence, { defaultTransition = {}, ...sequenceTransition } = {}, scope, generators, info) {
|
|
14512
14503
|
const defaultDuration = defaultTransition.duration || 0.3;
|
|
14513
14504
|
const animationDefinitions = new Map();
|
|
14514
14505
|
const sequences = new Map();
|
|
@@ -14521,196 +14512,233 @@
|
|
|
14521
14512
|
* Build the timeline by mapping over the sequence array and converting
|
|
14522
14513
|
* the definitions into keyframes and offsets with absolute time values.
|
|
14523
14514
|
* These will later get converted into relative offsets in a second pass.
|
|
14515
|
+
*
|
|
14516
|
+
* Nested sequences are resolved recursively. `base` is the absolute
|
|
14517
|
+
* start of the sequence being resolved, so numeric `at` values inside
|
|
14518
|
+
* a nested sequence are relative to where it starts. Returns the time
|
|
14519
|
+
* the sequence ends.
|
|
14524
14520
|
*/
|
|
14525
|
-
|
|
14526
|
-
|
|
14527
|
-
|
|
14528
|
-
|
|
14529
|
-
|
|
14530
|
-
|
|
14531
|
-
|
|
14532
|
-
continue;
|
|
14533
|
-
}
|
|
14534
|
-
else if (!Array.isArray(segment)) {
|
|
14535
|
-
timeLabels.set(segment.name, calcNextTime(currentTime, segment.at, prevTime, timeLabels));
|
|
14536
|
-
continue;
|
|
14537
|
-
}
|
|
14538
|
-
let [subject, keyframes, transition = {}] = segment;
|
|
14539
|
-
/**
|
|
14540
|
-
* If a relative or absolute time value has been specified we need to resolve
|
|
14541
|
-
* it in relation to the currentTime.
|
|
14542
|
-
*/
|
|
14543
|
-
if (transition.at !== undefined) {
|
|
14544
|
-
currentTime = calcNextTime(currentTime, transition.at, prevTime, timeLabels);
|
|
14545
|
-
}
|
|
14546
|
-
/**
|
|
14547
|
-
* Keep track of the maximum duration in this definition. This will be
|
|
14548
|
-
* applied to currentTime once the definition has been parsed.
|
|
14549
|
-
*/
|
|
14550
|
-
let maxDuration = 0;
|
|
14551
|
-
const resolveValueSequence = (valueKeyframes, valueTransition, valueSequence, elementIndex = 0, numSubjects = 0) => {
|
|
14552
|
-
const valueKeyframesAsList = keyframesAsList(valueKeyframes);
|
|
14553
|
-
const { delay = 0, times = defaultOffset$1(valueKeyframesAsList), type = defaultTransition.type || "keyframes", repeat, repeatType, repeatDelay = 0, ...remainingTransition } = valueTransition;
|
|
14554
|
-
let { ease = defaultTransition.ease || "easeOut", duration } = valueTransition;
|
|
14555
|
-
/**
|
|
14556
|
-
* Resolve stagger() if defined.
|
|
14557
|
-
*/
|
|
14558
|
-
const calculatedDelay = typeof delay === "function"
|
|
14559
|
-
? delay(elementIndex, numSubjects)
|
|
14560
|
-
: delay;
|
|
14521
|
+
const resolveSequence = (sequence, base) => {
|
|
14522
|
+
let sequenceEnd = base;
|
|
14523
|
+
const resolveTime = (at) => typeof at === "number"
|
|
14524
|
+
? base + at
|
|
14525
|
+
: calcNextTime(currentTime, at, prevTime, timeLabels);
|
|
14526
|
+
for (let i = 0; i < sequence.length; i++) {
|
|
14527
|
+
const segment = sequence[i];
|
|
14561
14528
|
/**
|
|
14562
|
-
* If this
|
|
14529
|
+
* If this is a timeline label, mark it and skip the rest of this iteration.
|
|
14563
14530
|
*/
|
|
14564
|
-
|
|
14565
|
-
|
|
14566
|
-
|
|
14567
|
-
|
|
14568
|
-
if (
|
|
14569
|
-
|
|
14570
|
-
|
|
14571
|
-
|
|
14572
|
-
|
|
14573
|
-
|
|
14574
|
-
|
|
14575
|
-
|
|
14576
|
-
|
|
14577
|
-
|
|
14578
|
-
|
|
14579
|
-
|
|
14531
|
+
if (typeof segment === "string") {
|
|
14532
|
+
timeLabels.set(segment, currentTime);
|
|
14533
|
+
continue;
|
|
14534
|
+
}
|
|
14535
|
+
else if (!Array.isArray(segment)) {
|
|
14536
|
+
const start = segment.at === undefined
|
|
14537
|
+
? currentTime
|
|
14538
|
+
: resolveTime(segment.at);
|
|
14539
|
+
if ("sequence" in segment) {
|
|
14540
|
+
/**
|
|
14541
|
+
* Resolve the nested sequence from its start. Afterwards
|
|
14542
|
+
* "<" points at its start and the next segment follows
|
|
14543
|
+
* its end, as if it were a single segment.
|
|
14544
|
+
*/
|
|
14545
|
+
prevTime = currentTime = start;
|
|
14546
|
+
currentTime = resolveSequence(segment.sequence, start);
|
|
14547
|
+
prevTime = start;
|
|
14548
|
+
sequenceEnd = Math.max(sequenceEnd, currentTime);
|
|
14580
14549
|
}
|
|
14581
|
-
|
|
14582
|
-
|
|
14583
|
-
...remainingTransition,
|
|
14584
|
-
};
|
|
14585
|
-
if (duration !== undefined) {
|
|
14586
|
-
springTransition.duration = secondsToMilliseconds(duration);
|
|
14550
|
+
else {
|
|
14551
|
+
timeLabels.set(segment.name, start);
|
|
14587
14552
|
}
|
|
14588
|
-
|
|
14589
|
-
ease = springEasing.ease;
|
|
14590
|
-
duration = springEasing.duration;
|
|
14553
|
+
continue;
|
|
14591
14554
|
}
|
|
14592
|
-
|
|
14593
|
-
const startTime = currentTime + calculatedDelay;
|
|
14555
|
+
let [subject, keyframes, transition = {}] = segment;
|
|
14594
14556
|
/**
|
|
14595
|
-
* If
|
|
14557
|
+
* If a relative or absolute time value has been specified we need to resolve
|
|
14558
|
+
* it in relation to the currentTime.
|
|
14596
14559
|
*/
|
|
14597
|
-
if (
|
|
14598
|
-
|
|
14560
|
+
if (transition.at !== undefined) {
|
|
14561
|
+
currentTime = resolveTime(transition.at);
|
|
14599
14562
|
}
|
|
14600
14563
|
/**
|
|
14601
|
-
*
|
|
14602
|
-
|
|
14603
|
-
const remainder = times.length - valueKeyframesAsList.length;
|
|
14604
|
-
remainder > 0 && fillOffset(times, remainder);
|
|
14605
|
-
/**
|
|
14606
|
-
* If only one value has been set, ie [1], push a null to the start of
|
|
14607
|
-
* the keyframe array. This will let us mark a keyframe at this point
|
|
14608
|
-
* that will later be hydrated with the previous value.
|
|
14564
|
+
* Keep track of the maximum duration in this definition. This will be
|
|
14565
|
+
* applied to currentTime once the definition has been parsed.
|
|
14609
14566
|
*/
|
|
14610
|
-
|
|
14611
|
-
|
|
14612
|
-
|
|
14613
|
-
|
|
14614
|
-
|
|
14615
|
-
* explode the keyframe array. Ignore with a warning.
|
|
14616
|
-
*/
|
|
14617
|
-
if (repeat) {
|
|
14618
|
-
exports.warning(repeat < MAX_REPEAT, `Sequence segments can't repeat ${repeat} times — ignoring repeat option. Use a value below ${MAX_REPEAT} or apply repeat at the sequence level instead.`);
|
|
14619
|
-
}
|
|
14620
|
-
if (repeat && repeat < MAX_REPEAT) {
|
|
14567
|
+
let maxDuration = 0;
|
|
14568
|
+
const resolveValueSequence = (valueKeyframes, valueTransition, valueSequence, elementIndex = 0, numSubjects = 0) => {
|
|
14569
|
+
const valueKeyframesAsList = keyframesAsList(valueKeyframes);
|
|
14570
|
+
const { delay = 0, times = defaultOffset$1(valueKeyframesAsList), type = defaultTransition.type || "keyframes", repeat, repeatType, repeatDelay = 0, ...remainingTransition } = valueTransition;
|
|
14571
|
+
let { ease = defaultTransition.ease || "easeOut", duration } = valueTransition;
|
|
14621
14572
|
/**
|
|
14622
|
-
*
|
|
14623
|
-
* so it can be added to the per-iteration time offsets below
|
|
14624
|
-
* before they're normalized to 0-1.
|
|
14573
|
+
* Resolve stagger() if defined.
|
|
14625
14574
|
*/
|
|
14626
|
-
const
|
|
14627
|
-
|
|
14628
|
-
|
|
14629
|
-
const originalTimes = [...times];
|
|
14630
|
-
ease = Array.isArray(ease) ? [...ease] : [ease];
|
|
14631
|
-
const originalEase = [...ease];
|
|
14575
|
+
const calculatedDelay = typeof delay === "function"
|
|
14576
|
+
? delay(elementIndex, numSubjects)
|
|
14577
|
+
: delay;
|
|
14632
14578
|
/**
|
|
14633
|
-
*
|
|
14634
|
-
* backwards. mirror matches JSAnimation's mirroredGenerator:
|
|
14635
|
-
* reversed keyframes, easings unchanged. reverse matches
|
|
14636
|
-
* JSAnimation's iterationProgress = 1 - p: reversed
|
|
14637
|
-
* keyframes, easing array reversed AND each function easing
|
|
14638
|
-
* mapped through reverseEasing (string easings unchanged —
|
|
14639
|
-
* they're resolved later by the keyframes engine).
|
|
14579
|
+
* If this animation should and can use a spring, generate a spring easing function.
|
|
14640
14580
|
*/
|
|
14641
|
-
const
|
|
14642
|
-
|
|
14643
|
-
|
|
14644
|
-
|
|
14645
|
-
|
|
14646
|
-
|
|
14647
|
-
|
|
14648
|
-
|
|
14649
|
-
|
|
14650
|
-
|
|
14651
|
-
|
|
14581
|
+
const numKeyframes = valueKeyframesAsList.length;
|
|
14582
|
+
const createGenerator = isGenerator(type)
|
|
14583
|
+
? type
|
|
14584
|
+
: generators?.[type || "keyframes"];
|
|
14585
|
+
if (numKeyframes <= 2 && createGenerator) {
|
|
14586
|
+
/**
|
|
14587
|
+
* As we're creating an easing function from a spring,
|
|
14588
|
+
* ideally we want to generate it using the real distance
|
|
14589
|
+
* between the two keyframes. However this isn't always
|
|
14590
|
+
* possible - in these situations we use 0-100.
|
|
14591
|
+
*/
|
|
14592
|
+
let absoluteDelta = 100;
|
|
14593
|
+
if (numKeyframes === 2 &&
|
|
14594
|
+
isNumberKeyframesArray(valueKeyframesAsList)) {
|
|
14595
|
+
const delta = valueKeyframesAsList[1] - valueKeyframesAsList[0];
|
|
14596
|
+
absoluteDelta = Math.abs(delta);
|
|
14597
|
+
}
|
|
14598
|
+
const springTransition = {
|
|
14599
|
+
...defaultTransition,
|
|
14600
|
+
...remainingTransition,
|
|
14601
|
+
};
|
|
14602
|
+
if (duration !== undefined) {
|
|
14603
|
+
springTransition.duration =
|
|
14604
|
+
secondsToMilliseconds(duration);
|
|
14652
14605
|
}
|
|
14606
|
+
const springEasing = createGeneratorEasing(springTransition, absoluteDelta, createGenerator);
|
|
14607
|
+
ease = springEasing.ease;
|
|
14608
|
+
duration = springEasing.duration;
|
|
14609
|
+
}
|
|
14610
|
+
duration ?? (duration = defaultDuration);
|
|
14611
|
+
const startTime = currentTime + calculatedDelay;
|
|
14612
|
+
/**
|
|
14613
|
+
* If there's only one time offset of 0, fill in a second with length 1
|
|
14614
|
+
*/
|
|
14615
|
+
if (times.length === 1 && times[0] === 0) {
|
|
14616
|
+
times[1] = 1;
|
|
14653
14617
|
}
|
|
14654
|
-
|
|
14655
|
-
|
|
14656
|
-
|
|
14657
|
-
|
|
14658
|
-
|
|
14659
|
-
|
|
14660
|
-
|
|
14618
|
+
/**
|
|
14619
|
+
* Fill out if offset if fewer offsets than keyframes
|
|
14620
|
+
*/
|
|
14621
|
+
const remainder = times.length - valueKeyframesAsList.length;
|
|
14622
|
+
remainder > 0 && fillOffset(times, remainder);
|
|
14623
|
+
/**
|
|
14624
|
+
* If only one value has been set, ie [1], push a null to the start of
|
|
14625
|
+
* the keyframe array. This will let us mark a keyframe at this point
|
|
14626
|
+
* that will later be hydrated with the previous value.
|
|
14627
|
+
*/
|
|
14628
|
+
valueKeyframesAsList.length === 1 &&
|
|
14629
|
+
valueKeyframesAsList.unshift(null);
|
|
14630
|
+
/**
|
|
14631
|
+
* Segments can't express `repeat: Infinity` or very large
|
|
14632
|
+
* counts — they'd leave dead time after the segment or
|
|
14633
|
+
* explode the keyframe array. Ignore with a warning.
|
|
14634
|
+
*/
|
|
14635
|
+
if (repeat) {
|
|
14636
|
+
typeof process !== "undefined" &&
|
|
14637
|
+
"development" !== "production" &&
|
|
14638
|
+
exports.warning(repeat < MAX_REPEAT, `Sequence segments can't repeat ${repeat} times — ignoring repeat option. Use a value below ${MAX_REPEAT} or apply repeat at the sequence level instead.`);
|
|
14639
|
+
}
|
|
14640
|
+
if (repeat && repeat < MAX_REPEAT) {
|
|
14641
|
+
/**
|
|
14642
|
+
* Express repeatDelay in units of a single iteration's duration
|
|
14643
|
+
* so it can be added to the per-iteration time offsets below
|
|
14644
|
+
* before they're normalized to 0-1.
|
|
14645
|
+
*/
|
|
14646
|
+
const repeatDelayUnits = duration > 0 ? repeatDelay / duration : 0;
|
|
14647
|
+
duration = calculateRepeatDuration(duration, repeat, repeatDelay);
|
|
14648
|
+
const originalKeyframes = [...valueKeyframesAsList];
|
|
14649
|
+
const originalTimes = [...times];
|
|
14650
|
+
ease = Array.isArray(ease) ? [...ease] : [ease];
|
|
14651
|
+
const originalEase = [...ease];
|
|
14661
14652
|
/**
|
|
14662
|
-
*
|
|
14663
|
-
*
|
|
14664
|
-
*
|
|
14653
|
+
* For reverse/mirror, alternate iterations play the segment
|
|
14654
|
+
* backwards. mirror matches JSAnimation's mirroredGenerator:
|
|
14655
|
+
* reversed keyframes, easings unchanged. reverse matches
|
|
14656
|
+
* JSAnimation's iterationProgress = 1 - p: reversed
|
|
14657
|
+
* keyframes, easing array reversed AND each function easing
|
|
14658
|
+
* mapped through reverseEasing (string easings unchanged —
|
|
14659
|
+
* they're resolved later by the keyframes engine).
|
|
14665
14660
|
*/
|
|
14666
|
-
|
|
14667
|
-
|
|
14668
|
-
|
|
14669
|
-
|
|
14661
|
+
const isFlipping = repeatType === "reverse" || repeatType === "mirror";
|
|
14662
|
+
let flippedKeyframes = originalKeyframes;
|
|
14663
|
+
let flippedEases = originalEase;
|
|
14664
|
+
if (isFlipping) {
|
|
14665
|
+
flippedKeyframes = [...originalKeyframes].reverse();
|
|
14666
|
+
if (repeatType === "reverse") {
|
|
14667
|
+
flippedEases = [...originalEase]
|
|
14668
|
+
.reverse()
|
|
14669
|
+
.map((e) => typeof e === "function"
|
|
14670
|
+
? reverseEasing(e)
|
|
14671
|
+
: e);
|
|
14672
|
+
}
|
|
14670
14673
|
}
|
|
14671
|
-
|
|
14672
|
-
|
|
14673
|
-
|
|
14674
|
-
|
|
14675
|
-
|
|
14676
|
-
|
|
14674
|
+
for (let repeatIndex = 0; repeatIndex < repeat; repeatIndex++) {
|
|
14675
|
+
const isFlipped = isFlipping && repeatIndex % 2 === 0;
|
|
14676
|
+
const iterKeyframes = isFlipped
|
|
14677
|
+
? flippedKeyframes
|
|
14678
|
+
: originalKeyframes;
|
|
14679
|
+
const iterEase = isFlipped ? flippedEases : originalEase;
|
|
14680
|
+
const iterStartOffset = (repeatIndex + 1) * (1 + repeatDelayUnits);
|
|
14681
|
+
/**
|
|
14682
|
+
* If repeatDelay is set, hold the previous iteration's
|
|
14683
|
+
* final value through the delay by inserting a keyframe
|
|
14684
|
+
* at the moment the next iteration begins.
|
|
14685
|
+
*/
|
|
14686
|
+
if (repeatDelayUnits > 0) {
|
|
14687
|
+
valueKeyframesAsList.push(valueKeyframesAsList[valueKeyframesAsList.length - 1]);
|
|
14688
|
+
times.push(iterStartOffset);
|
|
14689
|
+
ease.push("linear");
|
|
14690
|
+
}
|
|
14691
|
+
valueKeyframesAsList.push(...iterKeyframes);
|
|
14692
|
+
for (let keyframeIndex = 0; keyframeIndex < iterKeyframes.length; keyframeIndex++) {
|
|
14693
|
+
times.push(originalTimes[keyframeIndex] + iterStartOffset);
|
|
14694
|
+
ease.push(keyframeIndex === 0
|
|
14695
|
+
? "linear"
|
|
14696
|
+
: getEasingForSegment(iterEase, keyframeIndex - 1));
|
|
14697
|
+
}
|
|
14677
14698
|
}
|
|
14699
|
+
normalizeTimes(times, repeat, repeatDelayUnits);
|
|
14678
14700
|
}
|
|
14679
|
-
|
|
14701
|
+
const targetTime = startTime + duration;
|
|
14702
|
+
/**
|
|
14703
|
+
* Add keyframes, mapping offsets to absolute time.
|
|
14704
|
+
*/
|
|
14705
|
+
addKeyframes(valueSequence, valueKeyframesAsList, ease, times, startTime, targetTime);
|
|
14706
|
+
maxDuration = Math.max(calculatedDelay + duration, maxDuration);
|
|
14707
|
+
totalDuration = Math.max(targetTime, totalDuration);
|
|
14708
|
+
};
|
|
14709
|
+
if (isMotionValue(subject)) {
|
|
14710
|
+
const subjectSequence = getSubjectSequence(subject, sequences);
|
|
14711
|
+
resolveValueSequence(keyframes, transition, getValueSequence("default", subjectSequence));
|
|
14680
14712
|
}
|
|
14681
|
-
|
|
14682
|
-
|
|
14683
|
-
|
|
14684
|
-
*/
|
|
14685
|
-
addKeyframes(valueSequence, valueKeyframesAsList, ease, times, startTime, targetTime);
|
|
14686
|
-
maxDuration = Math.max(calculatedDelay + duration, maxDuration);
|
|
14687
|
-
totalDuration = Math.max(targetTime, totalDuration);
|
|
14688
|
-
};
|
|
14689
|
-
if (isMotionValue(subject)) {
|
|
14690
|
-
const subjectSequence = getSubjectSequence(subject, sequences);
|
|
14691
|
-
resolveValueSequence(keyframes, transition, getValueSequence("default", subjectSequence));
|
|
14692
|
-
}
|
|
14693
|
-
else {
|
|
14694
|
-
const subjects = resolveSubjects(subject, keyframes, scope, elementCache);
|
|
14695
|
-
const numSubjects = subjects.length;
|
|
14696
|
-
/**
|
|
14697
|
-
* For every element in this segment, process the defined values.
|
|
14698
|
-
*/
|
|
14699
|
-
for (let subjectIndex = 0; subjectIndex < numSubjects; subjectIndex++) {
|
|
14713
|
+
else {
|
|
14714
|
+
const subjects = resolveSubjects(subject, keyframes, scope, elementCache);
|
|
14715
|
+
const numSubjects = subjects.length;
|
|
14700
14716
|
/**
|
|
14701
|
-
*
|
|
14717
|
+
* For every element in this segment, process the defined values.
|
|
14702
14718
|
*/
|
|
14703
|
-
|
|
14704
|
-
|
|
14705
|
-
|
|
14706
|
-
|
|
14707
|
-
|
|
14708
|
-
|
|
14719
|
+
for (let subjectIndex = 0; subjectIndex < numSubjects; subjectIndex++) {
|
|
14720
|
+
/**
|
|
14721
|
+
* Cast necessary, but we know these are of this type
|
|
14722
|
+
*/
|
|
14723
|
+
keyframes = keyframes;
|
|
14724
|
+
transition = transition;
|
|
14725
|
+
const thisSubject = subjects[subjectIndex];
|
|
14726
|
+
const subjectSequence = getSubjectSequence(thisSubject, sequences);
|
|
14727
|
+
for (const key in keyframes) {
|
|
14728
|
+
resolveValueSequence(keyframes[key], getValueTransition(transition, key), getValueSequence(key, subjectSequence), subjectIndex, numSubjects);
|
|
14729
|
+
}
|
|
14709
14730
|
}
|
|
14710
14731
|
}
|
|
14732
|
+
prevTime = currentTime;
|
|
14733
|
+
currentTime += maxDuration;
|
|
14734
|
+
sequenceEnd = Math.max(sequenceEnd, currentTime);
|
|
14711
14735
|
}
|
|
14712
|
-
|
|
14713
|
-
|
|
14736
|
+
return sequenceEnd;
|
|
14737
|
+
};
|
|
14738
|
+
resolveSequence(sequence, 0);
|
|
14739
|
+
if (info) {
|
|
14740
|
+
info.duration = totalDuration;
|
|
14741
|
+
info.labels = timeLabels;
|
|
14714
14742
|
}
|
|
14715
14743
|
/**
|
|
14716
14744
|
* For every element and value combination create a new animation.
|
|
@@ -14858,7 +14886,7 @@
|
|
|
14858
14886
|
return animations;
|
|
14859
14887
|
}
|
|
14860
14888
|
|
|
14861
|
-
function animateSequence(sequence, options, scope) {
|
|
14889
|
+
function animateSequence(sequence, options, scope, info) {
|
|
14862
14890
|
const animations = [];
|
|
14863
14891
|
/**
|
|
14864
14892
|
* Pre-process: replace function segments with MotionValue segments,
|
|
@@ -14881,13 +14909,515 @@
|
|
|
14881
14909
|
}
|
|
14882
14910
|
return segment;
|
|
14883
14911
|
});
|
|
14884
|
-
const animationDefinitions = createAnimationsFromSequence(processedSequence, options, scope, { spring });
|
|
14912
|
+
const animationDefinitions = createAnimationsFromSequence(processedSequence, options, scope, { spring }, info);
|
|
14885
14913
|
animationDefinitions.forEach(({ keyframes, transition }, subject) => {
|
|
14886
14914
|
animations.push(...animateSubject(subject, keyframes, transition));
|
|
14887
14915
|
});
|
|
14888
14916
|
return animations;
|
|
14889
14917
|
}
|
|
14890
14918
|
|
|
14919
|
+
let markerId = 0;
|
|
14920
|
+
/**
|
|
14921
|
+
* Copy a segment with a new `at`, adding a transition if it has none.
|
|
14922
|
+
*/
|
|
14923
|
+
function withAt(segment, at) {
|
|
14924
|
+
const copy = [...segment];
|
|
14925
|
+
const i = typeof copy[0] === "function" ? Math.max(1, copy.length - 1) : 2;
|
|
14926
|
+
copy[i] = { ...copy[i], at };
|
|
14927
|
+
return copy;
|
|
14928
|
+
}
|
|
14929
|
+
const label = (name, at) => at === undefined ? name : { name, at };
|
|
14930
|
+
/**
|
|
14931
|
+
* Rewind to the start and stop, so every value is back at the value it
|
|
14932
|
+
* started from and the next build reads those values as its origin.
|
|
14933
|
+
*/
|
|
14934
|
+
function rewind(animation) {
|
|
14935
|
+
animation.pause();
|
|
14936
|
+
animation.time = 0;
|
|
14937
|
+
animation.stop();
|
|
14938
|
+
}
|
|
14939
|
+
/**
|
|
14940
|
+
* A sequence that can be changed after it's created. Segments, labels,
|
|
14941
|
+
* callbacks, pauses and other timelines can be added, moved and removed
|
|
14942
|
+
* at any point, including while it plays.
|
|
14943
|
+
*
|
|
14944
|
+
* Changes are batched and applied once per microtask. Each change
|
|
14945
|
+
* rebuilds the underlying animations from the timeline's starting
|
|
14946
|
+
* values, then restores the playhead, speed and play state, so changes
|
|
14947
|
+
* made while playing take effect without a jump.
|
|
14948
|
+
*/
|
|
14949
|
+
class Timeline {
|
|
14950
|
+
/**
|
|
14951
|
+
* `animations` are already running animations of `items`, as
|
|
14952
|
+
* animate() makes them. The timeline plays these until it's first
|
|
14953
|
+
* changed, so it costs nothing over the animations themselves until
|
|
14954
|
+
* then.
|
|
14955
|
+
*/
|
|
14956
|
+
constructor(items = [], options = {}, animations) {
|
|
14957
|
+
this.options = options;
|
|
14958
|
+
this.entries = [];
|
|
14959
|
+
/**
|
|
14960
|
+
* The repeat the playhead is in, to detect when it starts another.
|
|
14961
|
+
*/
|
|
14962
|
+
this.repeatIndex = 0;
|
|
14963
|
+
this.prevTime = 0;
|
|
14964
|
+
this.markers = [];
|
|
14965
|
+
this.labels = new Map();
|
|
14966
|
+
this.totalDuration = 0;
|
|
14967
|
+
this.isDirty = false;
|
|
14968
|
+
this.isDone = false;
|
|
14969
|
+
this.isRebuilding = false;
|
|
14970
|
+
this.playbackSpeed = 1;
|
|
14971
|
+
/**
|
|
14972
|
+
* True while this plays the animations it was created with, before
|
|
14973
|
+
* any change compiles it as a sequence.
|
|
14974
|
+
*/
|
|
14975
|
+
this.isWrapped = false;
|
|
14976
|
+
this.stop = () => {
|
|
14977
|
+
this.flush();
|
|
14978
|
+
this.group?.stop();
|
|
14979
|
+
};
|
|
14980
|
+
this.isPaused = options.autoplay === false;
|
|
14981
|
+
this.newFinished();
|
|
14982
|
+
items.forEach((item) => this.add(item));
|
|
14983
|
+
if (animations) {
|
|
14984
|
+
this.isDirty = false;
|
|
14985
|
+
this.isWrapped = true;
|
|
14986
|
+
this.group = new GroupAnimationWithThen(animations);
|
|
14987
|
+
this.watch();
|
|
14988
|
+
}
|
|
14989
|
+
else {
|
|
14990
|
+
/**
|
|
14991
|
+
* Build now, like animate(), so selectors resolve while a
|
|
14992
|
+
* scope is attached. Later changes are batched.
|
|
14993
|
+
*/
|
|
14994
|
+
this.invalidate();
|
|
14995
|
+
this.flush();
|
|
14996
|
+
}
|
|
14997
|
+
/**
|
|
14998
|
+
* Register with the scope (for instance from useAnimate()) so it
|
|
14999
|
+
* stops when the scope is torn down.
|
|
15000
|
+
*/
|
|
15001
|
+
const { scope } = options;
|
|
15002
|
+
if (scope) {
|
|
15003
|
+
scope.animations.push(this);
|
|
15004
|
+
this.finished.then(() => removeItem(scope.animations, this));
|
|
15005
|
+
}
|
|
15006
|
+
}
|
|
15007
|
+
/**
|
|
15008
|
+
* Add a segment, label or timeline. `at` overrides any `at` already
|
|
15009
|
+
* on the segment. The item itself is the handle for `move()` and
|
|
15010
|
+
* `remove()`.
|
|
15011
|
+
*/
|
|
15012
|
+
add(item, at) {
|
|
15013
|
+
if (item instanceof Timeline)
|
|
15014
|
+
item.adopt(this);
|
|
15015
|
+
return this.push({ item, at });
|
|
15016
|
+
}
|
|
15017
|
+
/**
|
|
15018
|
+
* Fire a callback when the playhead crosses `at`, in either direction.
|
|
15019
|
+
* It receives the direction: 1 forwards, -1 backwards.
|
|
15020
|
+
*/
|
|
15021
|
+
call(callback, at) {
|
|
15022
|
+
return this.push({
|
|
15023
|
+
item: callback,
|
|
15024
|
+
at,
|
|
15025
|
+
marker: { name: "_m" + markerId++, time: 0, callback },
|
|
15026
|
+
});
|
|
15027
|
+
}
|
|
15028
|
+
/**
|
|
15029
|
+
* Pause when the playhead reaches `at`.
|
|
15030
|
+
*/
|
|
15031
|
+
addPause(at, callback) {
|
|
15032
|
+
return this.push({
|
|
15033
|
+
item: callback || {},
|
|
15034
|
+
at,
|
|
15035
|
+
marker: { name: "_m" + markerId++, time: 0, callback, pause: true },
|
|
15036
|
+
});
|
|
15037
|
+
}
|
|
15038
|
+
/**
|
|
15039
|
+
* Change where an item starts.
|
|
15040
|
+
*/
|
|
15041
|
+
move(item, at) {
|
|
15042
|
+
this.entries.forEach((entry) => {
|
|
15043
|
+
if (entry.item === item)
|
|
15044
|
+
entry.at = at;
|
|
15045
|
+
});
|
|
15046
|
+
return this.invalidate();
|
|
15047
|
+
}
|
|
15048
|
+
/**
|
|
15049
|
+
* Remove a segment, label, callback or timeline.
|
|
15050
|
+
*/
|
|
15051
|
+
remove(item) {
|
|
15052
|
+
if (item instanceof Timeline)
|
|
15053
|
+
item.parent = undefined;
|
|
15054
|
+
this.entries = this.entries.filter((entry) => entry.item !== item);
|
|
15055
|
+
return this.invalidate();
|
|
15056
|
+
}
|
|
15057
|
+
clear() {
|
|
15058
|
+
this.entries = [];
|
|
15059
|
+
return this.invalidate();
|
|
15060
|
+
}
|
|
15061
|
+
/**
|
|
15062
|
+
* The time of a label in seconds, or `undefined` if there's no label
|
|
15063
|
+
* with this name.
|
|
15064
|
+
*/
|
|
15065
|
+
getLabelTime(name) {
|
|
15066
|
+
this.flush();
|
|
15067
|
+
return this.labels.get(name);
|
|
15068
|
+
}
|
|
15069
|
+
/**
|
|
15070
|
+
* Jump to a time in seconds or a label.
|
|
15071
|
+
*/
|
|
15072
|
+
seek(position) {
|
|
15073
|
+
this.time = this.resolve(position);
|
|
15074
|
+
return this;
|
|
15075
|
+
}
|
|
15076
|
+
/**
|
|
15077
|
+
* Play forwards or backwards to a time or label, then pause there.
|
|
15078
|
+
*/
|
|
15079
|
+
playTo(position) {
|
|
15080
|
+
const target = this.resolve(position);
|
|
15081
|
+
const speed = Math.abs(this.speed);
|
|
15082
|
+
this.speed = target < this.time ? -speed : speed;
|
|
15083
|
+
this.stopAt = target;
|
|
15084
|
+
this.clock || this.invalidate();
|
|
15085
|
+
this.play();
|
|
15086
|
+
return this;
|
|
15087
|
+
}
|
|
15088
|
+
reverse() {
|
|
15089
|
+
this.speed = -this.speed;
|
|
15090
|
+
this.play();
|
|
15091
|
+
return this;
|
|
15092
|
+
}
|
|
15093
|
+
get progress() {
|
|
15094
|
+
return this.duration ? this.time / this.duration : 0;
|
|
15095
|
+
}
|
|
15096
|
+
set progress(progress) {
|
|
15097
|
+
this.time = progress * this.duration;
|
|
15098
|
+
}
|
|
15099
|
+
get time() {
|
|
15100
|
+
return this.active()?.time ?? 0;
|
|
15101
|
+
}
|
|
15102
|
+
set time(time) {
|
|
15103
|
+
this.flush();
|
|
15104
|
+
this.prevTime = time;
|
|
15105
|
+
this.group && (this.group.time = time);
|
|
15106
|
+
this.repeatIndex = this.currentRepeat();
|
|
15107
|
+
}
|
|
15108
|
+
get speed() {
|
|
15109
|
+
return this.playbackSpeed;
|
|
15110
|
+
}
|
|
15111
|
+
set speed(speed) {
|
|
15112
|
+
this.playbackSpeed = speed;
|
|
15113
|
+
this.group && (this.group.speed = speed);
|
|
15114
|
+
}
|
|
15115
|
+
get duration() {
|
|
15116
|
+
this.flush();
|
|
15117
|
+
return this.isWrapped ? this.group.duration : this.totalDuration;
|
|
15118
|
+
}
|
|
15119
|
+
get iterationDuration() {
|
|
15120
|
+
this.flush();
|
|
15121
|
+
return this.isWrapped
|
|
15122
|
+
? this.group.iterationDuration
|
|
15123
|
+
: this.totalDuration;
|
|
15124
|
+
}
|
|
15125
|
+
get state() {
|
|
15126
|
+
return this.active()?.state ?? "idle";
|
|
15127
|
+
}
|
|
15128
|
+
get startTime() {
|
|
15129
|
+
return this.active()?.startTime ?? null;
|
|
15130
|
+
}
|
|
15131
|
+
play() {
|
|
15132
|
+
this.flush();
|
|
15133
|
+
this.isPaused = false;
|
|
15134
|
+
this.group?.play();
|
|
15135
|
+
/**
|
|
15136
|
+
* Watch after playing, so it waits for the restarted animations
|
|
15137
|
+
* rather than the ones that already finished.
|
|
15138
|
+
*/
|
|
15139
|
+
if (this.isDone) {
|
|
15140
|
+
this.newFinished();
|
|
15141
|
+
this.watch();
|
|
15142
|
+
}
|
|
15143
|
+
}
|
|
15144
|
+
pause() {
|
|
15145
|
+
this.flush();
|
|
15146
|
+
this.isPaused = true;
|
|
15147
|
+
this.group?.pause();
|
|
15148
|
+
}
|
|
15149
|
+
cancel() {
|
|
15150
|
+
this.flush();
|
|
15151
|
+
this.group?.cancel();
|
|
15152
|
+
}
|
|
15153
|
+
complete() {
|
|
15154
|
+
this.flush();
|
|
15155
|
+
this.group?.complete();
|
|
15156
|
+
}
|
|
15157
|
+
attachTimeline(timeline) {
|
|
15158
|
+
this.flush();
|
|
15159
|
+
this.scrollTimeline = timeline;
|
|
15160
|
+
this.detachScroll = this.group?.attachTimeline(timeline);
|
|
15161
|
+
return () => {
|
|
15162
|
+
this.scrollTimeline = undefined;
|
|
15163
|
+
this.detachScroll?.();
|
|
15164
|
+
};
|
|
15165
|
+
}
|
|
15166
|
+
then(onResolve, onReject) {
|
|
15167
|
+
return this.finished.then(onResolve, onReject);
|
|
15168
|
+
}
|
|
15169
|
+
/**
|
|
15170
|
+
* The current animations, if any are running. Skipped animations
|
|
15171
|
+
* (skipAnimations, reduced motion) can leave the group empty.
|
|
15172
|
+
*/
|
|
15173
|
+
active() {
|
|
15174
|
+
this.flush();
|
|
15175
|
+
return this.group?.animations.length ? this.group : undefined;
|
|
15176
|
+
}
|
|
15177
|
+
push(entry) {
|
|
15178
|
+
this.entries.push(entry);
|
|
15179
|
+
return this.invalidate();
|
|
15180
|
+
}
|
|
15181
|
+
/**
|
|
15182
|
+
* Schedule a rebuild. A nested timeline passes changes up, as it's
|
|
15183
|
+
* built as part of its parent.
|
|
15184
|
+
*/
|
|
15185
|
+
invalidate() {
|
|
15186
|
+
if (this.parent) {
|
|
15187
|
+
this.parent.invalidate();
|
|
15188
|
+
}
|
|
15189
|
+
else if (!this.isDirty) {
|
|
15190
|
+
this.isDirty = true;
|
|
15191
|
+
queueMicrotask(() => this.flush());
|
|
15192
|
+
}
|
|
15193
|
+
return this;
|
|
15194
|
+
}
|
|
15195
|
+
/**
|
|
15196
|
+
* Apply pending changes now, so reads straight after a change
|
|
15197
|
+
* reflect it.
|
|
15198
|
+
*/
|
|
15199
|
+
flush() {
|
|
15200
|
+
this.isDirty && !this.parent && this.build();
|
|
15201
|
+
}
|
|
15202
|
+
adopt(parent) {
|
|
15203
|
+
if (this.group) {
|
|
15204
|
+
rewind(this.group);
|
|
15205
|
+
this.group = undefined;
|
|
15206
|
+
}
|
|
15207
|
+
this.isDirty = false;
|
|
15208
|
+
this.parent = parent;
|
|
15209
|
+
}
|
|
15210
|
+
build() {
|
|
15211
|
+
this.isDirty = false;
|
|
15212
|
+
this.isWrapped = false;
|
|
15213
|
+
const prev = this.group;
|
|
15214
|
+
let time = 0;
|
|
15215
|
+
if (prev) {
|
|
15216
|
+
this.isRebuilding = true;
|
|
15217
|
+
if (this.scrollTimeline) {
|
|
15218
|
+
/**
|
|
15219
|
+
* Scroll-linked animations can't be seeked in seconds.
|
|
15220
|
+
* Cancelling also returns them to their starting values,
|
|
15221
|
+
* and the new build is attached to scroll instead.
|
|
15222
|
+
*/
|
|
15223
|
+
this.detachScroll?.();
|
|
15224
|
+
prev.cancel();
|
|
15225
|
+
}
|
|
15226
|
+
else {
|
|
15227
|
+
time = prev.animations.length ? prev.time : 0;
|
|
15228
|
+
rewind(prev);
|
|
15229
|
+
}
|
|
15230
|
+
this.isRebuilding = false;
|
|
15231
|
+
}
|
|
15232
|
+
const { defaultTransition, onComplete, onUpdate, onRepeat, onReverseComplete, autoplay, scope, ...options } = this.options;
|
|
15233
|
+
/**
|
|
15234
|
+
* Inherit MotionConfig settings from a useAnimate() scope.
|
|
15235
|
+
*/
|
|
15236
|
+
const playback = {};
|
|
15237
|
+
if (scope?.reduceMotion !== undefined) {
|
|
15238
|
+
playback.reduceMotion = scope.reduceMotion;
|
|
15239
|
+
}
|
|
15240
|
+
if (scope?.skipAnimations !== undefined) {
|
|
15241
|
+
playback.skipAnimations = scope.skipAnimations;
|
|
15242
|
+
}
|
|
15243
|
+
Object.assign(playback, options);
|
|
15244
|
+
const markers = new Map();
|
|
15245
|
+
const info = {};
|
|
15246
|
+
const animations = animateSequence(this.toSequence(markers), { defaultTransition, ...playback }, scope, info);
|
|
15247
|
+
const labels = info.labels;
|
|
15248
|
+
let duration = playback.duration ?? info.duration;
|
|
15249
|
+
/**
|
|
15250
|
+
* A `duration` option stretches every animation to fit, so scale
|
|
15251
|
+
* label and marker times to match.
|
|
15252
|
+
*/
|
|
15253
|
+
const scale = info.duration ? duration / info.duration : 1;
|
|
15254
|
+
labels.forEach((time, name) => labels.set(name, time * scale));
|
|
15255
|
+
/**
|
|
15256
|
+
* Markers are resolved as labels. Move them out of the label map
|
|
15257
|
+
* and extend the duration to reach any marker past the end.
|
|
15258
|
+
*/
|
|
15259
|
+
this.markers = [];
|
|
15260
|
+
markers.forEach((marker, name) => {
|
|
15261
|
+
marker.time = labels.get(name);
|
|
15262
|
+
labels.delete(name);
|
|
15263
|
+
duration = Math.max(duration, marker.time);
|
|
15264
|
+
this.markers.push(marker);
|
|
15265
|
+
});
|
|
15266
|
+
this.markers.sort((a, b) => a.time - b.time);
|
|
15267
|
+
this.labels = labels;
|
|
15268
|
+
this.totalDuration = duration;
|
|
15269
|
+
/**
|
|
15270
|
+
* Callbacks, pauses and playTo() watch a linear clock. Without
|
|
15271
|
+
* them, the timeline is only its animations.
|
|
15272
|
+
*/
|
|
15273
|
+
this.clock = this.clockAnimation = undefined;
|
|
15274
|
+
if (this.markers.length ||
|
|
15275
|
+
this.stopAt !== undefined ||
|
|
15276
|
+
onUpdate ||
|
|
15277
|
+
onRepeat) {
|
|
15278
|
+
const clock = (this.clock = motionValue(0));
|
|
15279
|
+
clock.on("change", (t) => clock === this.clock && this.onClock(t));
|
|
15280
|
+
animations.unshift((this.clockAnimation = animateSingleValue(clock, [0, duration], {
|
|
15281
|
+
...playback,
|
|
15282
|
+
duration,
|
|
15283
|
+
ease: "linear",
|
|
15284
|
+
})));
|
|
15285
|
+
}
|
|
15286
|
+
const group = (this.group = new GroupAnimationWithThen(animations));
|
|
15287
|
+
time = Math.min(time, duration);
|
|
15288
|
+
if (this.isDone && time < duration)
|
|
15289
|
+
this.newFinished();
|
|
15290
|
+
if (this.scrollTimeline) {
|
|
15291
|
+
this.detachScroll = group.attachTimeline(this.scrollTimeline);
|
|
15292
|
+
}
|
|
15293
|
+
else {
|
|
15294
|
+
group.speed = this.playbackSpeed;
|
|
15295
|
+
if (this.isPaused)
|
|
15296
|
+
group.pause();
|
|
15297
|
+
if (time)
|
|
15298
|
+
group.time = time;
|
|
15299
|
+
}
|
|
15300
|
+
this.repeatIndex = this.currentRepeat();
|
|
15301
|
+
/**
|
|
15302
|
+
* Treat a start from 0 as coming from just before it, so markers
|
|
15303
|
+
* at 0 fire.
|
|
15304
|
+
*/
|
|
15305
|
+
this.prevTime = time || -1;
|
|
15306
|
+
this.watch();
|
|
15307
|
+
}
|
|
15308
|
+
toSequence(markers) {
|
|
15309
|
+
return this.entries.map(({ item, at, marker }) => {
|
|
15310
|
+
if (marker) {
|
|
15311
|
+
markers.set(marker.name, marker);
|
|
15312
|
+
return label(marker.name, at);
|
|
15313
|
+
}
|
|
15314
|
+
if (item instanceof Timeline) {
|
|
15315
|
+
return { sequence: item.toSequence(markers), at };
|
|
15316
|
+
}
|
|
15317
|
+
if (typeof item === "string")
|
|
15318
|
+
return label(item, at);
|
|
15319
|
+
if (at === undefined)
|
|
15320
|
+
return item;
|
|
15321
|
+
return Array.isArray(item)
|
|
15322
|
+
? withAt(item, at)
|
|
15323
|
+
: { ...item, at };
|
|
15324
|
+
});
|
|
15325
|
+
}
|
|
15326
|
+
resolve(position) {
|
|
15327
|
+
this.flush();
|
|
15328
|
+
return typeof position === "number"
|
|
15329
|
+
? position
|
|
15330
|
+
: calcLabelTime(position, this.labels) ?? this.time;
|
|
15331
|
+
}
|
|
15332
|
+
/**
|
|
15333
|
+
* The repeat the clock is in, from its time across all repeats.
|
|
15334
|
+
*/
|
|
15335
|
+
currentRepeat() {
|
|
15336
|
+
const { repeat = 0, repeatDelay = 0, delay = 0 } = this.options;
|
|
15337
|
+
const cycle = this.totalDuration + repeatDelay;
|
|
15338
|
+
const time = (this.clockAnimation?.time ?? 0) - delay;
|
|
15339
|
+
return cycle && time > 0
|
|
15340
|
+
? Math.min(repeat, Math.floor(time / cycle))
|
|
15341
|
+
: 0;
|
|
15342
|
+
}
|
|
15343
|
+
onClock(time) {
|
|
15344
|
+
let prevTime = this.prevTime;
|
|
15345
|
+
this.prevTime = time;
|
|
15346
|
+
if (this.isRebuilding)
|
|
15347
|
+
return;
|
|
15348
|
+
const { onUpdate, onRepeat, repeatType = "loop" } = this.options;
|
|
15349
|
+
onUpdate?.(time);
|
|
15350
|
+
const repeatIndex = this.currentRepeat();
|
|
15351
|
+
if (repeatIndex !== this.repeatIndex) {
|
|
15352
|
+
this.repeatIndex = repeatIndex;
|
|
15353
|
+
onRepeat?.();
|
|
15354
|
+
/**
|
|
15355
|
+
* A loop jumps the playhead back to the start, or to the
|
|
15356
|
+
* end when playing backwards. Finish crossing the repeat
|
|
15357
|
+
* that ended, then cross the new one from its edge.
|
|
15358
|
+
*/
|
|
15359
|
+
if (repeatType === "loop") {
|
|
15360
|
+
const isForward = this.speed > 0;
|
|
15361
|
+
if (this.cross(prevTime, isForward ? this.totalDuration : 0)) {
|
|
15362
|
+
return;
|
|
15363
|
+
}
|
|
15364
|
+
prevTime = isForward ? -1 : Infinity;
|
|
15365
|
+
}
|
|
15366
|
+
}
|
|
15367
|
+
this.cross(prevTime, time);
|
|
15368
|
+
}
|
|
15369
|
+
/**
|
|
15370
|
+
* Fire markers between two playhead times. Returns `true` if it
|
|
15371
|
+
* paused.
|
|
15372
|
+
*/
|
|
15373
|
+
cross(from, to) {
|
|
15374
|
+
if (from === to)
|
|
15375
|
+
return;
|
|
15376
|
+
const isForward = to > from;
|
|
15377
|
+
const crossed = (t) => isForward ? t > from && t <= to : t < from && t >= to;
|
|
15378
|
+
const markers = isForward ? this.markers : [...this.markers].reverse();
|
|
15379
|
+
for (const marker of markers) {
|
|
15380
|
+
if (!crossed(marker.time))
|
|
15381
|
+
continue;
|
|
15382
|
+
marker.callback?.(isForward ? 1 : -1);
|
|
15383
|
+
if (marker.pause)
|
|
15384
|
+
return this.pauseAt(marker.time);
|
|
15385
|
+
}
|
|
15386
|
+
if (this.stopAt !== undefined && crossed(this.stopAt)) {
|
|
15387
|
+
this.pauseAt(this.stopAt);
|
|
15388
|
+
this.stopAt = undefined;
|
|
15389
|
+
return true;
|
|
15390
|
+
}
|
|
15391
|
+
}
|
|
15392
|
+
pauseAt(time) {
|
|
15393
|
+
this.pause();
|
|
15394
|
+
this.time = time;
|
|
15395
|
+
return true;
|
|
15396
|
+
}
|
|
15397
|
+
watch() {
|
|
15398
|
+
const group = this.group;
|
|
15399
|
+
group.finished.then(() => {
|
|
15400
|
+
if (group !== this.group || this.isDone)
|
|
15401
|
+
return;
|
|
15402
|
+
this.isDone = true;
|
|
15403
|
+
const { onComplete, onReverseComplete } = this.options;
|
|
15404
|
+
/**
|
|
15405
|
+
* Without onReverseComplete, onComplete fires in either
|
|
15406
|
+
* direction, as it does for a single animation.
|
|
15407
|
+
*/
|
|
15408
|
+
const callback = (this.speed < 0 && onReverseComplete) || onComplete;
|
|
15409
|
+
callback?.();
|
|
15410
|
+
this.resolveFinished();
|
|
15411
|
+
});
|
|
15412
|
+
}
|
|
15413
|
+
newFinished() {
|
|
15414
|
+
this.isDone = false;
|
|
15415
|
+
this.finished = new Promise((resolve) => {
|
|
15416
|
+
this.resolveFinished = resolve;
|
|
15417
|
+
});
|
|
15418
|
+
}
|
|
15419
|
+
}
|
|
15420
|
+
|
|
14891
15421
|
function isSequence(value) {
|
|
14892
15422
|
return Array.isArray(value) && value.some(Array.isArray);
|
|
14893
15423
|
}
|
|
@@ -14901,8 +15431,6 @@
|
|
|
14901
15431
|
* Implementation
|
|
14902
15432
|
*/
|
|
14903
15433
|
function scopedAnimate(subjectOrSequence, optionsOrKeyframes, options) {
|
|
14904
|
-
let animations = [];
|
|
14905
|
-
let animationOnComplete;
|
|
14906
15434
|
const motionScope = activeScope.current;
|
|
14907
15435
|
const scopeTransition = motionScope?.transition;
|
|
14908
15436
|
const inherited = {};
|
|
@@ -14910,45 +15438,53 @@
|
|
|
14910
15438
|
inherited.reduceMotion = reduceMotion;
|
|
14911
15439
|
if (skipAnimations !== undefined)
|
|
14912
15440
|
inherited.skipAnimations = skipAnimations;
|
|
15441
|
+
let timeline;
|
|
14913
15442
|
if (isSequence(subjectOrSequence)) {
|
|
14914
|
-
const
|
|
14915
|
-
|
|
14916
|
-
|
|
14917
|
-
|
|
15443
|
+
const sequenceOptions = {
|
|
15444
|
+
...inherited,
|
|
15445
|
+
...optionsOrKeyframes,
|
|
15446
|
+
scope,
|
|
15447
|
+
};
|
|
14918
15448
|
if (scopeTransition) {
|
|
14919
15449
|
sequenceOptions.defaultTransition = {
|
|
14920
15450
|
...scopeTransition,
|
|
14921
15451
|
...sequenceOptions.defaultTransition,
|
|
14922
15452
|
};
|
|
14923
15453
|
}
|
|
14924
|
-
|
|
15454
|
+
timeline = new Timeline(subjectOrSequence, sequenceOptions);
|
|
14925
15455
|
}
|
|
14926
15456
|
else {
|
|
14927
15457
|
// Extract top-level onComplete so it doesn't get applied per-value
|
|
14928
15458
|
const { onComplete, ...rest } = options || {};
|
|
14929
|
-
|
|
14930
|
-
animationOnComplete = onComplete;
|
|
14931
|
-
}
|
|
14932
|
-
animations = animateSubject(subjectOrSequence, optionsOrKeyframes, {
|
|
15459
|
+
const transition = {
|
|
14933
15460
|
...inherited,
|
|
14934
15461
|
...scopeTransition,
|
|
14935
15462
|
...rest,
|
|
14936
|
-
}
|
|
14937
|
-
|
|
14938
|
-
|
|
14939
|
-
|
|
14940
|
-
|
|
15463
|
+
};
|
|
15464
|
+
/**
|
|
15465
|
+
* Play the animations directly, and only compile them as a
|
|
15466
|
+
* sequence if the timeline is changed. Values animated from a
|
|
15467
|
+
* number are animated via their onUpdate callback instead.
|
|
15468
|
+
*/
|
|
15469
|
+
const { onUpdate } = rest;
|
|
15470
|
+
const segment = (typeof subjectOrSequence === "number" ||
|
|
15471
|
+
(typeof subjectOrSequence === "string" &&
|
|
15472
|
+
(typeof optionsOrKeyframes === "string" ||
|
|
15473
|
+
Array.isArray(optionsOrKeyframes)))
|
|
15474
|
+
? [
|
|
15475
|
+
onUpdate || (() => { }),
|
|
15476
|
+
Array.isArray(optionsOrKeyframes)
|
|
15477
|
+
? optionsOrKeyframes
|
|
15478
|
+
: [subjectOrSequence, optionsOrKeyframes],
|
|
15479
|
+
{ ...transition, onUpdate: undefined },
|
|
15480
|
+
]
|
|
15481
|
+
: [subjectOrSequence, optionsOrKeyframes, transition]);
|
|
15482
|
+
timeline = new Timeline([segment], { ...inherited, onComplete, scope }, animateSubject(subjectOrSequence, optionsOrKeyframes, transition, scope));
|
|
14941
15483
|
}
|
|
14942
15484
|
if (motionScope) {
|
|
14943
|
-
|
|
15485
|
+
timeline.finished.then(motionScope.add(() => timeline.stop()));
|
|
14944
15486
|
}
|
|
14945
|
-
|
|
14946
|
-
scope.animations.push(animation);
|
|
14947
|
-
animation.finished.then(() => {
|
|
14948
|
-
removeItem(scope.animations, animation);
|
|
14949
|
-
});
|
|
14950
|
-
}
|
|
14951
|
-
return animation;
|
|
15487
|
+
return timeline;
|
|
14952
15488
|
}
|
|
14953
15489
|
return scopedAnimate;
|
|
14954
15490
|
}
|
|
@@ -14971,7 +15507,9 @@
|
|
|
14971
15507
|
}
|
|
14972
15508
|
const elements = resolveElements(elementOrSelector, scope);
|
|
14973
15509
|
const numElements = elements.length;
|
|
14974
|
-
|
|
15510
|
+
typeof process !== "undefined" &&
|
|
15511
|
+
"development" !== "production" &&
|
|
15512
|
+
exports.invariant(Boolean(numElements), "No valid elements provided.", "no-valid-elements");
|
|
14975
15513
|
/**
|
|
14976
15514
|
* WAAPI doesn't support interrupting animations.
|
|
14977
15515
|
*
|
|
@@ -15280,22 +15818,26 @@
|
|
|
15280
15818
|
const p = (i + progress(at(i), at(i + 1), v)) / n;
|
|
15281
15819
|
return reverse ? 1 - p : p;
|
|
15282
15820
|
}
|
|
15283
|
-
|
|
15284
|
-
|
|
15285
|
-
|
|
15286
|
-
|
|
15287
|
-
|
|
15288
|
-
|
|
15289
|
-
|
|
15290
|
-
|
|
15291
|
-
|
|
15292
|
-
|
|
15293
|
-
|
|
15294
|
-
|
|
15295
|
-
|
|
15296
|
-
|
|
15821
|
+
/**
|
|
15822
|
+
* Resolves offsets on both axes, as useScroll reports the target's progress
|
|
15823
|
+
* on each (#2546). `axis` only picks which progress scroll() reports.
|
|
15824
|
+
*/
|
|
15825
|
+
function resolveOffsets(info, { offset: offsetDefinition = ScrollOffset.All }) {
|
|
15826
|
+
for (const axis in axisKeys) {
|
|
15827
|
+
const axisInfo = info[axis];
|
|
15828
|
+
/**
|
|
15829
|
+
* Reset the length of the resolved offset array rather than creating a new one.
|
|
15830
|
+
*/
|
|
15831
|
+
axisInfo.offset.length = 0;
|
|
15832
|
+
/**
|
|
15833
|
+
* Populate the offset array by resolving the user's offset definition into
|
|
15834
|
+
* a list of pixel scroll offsets.
|
|
15835
|
+
*/
|
|
15836
|
+
for (let i = 0; i < offsetDefinition.length; i++) {
|
|
15837
|
+
axisInfo.offset[i] = resolveOffset(offsetDefinition[i], axisInfo.containerLength, axisInfo.targetLength, axisInfo.targetOffset);
|
|
15838
|
+
}
|
|
15839
|
+
axisInfo.progress = clamp(0, 1, offsetsToProgress(axisInfo.offset, axisInfo.current));
|
|
15297
15840
|
}
|
|
15298
|
-
axisInfo.progress = clamp(0, 1, offsetsToProgress(axisInfo.offset, axisInfo.current));
|
|
15299
15841
|
}
|
|
15300
15842
|
|
|
15301
15843
|
function calcInset(element, container) {
|
|
@@ -15801,6 +16343,7 @@
|
|
|
15801
16343
|
exports.buildSVGAttrs = buildSVGAttrs;
|
|
15802
16344
|
exports.buildSVGPath = buildSVGPath;
|
|
15803
16345
|
exports.buildTransform = buildTransform$1;
|
|
16346
|
+
exports.buildTransformer = buildTransformer;
|
|
15804
16347
|
exports.calcAxisDelta = calcAxisDelta;
|
|
15805
16348
|
exports.calcBoxDelta = calcBoxDelta;
|
|
15806
16349
|
exports.calcChildStagger = calcChildStagger;
|