motion 14.5.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 +1468 -835
- package/dist/motion.js +1 -1
- package/package.json +4 -3
package/dist/motion.dev.js
CHANGED
|
@@ -247,8 +247,8 @@
|
|
|
247
247
|
: 0.5 * (2 - Math.pow(2, -10 * (p - 1)));
|
|
248
248
|
|
|
249
249
|
const circIn = (p) => 1 - Math.sin(Math.acos(p));
|
|
250
|
-
const circOut = reverseEasing(circIn);
|
|
251
|
-
const circInOut = mirrorEasing(circIn);
|
|
250
|
+
const circOut = /* @__PURE__ */ reverseEasing(circIn);
|
|
251
|
+
const circInOut = /* @__PURE__ */ mirrorEasing(circIn);
|
|
252
252
|
|
|
253
253
|
const easeIn = /*@__PURE__*/ cubicBezier(0.42, 0, 1, 1);
|
|
254
254
|
const easeOut = /*@__PURE__*/ cubicBezier(0, 0, 0.58, 1);
|
|
@@ -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");
|
|
@@ -928,6 +918,11 @@
|
|
|
928
918
|
*/
|
|
929
919
|
let isProcessing = false;
|
|
930
920
|
let flushNextFrame = false;
|
|
921
|
+
/**
|
|
922
|
+
* The callback currently running. If it schedules itself with `immediate`
|
|
923
|
+
* it's left in place, so as before it doesn't run again this step.
|
|
924
|
+
*/
|
|
925
|
+
let running;
|
|
931
926
|
/**
|
|
932
927
|
* A set of processes which were marked keepAlive when scheduled.
|
|
933
928
|
* A keepAlive process is always also held by a frame queue until
|
|
@@ -945,7 +940,7 @@
|
|
|
945
940
|
nextFrame.add(callback);
|
|
946
941
|
runNextFrame();
|
|
947
942
|
}
|
|
948
|
-
callback(latestFrameData);
|
|
943
|
+
(running = callback)(latestFrameData);
|
|
949
944
|
}
|
|
950
945
|
const step = {
|
|
951
946
|
/**
|
|
@@ -956,6 +951,14 @@
|
|
|
956
951
|
const queue = addToCurrentFrame ? thisFrame : nextFrame;
|
|
957
952
|
if (keepAlive)
|
|
958
953
|
toKeepAlive.add(callback);
|
|
954
|
+
/**
|
|
955
|
+
* A callback scheduled with `immediate` during processing may
|
|
956
|
+
* already have run this step (for instance, a value it reads was
|
|
957
|
+
* changed by a later callback). Re-adding moves it to the end of
|
|
958
|
+
* the queue, so it runs again. The running callback is excluded,
|
|
959
|
+
* so one that re-schedules itself can't loop forever.
|
|
960
|
+
*/
|
|
961
|
+
addToCurrentFrame && callback !== running && queue.delete(callback);
|
|
959
962
|
queue.add(callback);
|
|
960
963
|
return callback;
|
|
961
964
|
},
|
|
@@ -990,6 +993,7 @@
|
|
|
990
993
|
// Clear the frame so no callbacks remain. This is to avoid
|
|
991
994
|
// memory leaks should this render step not run for a while.
|
|
992
995
|
thisFrame.clear();
|
|
996
|
+
running = undefined;
|
|
993
997
|
isProcessing = false;
|
|
994
998
|
if (flushNextFrame) {
|
|
995
999
|
flushNextFrame = false;
|
|
@@ -1137,37 +1141,31 @@
|
|
|
1137
1141
|
};
|
|
1138
1142
|
}
|
|
1139
1143
|
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
},
|
|
1158
|
-
// Limits
|
|
1159
|
-
minDuration: 0.01, // in seconds
|
|
1160
|
-
maxDuration: 10.0, // in seconds
|
|
1161
|
-
minDamping: 0.05,
|
|
1162
|
-
};
|
|
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;
|
|
1163
1161
|
/**
|
|
1164
1162
|
* Maps bounce to a damping ratio as SwiftUI does: 0 is critically damped, and
|
|
1165
1163
|
* it approaches undamped towards 1 and infinitely overdamped towards -1. Both
|
|
1166
1164
|
* ends are limited so the spring still moves and settles.
|
|
1167
1165
|
*/
|
|
1168
1166
|
const bounceToDampingRatio = (bounce) => bounce < 0
|
|
1169
|
-
? 1 / Math.max(1 + bounce,
|
|
1170
|
-
: Math.max(1 - bounce,
|
|
1167
|
+
? 1 / Math.max(1 + bounce, minDamping)
|
|
1168
|
+
: Math.max(1 - bounce, minDamping);
|
|
1171
1169
|
/**
|
|
1172
1170
|
* Scales an overdamped spring's undamped frequency so that, at its defining
|
|
1173
1171
|
* time, it has as far left to go as the critically damped spring that's
|
|
@@ -1210,12 +1208,12 @@
|
|
|
1210
1208
|
* ignore inherited velocity and only resolve without valid physics. Returns
|
|
1211
1209
|
* NaN physics when the root search doesn't converge.
|
|
1212
1210
|
*/
|
|
1213
|
-
function findSpring({ duration =
|
|
1211
|
+
function findSpring({ duration = defaultDuration, bounce = defaultBounce, }) {
|
|
1214
1212
|
let envelope;
|
|
1215
1213
|
let derivative;
|
|
1216
|
-
exports.warning(duration <= secondsToMilliseconds(
|
|
1214
|
+
exports.warning(duration <= secondsToMilliseconds(maxDuration), "Spring duration must be 10 seconds or less", "spring-duration-limit");
|
|
1217
1215
|
const dampingRatio = bounceToDampingRatio(bounce);
|
|
1218
|
-
duration = clamp(
|
|
1216
|
+
duration = clamp(minDuration, maxDuration, millisecondsToSeconds(duration));
|
|
1219
1217
|
if (dampingRatio < 1) {
|
|
1220
1218
|
/**
|
|
1221
1219
|
* Underdamped spring
|
|
@@ -1300,9 +1298,9 @@
|
|
|
1300
1298
|
const validMass = resolvePhysics(options.mass);
|
|
1301
1299
|
const springOptions = {
|
|
1302
1300
|
...options,
|
|
1303
|
-
stiffness: validStiffness ??
|
|
1304
|
-
damping: validDamping ??
|
|
1305
|
-
mass: validMass ??
|
|
1301
|
+
stiffness: validStiffness ?? defaultStiffness,
|
|
1302
|
+
damping: validDamping ?? defaultDamping,
|
|
1303
|
+
mass: validMass ?? defaultMass,
|
|
1306
1304
|
isResolvedFromDuration: false,
|
|
1307
1305
|
// stiffness/damping/mass overrides duration/bounce
|
|
1308
1306
|
isTimeDefined: (validStiffness ?? validDamping ?? validMass) === undefined &&
|
|
@@ -1330,13 +1328,13 @@
|
|
|
1330
1328
|
*/
|
|
1331
1329
|
if (!isValidPhysics(springOptions.stiffness) ||
|
|
1332
1330
|
!isValidPhysics(springOptions.damping, true)) {
|
|
1333
|
-
springOptions.stiffness =
|
|
1334
|
-
springOptions.damping =
|
|
1331
|
+
springOptions.stiffness = defaultStiffness;
|
|
1332
|
+
springOptions.damping = defaultDamping;
|
|
1335
1333
|
}
|
|
1336
1334
|
}
|
|
1337
1335
|
return springOptions;
|
|
1338
1336
|
}
|
|
1339
|
-
function
|
|
1337
|
+
function springGenerator(optionsOrVisualDuration = defaultVisualDuration, bounce = defaultBounce) {
|
|
1340
1338
|
const options = typeof optionsOrVisualDuration !== "object"
|
|
1341
1339
|
? {
|
|
1342
1340
|
visualDuration: optionsOrVisualDuration,
|
|
@@ -1386,14 +1384,10 @@
|
|
|
1386
1384
|
const isGranularScale = Math.abs(s.delta) < 5;
|
|
1387
1385
|
s.restSpeed =
|
|
1388
1386
|
options.restSpeed ||
|
|
1389
|
-
(isGranularScale
|
|
1390
|
-
? springDefaults.restSpeed.granular
|
|
1391
|
-
: springDefaults.restSpeed.default);
|
|
1387
|
+
(isGranularScale ? restSpeedGranular : restSpeedDefault);
|
|
1392
1388
|
s.restDelta =
|
|
1393
1389
|
options.restDelta ||
|
|
1394
|
-
(isGranularScale
|
|
1395
|
-
? springDefaults.restDelta.granular
|
|
1396
|
-
: springDefaults.restDelta.default);
|
|
1390
|
+
(isGranularScale ? restDeltaGranular : restDeltaDefault);
|
|
1397
1391
|
};
|
|
1398
1392
|
setRestThresholds();
|
|
1399
1393
|
let resolveSpring;
|
|
@@ -1462,11 +1456,16 @@
|
|
|
1462
1456
|
* dampedAngularFreq * t passes 300, both terms decay at the damping
|
|
1463
1457
|
* rate. Heavily overdamped drag springs (dragElastic: 0,
|
|
1464
1458
|
* dragTransition) rely on this to settle almost at once rather than
|
|
1465
|
-
* creep at stiffness / damping.
|
|
1466
|
-
*
|
|
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.
|
|
1467
1464
|
*/
|
|
1468
|
-
const limit =
|
|
1469
|
-
|
|
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);
|
|
1470
1469
|
const c = { S: 0, F: 0 };
|
|
1471
1470
|
update = () => {
|
|
1472
1471
|
const P = (s.velocity + decay * s.delta) / dampedAngularFreq;
|
|
@@ -1521,13 +1520,15 @@
|
|
|
1521
1520
|
};
|
|
1522
1521
|
return generator;
|
|
1523
1522
|
}
|
|
1524
|
-
spring
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1523
|
+
const spring = /* @__PURE__ */ Object.assign(springGenerator, {
|
|
1524
|
+
applyToOptions: (options) => {
|
|
1525
|
+
const generatorOptions = createGeneratorEasing(options, 100, springGenerator);
|
|
1526
|
+
options.ease = generatorOptions.ease;
|
|
1527
|
+
options.duration = secondsToMilliseconds(generatorOptions.duration);
|
|
1528
|
+
options.type = "keyframes";
|
|
1529
|
+
return options;
|
|
1530
|
+
},
|
|
1531
|
+
});
|
|
1531
1532
|
|
|
1532
1533
|
function inertia({ keyframes, velocity = 0.0, power = 0.8, timeConstant = 325, bounceDamping = 10, bounceStiffness = 500, modifyTarget, min, max, restDelta = 0.5, restSpeed, }) {
|
|
1533
1534
|
const origin = keyframes[0];
|
|
@@ -1545,7 +1546,7 @@
|
|
|
1545
1546
|
};
|
|
1546
1547
|
let amplitude = power * velocity;
|
|
1547
1548
|
const ideal = origin + amplitude;
|
|
1548
|
-
const target = modifyTarget
|
|
1549
|
+
const target = modifyTarget ? modifyTarget(ideal) : ideal;
|
|
1549
1550
|
/**
|
|
1550
1551
|
* If the target has changed we need to re-calculate the amplitude, otherwise
|
|
1551
1552
|
* the animation will start from the wrong position.
|
|
@@ -1556,7 +1557,8 @@
|
|
|
1556
1557
|
const applyFriction = (t) => {
|
|
1557
1558
|
const delta = calcDelta(t);
|
|
1558
1559
|
state.done = Math.abs(delta) <= restDelta;
|
|
1559
|
-
|
|
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;
|
|
1560
1562
|
};
|
|
1561
1563
|
/**
|
|
1562
1564
|
* Ideally this would resolve for t in a stateless way, we could
|
|
@@ -2002,17 +2004,12 @@
|
|
|
2002
2004
|
this.generator = generator;
|
|
2003
2005
|
}
|
|
2004
2006
|
updateTime(timestamp) {
|
|
2005
|
-
|
|
2006
|
-
//
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
// Rounding the time because floating point arithmetic is not always accurate, e.g. 3000.367 - 1000.367 =
|
|
2012
|
-
// 2000.0000000000002. This is a problem when we are comparing the currentTime with the duration, for
|
|
2013
|
-
// example.
|
|
2014
|
-
this.currentTime = animationTime;
|
|
2015
|
-
}
|
|
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;
|
|
2016
2013
|
}
|
|
2017
2014
|
tick(timestamp, sample = false) {
|
|
2018
2015
|
const { generator, totalDuration, calculatedDuration } = this;
|
|
@@ -2053,10 +2050,17 @@
|
|
|
2053
2050
|
}
|
|
2054
2051
|
const state = this.generate(this.currentTime, isInDelayPhase);
|
|
2055
2052
|
let { done } = state;
|
|
2056
|
-
|
|
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) {
|
|
2057
2059
|
done =
|
|
2058
2060
|
this.playbackSpeed >= 0
|
|
2059
|
-
?
|
|
2061
|
+
? calculatedDuration < Infinity
|
|
2062
|
+
? this.currentTime >= totalDuration
|
|
2063
|
+
: done
|
|
2060
2064
|
: this.currentTime <= 0;
|
|
2061
2065
|
}
|
|
2062
2066
|
const isAnimationFinished = this.holdTime === null &&
|
|
@@ -2162,20 +2166,11 @@
|
|
|
2162
2166
|
}
|
|
2163
2167
|
return state;
|
|
2164
2168
|
}
|
|
2165
|
-
/**
|
|
2166
|
-
* Allows the returned animation to be awaited or promise-chained. Currently
|
|
2167
|
-
* resolves when the animation finishes at all but in a future update could/should
|
|
2168
|
-
* reject if its cancels.
|
|
2169
|
-
*/
|
|
2170
|
-
then(resolve, reject) {
|
|
2171
|
-
return this.finished.then(resolve, reject);
|
|
2172
|
-
}
|
|
2173
2169
|
get duration() {
|
|
2174
2170
|
return millisecondsToSeconds(this.calculatedDuration);
|
|
2175
2171
|
}
|
|
2176
2172
|
get iterationDuration() {
|
|
2177
|
-
|
|
2178
|
-
return this.duration + millisecondsToSeconds(delay);
|
|
2173
|
+
return this.duration + millisecondsToSeconds(this.options.delay ?? 0);
|
|
2179
2174
|
}
|
|
2180
2175
|
get time() {
|
|
2181
2176
|
return millisecondsToSeconds(this.currentTime);
|
|
@@ -2279,14 +2274,9 @@
|
|
|
2279
2274
|
}
|
|
2280
2275
|
teardown() {
|
|
2281
2276
|
this.state = "idle";
|
|
2282
|
-
this.
|
|
2283
|
-
this.startTime = this.holdTime = null;
|
|
2284
|
-
}
|
|
2285
|
-
stopDriver() {
|
|
2286
|
-
if (!this.driver)
|
|
2287
|
-
return;
|
|
2288
|
-
this.driver.stop();
|
|
2277
|
+
this.driver?.stop();
|
|
2289
2278
|
this.driver = undefined;
|
|
2279
|
+
this.startTime = this.holdTime = null;
|
|
2290
2280
|
}
|
|
2291
2281
|
sample(sampleTime) {
|
|
2292
2282
|
this.startTime = 0;
|
|
@@ -2543,23 +2533,15 @@
|
|
|
2543
2533
|
if (!transform || transform === "none") {
|
|
2544
2534
|
return defaultTransformValue(name);
|
|
2545
2535
|
}
|
|
2546
|
-
const
|
|
2547
|
-
let parsers;
|
|
2548
|
-
let match;
|
|
2549
|
-
if (matrix3dMatch) {
|
|
2550
|
-
parsers = matrix3dParsers;
|
|
2551
|
-
match = matrix3dMatch;
|
|
2552
|
-
}
|
|
2553
|
-
else {
|
|
2554
|
-
const matrix2dMatch = transform.match(/^matrix\(([-\d.e\s,]+)\)$/u);
|
|
2555
|
-
parsers = matrix2dParsers;
|
|
2556
|
-
match = matrix2dMatch;
|
|
2557
|
-
}
|
|
2536
|
+
const match = transform.match(/^matrix(3d)?\(([-\d.e\s,]+)\)$/u);
|
|
2558
2537
|
if (!match) {
|
|
2559
2538
|
return defaultTransformValue(name);
|
|
2560
2539
|
}
|
|
2561
|
-
const valueParser =
|
|
2562
|
-
|
|
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));
|
|
2563
2545
|
return typeof valueParser === "function"
|
|
2564
2546
|
? valueParser(values)
|
|
2565
2547
|
: values[valueParser];
|
|
@@ -2568,9 +2550,6 @@
|
|
|
2568
2550
|
const { transform = "none" } = getComputedStyle(instance);
|
|
2569
2551
|
return parseValueFromTransform(transform, name);
|
|
2570
2552
|
};
|
|
2571
|
-
function convertTransformToNumber(value) {
|
|
2572
|
-
return parseFloat(value.trim());
|
|
2573
|
-
}
|
|
2574
2553
|
|
|
2575
2554
|
/**
|
|
2576
2555
|
* Generate a list of every possible transform key.
|
|
@@ -2609,7 +2588,7 @@
|
|
|
2609
2588
|
|
|
2610
2589
|
const isNumOrPxType = (v) => v === number || v === px;
|
|
2611
2590
|
const transformKeys = new Set(["x", "y", "z"]);
|
|
2612
|
-
const nonTranslationalTransformKeys = transformPropOrder.filter((key) => !transformKeys.has(key));
|
|
2591
|
+
const nonTranslationalTransformKeys = /* @__PURE__ */ transformPropOrder.filter((key) => !transformKeys.has(key));
|
|
2613
2592
|
/**
|
|
2614
2593
|
* Reset any bounding box-changing transforms so the element can be
|
|
2615
2594
|
* measured. Returns the values to restore. Values already at their
|
|
@@ -2651,6 +2630,8 @@
|
|
|
2651
2630
|
? size
|
|
2652
2631
|
: size - parseFloat(paddingStart) - parseFloat(paddingEnd);
|
|
2653
2632
|
}
|
|
2633
|
+
const transformX = ({ transform }) => parseValueFromTransform(transform, "x");
|
|
2634
|
+
const transformY = ({ transform }) => parseValueFromTransform(transform, "y");
|
|
2654
2635
|
const positionalValues = {
|
|
2655
2636
|
// Dimensions
|
|
2656
2637
|
width: ({ width, paddingLeft = "0", paddingRight = "0", boxSizing }, measureBox) => measureDimension(width, measureBox, "x", paddingLeft, paddingRight, boxSizing),
|
|
@@ -2665,13 +2646,12 @@
|
|
|
2665
2646
|
const { x } = measureBox();
|
|
2666
2647
|
return parseFloat(left) + (x.max - x.min);
|
|
2667
2648
|
},
|
|
2668
|
-
// Transform
|
|
2669
|
-
x:
|
|
2670
|
-
y:
|
|
2649
|
+
// Transform, including translate longform names
|
|
2650
|
+
x: transformX,
|
|
2651
|
+
y: transformY,
|
|
2652
|
+
translateX: transformX,
|
|
2653
|
+
translateY: transformY,
|
|
2671
2654
|
};
|
|
2672
|
-
// Alias translate longform names
|
|
2673
|
-
positionalValues.translateX = positionalValues.x;
|
|
2674
|
-
positionalValues.translateY = positionalValues.y;
|
|
2675
2655
|
|
|
2676
2656
|
const toResolve = new Set();
|
|
2677
2657
|
let isScheduled = false;
|
|
@@ -2972,7 +2952,9 @@
|
|
|
2972
2952
|
this.isPseudoElement = Boolean(pseudoElement);
|
|
2973
2953
|
this.allowFlatten = allowFlatten;
|
|
2974
2954
|
this.options = options;
|
|
2975
|
-
|
|
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");
|
|
2976
2958
|
const transition = applyGeneratorOptions(options);
|
|
2977
2959
|
this.animation = startWaapiAnimation(element, name, keyframes, transition, pseudoElement);
|
|
2978
2960
|
if (transition.autoplay === false) {
|
|
@@ -2982,9 +2964,7 @@
|
|
|
2982
2964
|
this.finishedTime = this.time;
|
|
2983
2965
|
if (!pseudoElement) {
|
|
2984
2966
|
const keyframe = getFinalKeyframe(keyframes, this.options, finalKeyframe, this.speed);
|
|
2985
|
-
|
|
2986
|
-
this.updateMotionValue(keyframe);
|
|
2987
|
-
}
|
|
2967
|
+
this.updateMotionValue?.(keyframe);
|
|
2988
2968
|
/**
|
|
2989
2969
|
* If we can, we want to commit the final style as set by the user,
|
|
2990
2970
|
* rather than the computed keyframe value supplied by the animation.
|
|
@@ -3025,18 +3005,24 @@
|
|
|
3025
3005
|
if (this.isStopped)
|
|
3026
3006
|
return;
|
|
3027
3007
|
this.isStopped = true;
|
|
3028
|
-
|
|
3029
|
-
|
|
3008
|
+
/**
|
|
3009
|
+
* Before its finish event, a finished animation (an instant one is
|
|
3010
|
+
* as soon as it starts) is still showing its end, so it's stopped
|
|
3011
|
+
* like a running one. Otherwise that event would apply its end
|
|
3012
|
+
* over whatever animates the value next.
|
|
3013
|
+
*/
|
|
3014
|
+
if (this.state === "idle" || this.finishedTime !== null)
|
|
3030
3015
|
return;
|
|
3031
|
-
}
|
|
3032
3016
|
if (this.updateMotionValue) {
|
|
3033
3017
|
this.updateMotionValue();
|
|
3034
3018
|
}
|
|
3035
3019
|
else {
|
|
3036
3020
|
this.commitStyles();
|
|
3037
3021
|
}
|
|
3038
|
-
if (!this.isPseudoElement)
|
|
3022
|
+
if (!this.isPseudoElement) {
|
|
3023
|
+
this.animation.onfinish = null;
|
|
3039
3024
|
this.cancel();
|
|
3025
|
+
}
|
|
3040
3026
|
}
|
|
3041
3027
|
/**
|
|
3042
3028
|
* WAAPI doesn't natively have any interruption capabilities.
|
|
@@ -3299,6 +3285,7 @@
|
|
|
3299
3285
|
if (!isOriginAnimatable || !isTargetAnimatable) {
|
|
3300
3286
|
// Only build the message when we're going to warn
|
|
3301
3287
|
isOriginAnimatable !== isTargetAnimatable &&
|
|
3288
|
+
"development" !== "production" &&
|
|
3302
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");
|
|
3303
3290
|
return false;
|
|
3304
3291
|
}
|
|
@@ -3313,13 +3300,16 @@
|
|
|
3313
3300
|
|
|
3314
3301
|
/**
|
|
3315
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.
|
|
3316
3307
|
*/
|
|
3317
3308
|
const acceleratedValues = new Set([
|
|
3318
3309
|
"opacity",
|
|
3319
3310
|
"clipPath",
|
|
3320
3311
|
"filter",
|
|
3321
3312
|
"transform",
|
|
3322
|
-
"backgroundColor",
|
|
3323
3313
|
]);
|
|
3324
3314
|
|
|
3325
3315
|
const browserColorFunctions = /^(?:oklch|oklab|lab|lch|color|color-mix|light-dark)\(/;
|
|
@@ -3345,7 +3335,13 @@
|
|
|
3345
3335
|
"borderBottomColor",
|
|
3346
3336
|
"borderLeftColor",
|
|
3347
3337
|
]);
|
|
3348
|
-
|
|
3338
|
+
/**
|
|
3339
|
+
* WAAPI runs on the browser's clock, so on a manual clock every animation
|
|
3340
|
+
* runs in JS where processFrame can drive it. Set the flag before starting
|
|
3341
|
+
* animations: running WAAPI animations stay on the browser's clock.
|
|
3342
|
+
*/
|
|
3343
|
+
const supportsWaapi = () => !MotionGlobalConfig.useManualTiming &&
|
|
3344
|
+
Object.hasOwnProperty.call(Element.prototype, "animate");
|
|
3349
3345
|
function supportsBrowserAnimation(options) {
|
|
3350
3346
|
const { motionValue, name, repeatDelay, repeatType, damping, type, keyframes, } = options;
|
|
3351
3347
|
/**
|
|
@@ -3405,6 +3401,20 @@
|
|
|
3405
3401
|
transformPerspective: "perspective",
|
|
3406
3402
|
};
|
|
3407
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);
|
|
3408
3418
|
/**
|
|
3409
3419
|
* Build a CSS transform style from individual x/y/scale etc properties.
|
|
3410
3420
|
*
|
|
@@ -3414,7 +3424,6 @@
|
|
|
3414
3424
|
function buildTransform$1(latestValues, transform, transformTemplate) {
|
|
3415
3425
|
// The transform string we're going to build into.
|
|
3416
3426
|
let transformString = "";
|
|
3417
|
-
let transformIsDefault = true;
|
|
3418
3427
|
/**
|
|
3419
3428
|
* Loop over all possible transforms in order, adding the ones that
|
|
3420
3429
|
* are present to the transform string.
|
|
@@ -3424,24 +3433,11 @@
|
|
|
3424
3433
|
const value = latestValues[key];
|
|
3425
3434
|
if (value === undefined)
|
|
3426
3435
|
continue;
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
const parsed = parseFloat(value);
|
|
3433
|
-
valueIsDefault = key.startsWith("scale") ? parsed === 1 : parsed === 0;
|
|
3434
|
-
}
|
|
3435
|
-
if (!valueIsDefault || transformTemplate) {
|
|
3436
|
-
const valueAsType = getValueAsType(value, numberValueTypes[key]);
|
|
3437
|
-
if (!valueIsDefault) {
|
|
3438
|
-
transformIsDefault = false;
|
|
3439
|
-
const transformName = translateAlias[key] || key;
|
|
3440
|
-
transformString += `${transformName}(${valueAsType}) `;
|
|
3441
|
-
}
|
|
3442
|
-
if (transformTemplate) {
|
|
3443
|
-
transform[key] = valueAsType;
|
|
3444
|
-
}
|
|
3436
|
+
const valueAsType = transformTemplate &&
|
|
3437
|
+
(transform[key] = getValueAsType(value, numberValueTypes[key]));
|
|
3438
|
+
if (!isDefaultTransform(key, value)) {
|
|
3439
|
+
transformString +=
|
|
3440
|
+
transformFunction(key, value, valueAsType || undefined) + " ";
|
|
3445
3441
|
}
|
|
3446
3442
|
}
|
|
3447
3443
|
// `pathRotation` composes onto `rotate` as a separate additive term so
|
|
@@ -3449,19 +3445,14 @@
|
|
|
3449
3445
|
// `transformPropOrder`.
|
|
3450
3446
|
const pathRotation = latestValues.pathRotation;
|
|
3451
3447
|
if (pathRotation) {
|
|
3452
|
-
|
|
3453
|
-
transformString += `rotate(${getValueAsType(pathRotation, numberValueTypes.pathRotation)}) `;
|
|
3448
|
+
transformString += transformFunction("rotate", pathRotation);
|
|
3454
3449
|
}
|
|
3455
3450
|
transformString = transformString.trim();
|
|
3456
3451
|
// If we have a custom `transform` template, pass our transform values and
|
|
3457
3452
|
// generated transformString to that before returning
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
else if (transformIsDefault) {
|
|
3462
|
-
transformString = "none";
|
|
3463
|
-
}
|
|
3464
|
-
return transformString;
|
|
3453
|
+
return transformTemplate
|
|
3454
|
+
? transformTemplate(transform, transformString)
|
|
3455
|
+
: transformString || "none";
|
|
3465
3456
|
}
|
|
3466
3457
|
|
|
3467
3458
|
const transformGroups = new WeakMap();
|
|
@@ -3803,27 +3794,30 @@
|
|
|
3803
3794
|
return values;
|
|
3804
3795
|
};
|
|
3805
3796
|
/**
|
|
3806
|
-
* Build
|
|
3807
|
-
* is written, even at its default, so each keyframe has the
|
|
3808
|
-
* 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.
|
|
3809
3801
|
*/
|
|
3810
|
-
const
|
|
3802
|
+
const toTransform = (values, isStyle) => {
|
|
3811
3803
|
let transform = "";
|
|
3812
|
-
const values = valuesAt(timestamp, settled);
|
|
3813
3804
|
for (const key in values) {
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
") ";
|
|
3805
|
+
const value = values[key];
|
|
3806
|
+
if (!isStyle || !isDefaultTransform(key, value)) {
|
|
3807
|
+
transform += transformFunction(key, value) + " ";
|
|
3808
|
+
}
|
|
3819
3809
|
}
|
|
3820
|
-
return transform || "none";
|
|
3810
|
+
return (isStyle ? transform.trim() : transform) || "none";
|
|
3821
3811
|
};
|
|
3812
|
+
/**
|
|
3813
|
+
* Build the transform at a timestamp.
|
|
3814
|
+
*/
|
|
3815
|
+
const compose = (timestamp, settled) => toTransform(valuesAt(timestamp, settled));
|
|
3822
3816
|
/**
|
|
3823
3817
|
* Write the transform as an inline style, as the renderer would.
|
|
3824
3818
|
*/
|
|
3825
3819
|
const commit = (values = valuesAt()) => {
|
|
3826
|
-
element.style.transform =
|
|
3820
|
+
element.style.transform = toTransform(values, true);
|
|
3827
3821
|
};
|
|
3828
3822
|
/**
|
|
3829
3823
|
* Cancel the WAAPI animations, by default first committing the
|
|
@@ -3834,6 +3828,13 @@
|
|
|
3834
3828
|
segments.forEach(({ animation }) => animation.cancel());
|
|
3835
3829
|
segments = [];
|
|
3836
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));
|
|
3837
3838
|
const add = (animation, offset, end = Infinity) => {
|
|
3838
3839
|
const segment = { animation, offset, end };
|
|
3839
3840
|
animation.startTime = time.now() + offset;
|
|
@@ -4026,7 +4027,7 @@
|
|
|
4026
4027
|
commit(valuesAt(from, settled));
|
|
4027
4028
|
keyframes = null;
|
|
4028
4029
|
}
|
|
4029
|
-
return
|
|
4030
|
+
return create(keyframes, {
|
|
4030
4031
|
...options,
|
|
4031
4032
|
duration: to - from,
|
|
4032
4033
|
easing: easing || "linear",
|
|
@@ -4116,9 +4117,7 @@
|
|
|
4116
4117
|
* empty animations that end with them.
|
|
4117
4118
|
*/
|
|
4118
4119
|
ends.forEach((trackEnd) => trackEnd < end - 1 &&
|
|
4119
|
-
add(
|
|
4120
|
-
duration: trackEnd - now,
|
|
4121
|
-
}), 0, trackEnd - now));
|
|
4120
|
+
add(create(null, { duration: trackEnd - now }), 0, trackEnd - now));
|
|
4122
4121
|
}
|
|
4123
4122
|
if (cycle) {
|
|
4124
4123
|
/**
|
|
@@ -4192,12 +4191,29 @@
|
|
|
4192
4191
|
if (isDone)
|
|
4193
4192
|
continue;
|
|
4194
4193
|
const local = elapsed - offset;
|
|
4195
|
-
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) {
|
|
4196
4207
|
animation.playbackRate = rate;
|
|
4197
4208
|
animation.startTime = now - local / rate;
|
|
4198
4209
|
}
|
|
4199
4210
|
else {
|
|
4200
|
-
|
|
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;
|
|
4201
4217
|
animation.currentTime = local;
|
|
4202
4218
|
}
|
|
4203
4219
|
}
|
|
@@ -4388,9 +4404,8 @@
|
|
|
4388
4404
|
* If we can't animate this value with the resolved keyframes
|
|
4389
4405
|
* then we should complete it immediately.
|
|
4390
4406
|
*/
|
|
4391
|
-
|
|
4392
|
-
if (!
|
|
4393
|
-
canAnimateValue = false;
|
|
4407
|
+
const canAnimateValue = canAnimate(keyframes, name, type, velocity);
|
|
4408
|
+
if (!canAnimateValue) {
|
|
4394
4409
|
if (MotionGlobalConfig.instantAnimations || !delay) {
|
|
4395
4410
|
onUpdate?.(getFinalKeyframe(keyframes, options, finalKeyframe));
|
|
4396
4411
|
}
|
|
@@ -4534,9 +4549,7 @@
|
|
|
4534
4549
|
this.animation.complete();
|
|
4535
4550
|
}
|
|
4536
4551
|
cancel() {
|
|
4537
|
-
|
|
4538
|
-
this.animation.cancel();
|
|
4539
|
-
}
|
|
4552
|
+
this._animation?.cancel();
|
|
4540
4553
|
this.keyframeResolver?.cancel();
|
|
4541
4554
|
}
|
|
4542
4555
|
}
|
|
@@ -4719,7 +4732,7 @@
|
|
|
4719
4732
|
this.setCurrent(v);
|
|
4720
4733
|
// Update update subscribers
|
|
4721
4734
|
if (this.current !== this.prev) {
|
|
4722
|
-
this.
|
|
4735
|
+
this.dirty();
|
|
4723
4736
|
if (this.dependents) {
|
|
4724
4737
|
for (const dependent of this.dependents) {
|
|
4725
4738
|
dependent.dirty();
|
|
@@ -4857,12 +4870,9 @@
|
|
|
4857
4870
|
* @public
|
|
4858
4871
|
*/
|
|
4859
4872
|
set(v) {
|
|
4860
|
-
|
|
4861
|
-
this.
|
|
4862
|
-
|
|
4863
|
-
else {
|
|
4864
|
-
this.passiveEffect(v, this.updateAndNotify);
|
|
4865
|
-
}
|
|
4873
|
+
this.passiveEffect
|
|
4874
|
+
? this.passiveEffect(v, this.updateAndNotify)
|
|
4875
|
+
: this.updateAndNotify(v);
|
|
4866
4876
|
}
|
|
4867
4877
|
setWithVelocity(prev, current, delta) {
|
|
4868
4878
|
this.set(current);
|
|
@@ -4879,13 +4889,9 @@
|
|
|
4879
4889
|
this.prev = v;
|
|
4880
4890
|
this.prevUpdatedAt = this.prevFrameValue = undefined;
|
|
4881
4891
|
endAnimation && this.stop();
|
|
4882
|
-
|
|
4883
|
-
this.stopPassiveEffect();
|
|
4892
|
+
this.stopPassiveEffect?.();
|
|
4884
4893
|
}
|
|
4885
4894
|
dirty() {
|
|
4886
|
-
this.notifyChange();
|
|
4887
|
-
}
|
|
4888
|
-
notifyChange() {
|
|
4889
4895
|
const { current, changeSubscriber } = this;
|
|
4890
4896
|
/**
|
|
4891
4897
|
* One or the other exists. Subscribing during the direct
|
|
@@ -4900,15 +4906,10 @@
|
|
|
4900
4906
|
}
|
|
4901
4907
|
}
|
|
4902
4908
|
addDependent(dependent) {
|
|
4903
|
-
|
|
4904
|
-
this.dependents = new Set();
|
|
4905
|
-
}
|
|
4906
|
-
this.dependents.add(dependent);
|
|
4909
|
+
(this.dependents || (this.dependents = new Set())).add(dependent);
|
|
4907
4910
|
}
|
|
4908
4911
|
removeDependent(dependent) {
|
|
4909
|
-
|
|
4910
|
-
this.dependents.delete(dependent);
|
|
4911
|
-
}
|
|
4912
|
+
this.dependents?.delete(dependent);
|
|
4912
4913
|
}
|
|
4913
4914
|
/**
|
|
4914
4915
|
* Returns the latest state of `MotionValue`
|
|
@@ -4918,9 +4919,7 @@
|
|
|
4918
4919
|
* @public
|
|
4919
4920
|
*/
|
|
4920
4921
|
get() {
|
|
4921
|
-
|
|
4922
|
-
collectMotionValues.current.push(this);
|
|
4923
|
-
}
|
|
4922
|
+
collectMotionValues.current?.push(this);
|
|
4924
4923
|
/**
|
|
4925
4924
|
* A value animating on the compositor isn't updated every frame,
|
|
4926
4925
|
* so read it from its animation.
|
|
@@ -4986,7 +4985,7 @@
|
|
|
4986
4985
|
isComplete = true;
|
|
4987
4986
|
this.events.animationComplete?.notify();
|
|
4988
4987
|
if (this.animation === animation)
|
|
4989
|
-
this.
|
|
4988
|
+
this.animation = undefined;
|
|
4990
4989
|
resolve();
|
|
4991
4990
|
});
|
|
4992
4991
|
if (!isComplete)
|
|
@@ -5002,11 +5001,10 @@
|
|
|
5002
5001
|
stop() {
|
|
5003
5002
|
if (this.animation) {
|
|
5004
5003
|
this.animation.stop();
|
|
5005
|
-
|
|
5006
|
-
this.events.animationCancel.notify();
|
|
5007
|
-
}
|
|
5004
|
+
this.events.animationCancel?.notify();
|
|
5008
5005
|
}
|
|
5009
|
-
|
|
5006
|
+
// Assign rather than delete to keep the object's shape stable
|
|
5007
|
+
this.animation = undefined;
|
|
5010
5008
|
}
|
|
5011
5009
|
/**
|
|
5012
5010
|
* Returns `true` if this value is currently animating.
|
|
@@ -5016,10 +5014,6 @@
|
|
|
5016
5014
|
isAnimating() {
|
|
5017
5015
|
return !!this.animation;
|
|
5018
5016
|
}
|
|
5019
|
-
clearAnimation() {
|
|
5020
|
-
// Assign rather than delete to keep the object's shape stable
|
|
5021
|
-
this.animation = undefined;
|
|
5022
|
-
}
|
|
5023
5017
|
/**
|
|
5024
5018
|
* Destroy and clean up subscribers to this `MotionValue`.
|
|
5025
5019
|
*
|
|
@@ -5034,9 +5028,7 @@
|
|
|
5034
5028
|
this.events.destroy?.notify();
|
|
5035
5029
|
this.clearListeners();
|
|
5036
5030
|
this.stop();
|
|
5037
|
-
|
|
5038
|
-
this.stopPassiveEffect();
|
|
5039
|
-
}
|
|
5031
|
+
this.stopPassiveEffect?.();
|
|
5040
5032
|
}
|
|
5041
5033
|
}
|
|
5042
5034
|
function motionValue(init, options) {
|
|
@@ -5114,6 +5106,7 @@
|
|
|
5114
5106
|
"repeatDelay",
|
|
5115
5107
|
"from",
|
|
5116
5108
|
"elapsed",
|
|
5109
|
+
"out",
|
|
5117
5110
|
]);
|
|
5118
5111
|
/**
|
|
5119
5112
|
* Decide whether a transition is defined on a given Transition.
|
|
@@ -5536,12 +5529,8 @@
|
|
|
5536
5529
|
}
|
|
5537
5530
|
}
|
|
5538
5531
|
|
|
5539
|
-
function camelToDash(str) {
|
|
5540
|
-
return str.replace(/([A-Z])/g, (match) => `-${match.toLowerCase()}`);
|
|
5541
|
-
}
|
|
5542
|
-
|
|
5543
5532
|
const optimizedAppearDataId = "framerAppearId";
|
|
5544
|
-
const optimizedAppearDataAttribute = "data-"
|
|
5533
|
+
const optimizedAppearDataAttribute = "data-framer-appear-id";
|
|
5545
5534
|
|
|
5546
5535
|
function getOptimisedAppearId(visualElement) {
|
|
5547
5536
|
return visualElement.props[optimizedAppearDataAttribute];
|
|
@@ -5588,7 +5577,7 @@
|
|
|
5588
5577
|
}
|
|
5589
5578
|
const valueTransition = {
|
|
5590
5579
|
delay,
|
|
5591
|
-
...getValueTransition$1(transition || {}, key),
|
|
5580
|
+
...getValueTransition$1(animationTypeState?.out[key] || transition || {}, key),
|
|
5592
5581
|
};
|
|
5593
5582
|
if (skipAnimations)
|
|
5594
5583
|
valueTransition.skipAnimations = true;
|
|
@@ -5758,8 +5747,9 @@
|
|
|
5758
5747
|
}
|
|
5759
5748
|
|
|
5760
5749
|
class DOMKeyframesResolver extends KeyframeResolver {
|
|
5761
|
-
constructor(
|
|
5762
|
-
super(
|
|
5750
|
+
constructor() {
|
|
5751
|
+
super(...arguments);
|
|
5752
|
+
this.isAsync = true;
|
|
5763
5753
|
}
|
|
5764
5754
|
/**
|
|
5765
5755
|
* Keyframes that don't need reading from the DOM, or measuring,
|
|
@@ -5787,7 +5777,7 @@
|
|
|
5787
5777
|
}
|
|
5788
5778
|
readKeyframes() {
|
|
5789
5779
|
const { unresolvedKeyframes, element, name } = this;
|
|
5790
|
-
if (!element
|
|
5780
|
+
if (!element?.current)
|
|
5791
5781
|
return;
|
|
5792
5782
|
super.readKeyframes();
|
|
5793
5783
|
/**
|
|
@@ -5882,11 +5872,10 @@
|
|
|
5882
5872
|
}
|
|
5883
5873
|
measureInitialState() {
|
|
5884
5874
|
const { element, unresolvedKeyframes, name } = this;
|
|
5885
|
-
if (!element
|
|
5875
|
+
if (!element?.current)
|
|
5886
5876
|
return;
|
|
5887
|
-
if (name === "height")
|
|
5877
|
+
if (name === "height")
|
|
5888
5878
|
this.suspendedScrollY = window.pageYOffset;
|
|
5889
|
-
}
|
|
5890
5879
|
this.measuredOrigin = this.measure();
|
|
5891
5880
|
unresolvedKeyframes[0] = this.measuredOrigin;
|
|
5892
5881
|
// Set final key frame to measure after next render
|
|
@@ -5897,7 +5886,7 @@
|
|
|
5897
5886
|
}
|
|
5898
5887
|
measureEndState() {
|
|
5899
5888
|
const { element, unresolvedKeyframes } = this;
|
|
5900
|
-
if (!element
|
|
5889
|
+
if (!element?.current)
|
|
5901
5890
|
return;
|
|
5902
5891
|
this.motionValue?.jump(this.measuredOrigin, false);
|
|
5903
5892
|
const finalKeyframeIndex = unresolvedKeyframes.length - 1;
|
|
@@ -5906,14 +5895,6 @@
|
|
|
5906
5895
|
if (finalKeyframe !== null && this.finalKeyframe === undefined) {
|
|
5907
5896
|
this.finalKeyframe = finalKeyframe;
|
|
5908
5897
|
}
|
|
5909
|
-
// If we removed transform values, reapply them before the next render
|
|
5910
|
-
if (this.removedTransforms?.length) {
|
|
5911
|
-
this.removedTransforms.forEach(([unsetTransformName, unsetTransformValue]) => {
|
|
5912
|
-
element
|
|
5913
|
-
.getValue(unsetTransformName)
|
|
5914
|
-
.set(unsetTransformValue);
|
|
5915
|
-
});
|
|
5916
|
-
}
|
|
5917
5898
|
this.resolveNoneKeyframes();
|
|
5918
5899
|
}
|
|
5919
5900
|
}
|
|
@@ -6018,7 +5999,8 @@
|
|
|
6018
5999
|
*/
|
|
6019
6000
|
const effects = [];
|
|
6020
6001
|
function addEffect(effect) {
|
|
6021
|
-
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");
|
|
6022
6004
|
removeEffect(effect);
|
|
6023
6005
|
effects.unshift(effect);
|
|
6024
6006
|
}
|
|
@@ -6345,11 +6327,6 @@
|
|
|
6345
6327
|
});
|
|
6346
6328
|
}
|
|
6347
6329
|
|
|
6348
|
-
/**
|
|
6349
|
-
* Cache of `translateX(`-style openers so each render is a few string
|
|
6350
|
-
* concatenations rather than a template per transform.
|
|
6351
|
-
*/
|
|
6352
|
-
const openers = {};
|
|
6353
6330
|
function buildTransform(state) {
|
|
6354
6331
|
let transform = "";
|
|
6355
6332
|
const { transformKeys: keys = [], transformValues: values = {} } = state;
|
|
@@ -6362,14 +6339,8 @@
|
|
|
6362
6339
|
const value = values[key].getCurrent();
|
|
6363
6340
|
if (value === undefined)
|
|
6364
6341
|
continue;
|
|
6365
|
-
|
|
6366
|
-
|
|
6367
|
-
transform +=
|
|
6368
|
-
(transform && " ") +
|
|
6369
|
-
(openers[key] ||
|
|
6370
|
-
(openers[key] = (translateAlias[key] || key) + "(")) +
|
|
6371
|
-
getValueAsType(value, transformValueTypes[key]) +
|
|
6372
|
-
")";
|
|
6342
|
+
if (!isDefaultTransform(key, value)) {
|
|
6343
|
+
transform += (transform && " ") + transformFunction(key, value);
|
|
6373
6344
|
}
|
|
6374
6345
|
}
|
|
6375
6346
|
// See build-transform.ts: additive `rotate()` so user `rotate` isn't
|
|
@@ -6377,10 +6348,7 @@
|
|
|
6377
6348
|
const pathRotation = state.get("pathRotation")?.get();
|
|
6378
6349
|
if (pathRotation) {
|
|
6379
6350
|
transform +=
|
|
6380
|
-
(transform && " ") +
|
|
6381
|
-
"rotate(" +
|
|
6382
|
-
getValueAsType(pathRotation, transformValueTypes.pathRotation) +
|
|
6383
|
-
")";
|
|
6351
|
+
(transform && " ") + transformFunction("rotate", pathRotation);
|
|
6384
6352
|
}
|
|
6385
6353
|
return transform || "none";
|
|
6386
6354
|
}
|
|
@@ -6469,6 +6437,10 @@
|
|
|
6469
6437
|
const styleEffect =
|
|
6470
6438
|
/*@__PURE__*/ createSelectorEffect(styleSubjectEffect);
|
|
6471
6439
|
|
|
6440
|
+
function camelToDash(str) {
|
|
6441
|
+
return str.replace(/([A-Z])/g, (match) => `-${match.toLowerCase()}`);
|
|
6442
|
+
}
|
|
6443
|
+
|
|
6472
6444
|
function buildHTMLStyles(state, latestValues, transformTemplate) {
|
|
6473
6445
|
const { style, vars, transformOrigin } = state;
|
|
6474
6446
|
// Track whether we encounter any transform or transformOrigin values.
|
|
@@ -6779,10 +6751,24 @@
|
|
|
6779
6751
|
!isIdentityScale(scaleX) ||
|
|
6780
6752
|
!isIdentityScale(scaleY));
|
|
6781
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
|
+
];
|
|
6782
6765
|
function hasTransform(values) {
|
|
6783
6766
|
return (hasScale(values) ||
|
|
6784
6767
|
has2DTranslate(values) ||
|
|
6785
|
-
values
|
|
6768
|
+
hasDistortingTransform(values));
|
|
6769
|
+
}
|
|
6770
|
+
function hasDistortingTransform(values) {
|
|
6771
|
+
return (values.z ||
|
|
6786
6772
|
values.rotate ||
|
|
6787
6773
|
values.rotateX ||
|
|
6788
6774
|
values.rotateY ||
|
|
@@ -6835,6 +6821,11 @@
|
|
|
6835
6821
|
* in a tree upon our box before then calculating how to project it into our desired viewport-relative box
|
|
6836
6822
|
*
|
|
6837
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.
|
|
6838
6829
|
*/
|
|
6839
6830
|
function applyTreeDeltas(box, treeScale, treePath, isSharedTransition = false) {
|
|
6840
6831
|
const treeLength = treePath.length;
|
|
@@ -6851,13 +6842,10 @@
|
|
|
6851
6842
|
* TODO: Prefer to remove this, but currently we have motion components with
|
|
6852
6843
|
* display: contents in Framer.
|
|
6853
6844
|
*/
|
|
6854
|
-
|
|
6855
|
-
if (visualElement &&
|
|
6856
|
-
visualElement.props.style &&
|
|
6857
|
-
visualElement.props.style.display === "contents") {
|
|
6845
|
+
if (node.options.visualElement?.props.style?.display === "contents") {
|
|
6858
6846
|
continue;
|
|
6859
6847
|
}
|
|
6860
|
-
if (isSharedTransition &&
|
|
6848
|
+
if (isSharedTransition === true &&
|
|
6861
6849
|
node.options.layoutScroll &&
|
|
6862
6850
|
node.scroll &&
|
|
6863
6851
|
node !== node.root) {
|
|
@@ -6871,7 +6859,9 @@
|
|
|
6871
6859
|
// Apply each ancestor's calculated delta into this component's recorded layout box
|
|
6872
6860
|
applyBoxDelta(box, delta);
|
|
6873
6861
|
}
|
|
6874
|
-
if (isSharedTransition &&
|
|
6862
|
+
if (isSharedTransition &&
|
|
6863
|
+
(isSharedTransition.path?.[i] || isSharedTransition) !== node &&
|
|
6864
|
+
hasTransform(node.latestValues)) {
|
|
6875
6865
|
transformBox(box, node.latestValues, node.layout?.layoutBox);
|
|
6876
6866
|
}
|
|
6877
6867
|
}
|
|
@@ -7044,6 +7034,41 @@
|
|
|
7044
7034
|
const { schedule: microtask, cancel: cancelMicrotask } =
|
|
7045
7035
|
/* @__PURE__ */ createRenderBatcher(queueMicrotask, false);
|
|
7046
7036
|
|
|
7037
|
+
/**
|
|
7038
|
+
* Process a frame now, for driving Motion from a loop other than
|
|
7039
|
+
* window.requestAnimationFrame, like an XRSession's during an immersive
|
|
7040
|
+
* WebXR session. Wherever window.requestAnimationFrame does fire, Motion
|
|
7041
|
+
* keeps processing frames from it too.
|
|
7042
|
+
*
|
|
7043
|
+
* `timestamp` defaults to now. For a timeline you control, set
|
|
7044
|
+
* `MotionGlobalConfig.useManualTiming = true` and pass each frame's time in
|
|
7045
|
+
* milliseconds.
|
|
7046
|
+
*
|
|
7047
|
+
* Animations Motion hands to the browser (WAAPI), like `opacity` or
|
|
7048
|
+
* `transform` on an HTML or SVG element, follow the browser's clock. With
|
|
7049
|
+
* `useManualTiming` on, Motion runs those in JS instead, except for
|
|
7050
|
+
* `motion/mini`, view transitions and optimized appear animations, and
|
|
7051
|
+
* colours only the browser can mix, like `oklch()`, which jump to their end.
|
|
7052
|
+
*
|
|
7053
|
+
* Ignored when called inside a frame.
|
|
7054
|
+
*/
|
|
7055
|
+
const processFrame = (timestamp = time.now()) => {
|
|
7056
|
+
if (frameData.isProcessing)
|
|
7057
|
+
return;
|
|
7058
|
+
const delta = timestamp - frameData.timestamp;
|
|
7059
|
+
// Clamp like the requestAnimationFrame loop, unless the timeline is ours
|
|
7060
|
+
frameData.delta = MotionGlobalConfig.useManualTiming
|
|
7061
|
+
? delta
|
|
7062
|
+
: Math.max(Math.min(delta, 40), 1);
|
|
7063
|
+
time.set((frameData.timestamp = timestamp));
|
|
7064
|
+
frameData.isProcessing = true;
|
|
7065
|
+
// frameSteps' keys are in stepsOrder
|
|
7066
|
+
for (const step in frameSteps) {
|
|
7067
|
+
frameSteps[step].process(frameData);
|
|
7068
|
+
}
|
|
7069
|
+
frameData.isProcessing = false;
|
|
7070
|
+
};
|
|
7071
|
+
|
|
7047
7072
|
function animateSingleValue(value, keyframes, options) {
|
|
7048
7073
|
const motionValue$1 = isMotionValue(value) ? value : motionValue(value);
|
|
7049
7074
|
motionValue$1.start(animateMotionValue("", motionValue$1, keyframes, options));
|
|
@@ -7103,15 +7128,6 @@
|
|
|
7103
7128
|
const asNumber = (v) => typeof v === "number" ? v : parseFloat(v);
|
|
7104
7129
|
|
|
7105
7130
|
const numBorders = cornerRadiusProps.length;
|
|
7106
|
-
const mixedTransforms = [
|
|
7107
|
-
"rotate",
|
|
7108
|
-
"rotateX",
|
|
7109
|
-
"rotateY",
|
|
7110
|
-
"rotateZ",
|
|
7111
|
-
"skewX",
|
|
7112
|
-
"skewY",
|
|
7113
|
-
];
|
|
7114
|
-
const numMixedTransforms = mixedTransforms.length;
|
|
7115
7131
|
const isPx = (value) => typeof value === "number" || px.test(value);
|
|
7116
7132
|
function mixValues(target, follow, lead, progress, shouldCrossfadeOpacity, isOnlyMember) {
|
|
7117
7133
|
if (shouldCrossfadeOpacity) {
|
|
@@ -7149,8 +7165,8 @@
|
|
|
7149
7165
|
* Mix rotation and skew. These can't be projected, so mix them every
|
|
7150
7166
|
* frame rather than leaving them frozen at the animation-start snapshot.
|
|
7151
7167
|
*/
|
|
7152
|
-
for (let i = 0; i <
|
|
7153
|
-
const key =
|
|
7168
|
+
for (let i = 0; i < angleTransforms.length; i++) {
|
|
7169
|
+
const key = angleTransforms[i];
|
|
7154
7170
|
if (follow[key] || lead[key]) {
|
|
7155
7171
|
target[key] = mixNumber$1(follow[key] || 0, lead[key] || 0, progress);
|
|
7156
7172
|
}
|
|
@@ -7233,8 +7249,8 @@
|
|
|
7233
7249
|
}
|
|
7234
7250
|
}
|
|
7235
7251
|
function calcBoxDelta(delta, source, target, origin) {
|
|
7236
|
-
calcAxisDelta(delta.x, source.x, target.x, origin
|
|
7237
|
-
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);
|
|
7238
7254
|
}
|
|
7239
7255
|
function calcRelativeAxis(target, relative, parent, anchor = 0) {
|
|
7240
7256
|
const anchorPoint = anchor
|
|
@@ -7304,8 +7320,8 @@
|
|
|
7304
7320
|
* and acts as a bridge between motion values and removeAxisDelta
|
|
7305
7321
|
*/
|
|
7306
7322
|
function removeBoxTransforms(box, transforms, originBox, sourceBox) {
|
|
7307
|
-
removeAxisTransforms(box.x, transforms, xKeys, originBox
|
|
7308
|
-
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);
|
|
7309
7325
|
}
|
|
7310
7326
|
|
|
7311
7327
|
const createAxisDelta = () => ({
|
|
@@ -7503,10 +7519,9 @@
|
|
|
7503
7519
|
};
|
|
7504
7520
|
|
|
7505
7521
|
const defaultScaleCorrectors = {
|
|
7506
|
-
borderRadius: {
|
|
7507
|
-
|
|
7508
|
-
|
|
7509
|
-
},
|
|
7522
|
+
borderRadius: /* @__PURE__ */ Object.assign({}, correctBorderRadius, {
|
|
7523
|
+
applyTo: /* @__PURE__ */ cornerRadiusProps.slice(),
|
|
7524
|
+
}),
|
|
7510
7525
|
borderTopLeftRadius: correctBorderRadius,
|
|
7511
7526
|
borderTopRightRadius: correctBorderRadius,
|
|
7512
7527
|
borderBottomLeftRadius: correctBorderRadius,
|
|
@@ -7536,24 +7551,18 @@
|
|
|
7536
7551
|
transform += `scale(${1 / treeScale.x}, ${1 / treeScale.y}) `;
|
|
7537
7552
|
}
|
|
7538
7553
|
if (latestTransform) {
|
|
7539
|
-
const { transformPerspective
|
|
7554
|
+
const { transformPerspective } = latestTransform;
|
|
7540
7555
|
if (transformPerspective)
|
|
7541
7556
|
transform = `perspective(${transformPerspective}px) ${transform}`;
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
|
|
7545
|
-
|
|
7546
|
-
|
|
7547
|
-
|
|
7548
|
-
|
|
7549
|
-
|
|
7550
|
-
|
|
7551
|
-
if (rotateZ)
|
|
7552
|
-
transform += `rotateZ(${rotateZ}deg) `;
|
|
7553
|
-
if (skewX)
|
|
7554
|
-
transform += `skewX(${skewX}deg) `;
|
|
7555
|
-
if (skewY)
|
|
7556
|
-
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
|
+
}
|
|
7557
7566
|
}
|
|
7558
7567
|
/**
|
|
7559
7568
|
* Apply scale to match the size of the element to the size we want it.
|
|
@@ -7611,20 +7620,15 @@
|
|
|
7611
7620
|
hasEverUpdated: false,
|
|
7612
7621
|
};
|
|
7613
7622
|
|
|
7614
|
-
/**
|
|
7615
|
-
* Correctors registered via addScaleCorrector before projection loaded
|
|
7616
|
-
* (e.g. with an async LazyMotion bundle) take precedence over the defaults.
|
|
7617
|
-
*/
|
|
7618
|
-
Object.assign(scaleCorrectors, {
|
|
7619
|
-
...defaultScaleCorrectors,
|
|
7620
|
-
...scaleCorrectors,
|
|
7621
|
-
});
|
|
7622
7623
|
const metrics = {
|
|
7623
7624
|
nodes: 0,
|
|
7624
7625
|
calculatedTargetDeltas: 0,
|
|
7625
7626
|
calculatedProjections: 0,
|
|
7626
7627
|
};
|
|
7627
|
-
|
|
7628
|
+
/**
|
|
7629
|
+
* Transforms that distort an element's layout, so must be reset before measuring.
|
|
7630
|
+
*/
|
|
7631
|
+
const distortingTransforms = ["z", "skew", "skewZ"];
|
|
7628
7632
|
/**
|
|
7629
7633
|
* We use 1000 as the animation target as 0-1000 maps better to pixels than 0-1
|
|
7630
7634
|
* which has a noticeable difference in spring animations
|
|
@@ -7661,6 +7665,16 @@
|
|
|
7661
7665
|
}
|
|
7662
7666
|
}
|
|
7663
7667
|
function createProjectionNode({ attachResizeListener, defaultParent, measureScroll, checkIsScrollRoot, resetTransform, }) {
|
|
7668
|
+
/**
|
|
7669
|
+
* Correctors registered via addScaleCorrector before projection loaded
|
|
7670
|
+
* (e.g. with an async LazyMotion bundle) take precedence over the defaults.
|
|
7671
|
+
* Applied here rather than at module scope so bundlers can drop projection
|
|
7672
|
+
* when it's unused.
|
|
7673
|
+
*/
|
|
7674
|
+
Object.assign(scaleCorrectors, {
|
|
7675
|
+
...defaultScaleCorrectors,
|
|
7676
|
+
...scaleCorrectors,
|
|
7677
|
+
});
|
|
7664
7678
|
return class ProjectionNode {
|
|
7665
7679
|
constructor(latestValues = {}, parent = defaultParent?.()) {
|
|
7666
7680
|
/**
|
|
@@ -7822,8 +7836,7 @@
|
|
|
7822
7836
|
return this.eventHandlers.get(name).add(handler);
|
|
7823
7837
|
}
|
|
7824
7838
|
notifyListeners(name, ...args) {
|
|
7825
|
-
|
|
7826
|
-
subscriptionManager && subscriptionManager.notify(...args);
|
|
7839
|
+
this.eventHandlers.get(name)?.notify(...args);
|
|
7827
7840
|
}
|
|
7828
7841
|
hasListeners(name) {
|
|
7829
7842
|
return this.eventHandlers.has(name);
|
|
@@ -7841,7 +7854,7 @@
|
|
|
7841
7854
|
visualElement.mount(instance);
|
|
7842
7855
|
}
|
|
7843
7856
|
this.root.nodes.add(this);
|
|
7844
|
-
this.parent
|
|
7857
|
+
this.parent?.children.add(this);
|
|
7845
7858
|
if (this.root.hasTreeAnimated && (layout || layoutId)) {
|
|
7846
7859
|
this.isLayoutDirty = true;
|
|
7847
7860
|
}
|
|
@@ -7859,7 +7872,7 @@
|
|
|
7859
7872
|
return;
|
|
7860
7873
|
innerWidth = newInnerWidth;
|
|
7861
7874
|
this.root.updateBlockedByResize = true;
|
|
7862
|
-
cancelDelay
|
|
7875
|
+
cancelDelay?.();
|
|
7863
7876
|
cancelDelay = delay(resizeUnblockUpdate, 250);
|
|
7864
7877
|
if (globalProjectionState.hasAnimatedSinceResize) {
|
|
7865
7878
|
globalProjectionState.hasAnimatedSinceResize = false;
|
|
@@ -7950,9 +7963,7 @@
|
|
|
7950
7963
|
if (!hasLayoutChanged) {
|
|
7951
7964
|
finishAnimation(this);
|
|
7952
7965
|
}
|
|
7953
|
-
|
|
7954
|
-
this.options.onExitComplete();
|
|
7955
|
-
}
|
|
7966
|
+
this.isLead() && this.options.onExitComplete?.();
|
|
7956
7967
|
}
|
|
7957
7968
|
this.targetLayout = newLayout;
|
|
7958
7969
|
});
|
|
@@ -7960,9 +7971,8 @@
|
|
|
7960
7971
|
unmount() {
|
|
7961
7972
|
this.options.layoutId && this.willUpdate();
|
|
7962
7973
|
this.root.nodes.remove(this);
|
|
7963
|
-
|
|
7964
|
-
|
|
7965
|
-
this.parent && this.parent.children.delete(this);
|
|
7974
|
+
this.getStack()?.remove(this);
|
|
7975
|
+
this.parent?.children.delete(this);
|
|
7966
7976
|
this.instance = undefined;
|
|
7967
7977
|
this.eventHandlers.clear();
|
|
7968
7978
|
this.hasLayoutListener = false;
|
|
@@ -7983,7 +7993,7 @@
|
|
|
7983
7993
|
if (this.isUpdateBlocked())
|
|
7984
7994
|
return;
|
|
7985
7995
|
this.isUpdating = true;
|
|
7986
|
-
this.nodes
|
|
7996
|
+
this.nodes?.forEach(resetSkewAndRotation);
|
|
7987
7997
|
this.animationId++;
|
|
7988
7998
|
}
|
|
7989
7999
|
getTransformTemplate() {
|
|
@@ -7993,7 +8003,7 @@
|
|
|
7993
8003
|
willUpdate(shouldNotifyListeners = true) {
|
|
7994
8004
|
this.root.hasTreeAnimated = true;
|
|
7995
8005
|
if (this.root.isUpdateBlocked()) {
|
|
7996
|
-
this.options.onExitComplete
|
|
8006
|
+
this.options.onExitComplete?.();
|
|
7997
8007
|
return;
|
|
7998
8008
|
}
|
|
7999
8009
|
/**
|
|
@@ -8210,24 +8220,23 @@
|
|
|
8210
8220
|
this.projectionDelta = undefined;
|
|
8211
8221
|
this.notifyListeners("measure", this.layout.layoutBox);
|
|
8212
8222
|
const { visualElement } = this.options;
|
|
8213
|
-
visualElement
|
|
8214
|
-
visualElement.notify("LayoutMeasure", this.layout.layoutBox, prevLayout ? prevLayout.layoutBox : undefined);
|
|
8223
|
+
visualElement?.notify("LayoutMeasure", this.layout.layoutBox, prevLayout?.layoutBox);
|
|
8215
8224
|
}
|
|
8216
8225
|
updateScroll(phase = "measure") {
|
|
8217
|
-
|
|
8218
|
-
|
|
8219
|
-
|
|
8220
|
-
|
|
8221
|
-
|
|
8222
|
-
|
|
8223
|
-
|
|
8224
|
-
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);
|
|
8225
8234
|
this.scroll = {
|
|
8226
|
-
animationId
|
|
8235
|
+
animationId,
|
|
8227
8236
|
phase,
|
|
8228
8237
|
isRoot,
|
|
8229
|
-
offset: measureScroll(
|
|
8230
|
-
wasRoot:
|
|
8238
|
+
offset: measureScroll(instance),
|
|
8239
|
+
wasRoot: scroll ? scroll.isRoot : isRoot,
|
|
8231
8240
|
};
|
|
8232
8241
|
}
|
|
8233
8242
|
}
|
|
@@ -8317,10 +8326,10 @@
|
|
|
8317
8326
|
}
|
|
8318
8327
|
return boxWithoutScroll;
|
|
8319
8328
|
}
|
|
8320
|
-
applyTransform(box, transformOnly = false, output) {
|
|
8329
|
+
applyTransform(box, transformOnly = false, output, skipOwnTransform, start = 0) {
|
|
8321
8330
|
const withTransforms = output || createBox();
|
|
8322
8331
|
copyBoxInto(withTransforms, box);
|
|
8323
|
-
for (let i =
|
|
8332
|
+
for (let i = start; i < this.path.length; i++) {
|
|
8324
8333
|
const node = this.path[i];
|
|
8325
8334
|
if (!transformOnly &&
|
|
8326
8335
|
node.options.layoutScroll &&
|
|
@@ -8333,7 +8342,7 @@
|
|
|
8333
8342
|
continue;
|
|
8334
8343
|
transformBox(withTransforms, node.latestValues, node.layout?.layoutBox);
|
|
8335
8344
|
}
|
|
8336
|
-
if (hasTransform(this.latestValues)) {
|
|
8345
|
+
if (!skipOwnTransform && hasTransform(this.latestValues)) {
|
|
8337
8346
|
transformBox(withTransforms, this.latestValues, this.layout?.layoutBox);
|
|
8338
8347
|
}
|
|
8339
8348
|
return withTransforms;
|
|
@@ -8468,7 +8477,11 @@
|
|
|
8468
8477
|
}
|
|
8469
8478
|
else if (this.targetDelta) {
|
|
8470
8479
|
if (Boolean(this.resumingFrom)) {
|
|
8471
|
-
|
|
8480
|
+
/**
|
|
8481
|
+
* Own transforms are applied later, in applyTransformsToTarget,
|
|
8482
|
+
* so only apply ancestor transforms here or they'd be doubled.
|
|
8483
|
+
*/
|
|
8484
|
+
this.applyTransform(this.layout.layoutBox, false, this.target, true);
|
|
8472
8485
|
}
|
|
8473
8486
|
else {
|
|
8474
8487
|
copyBoxInto(this.target, this.layout.layoutBox);
|
|
@@ -8488,8 +8501,13 @@
|
|
|
8488
8501
|
this.attemptToResolveRelativeTarget = false;
|
|
8489
8502
|
if (this.options.layoutAnchor !== false &&
|
|
8490
8503
|
relativeParent &&
|
|
8491
|
-
|
|
8492
|
-
|
|
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)) &&
|
|
8493
8511
|
!relativeParent.options.layoutScroll &&
|
|
8494
8512
|
relativeParent.target &&
|
|
8495
8513
|
this.animationProgress !== 1) {
|
|
@@ -8569,7 +8587,7 @@
|
|
|
8569
8587
|
* If this section of the tree isn't animating we can
|
|
8570
8588
|
* delete our target sources for the following frame.
|
|
8571
8589
|
*/
|
|
8572
|
-
this.isTreeAnimating = Boolean(
|
|
8590
|
+
this.isTreeAnimating = Boolean(this.parent?.isTreeAnimating ||
|
|
8573
8591
|
this.currentAnimation ||
|
|
8574
8592
|
this.pendingAnimation);
|
|
8575
8593
|
if (!this.isTreeAnimating) {
|
|
@@ -8590,8 +8608,17 @@
|
|
|
8590
8608
|
/**
|
|
8591
8609
|
* Apply all the parent deltas to this box to produce the corrected box. This
|
|
8592
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.
|
|
8593
8617
|
*/
|
|
8594
|
-
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));
|
|
8595
8622
|
/**
|
|
8596
8623
|
* If this layer needs to perform scale correction but doesn't have a target,
|
|
8597
8624
|
* use the layout as the target.
|
|
@@ -8658,8 +8685,7 @@
|
|
|
8658
8685
|
scheduleRender(notifyAll = true) {
|
|
8659
8686
|
this.options.visualElement?.scheduleRender();
|
|
8660
8687
|
if (notifyAll) {
|
|
8661
|
-
|
|
8662
|
-
stack && stack.scheduleRender();
|
|
8688
|
+
this.getStack()?.scheduleRender();
|
|
8663
8689
|
}
|
|
8664
8690
|
if (this.resumingFrom && !this.resumingFrom.instance) {
|
|
8665
8691
|
this.resumingFrom = undefined;
|
|
@@ -8681,9 +8707,7 @@
|
|
|
8681
8707
|
}
|
|
8682
8708
|
this.attemptToResolveRelativeTarget = !hasOnlyRelativeTargetChanged;
|
|
8683
8709
|
const relativeLayout = createBox();
|
|
8684
|
-
const
|
|
8685
|
-
const layoutSource = this.layout ? this.layout.source : undefined;
|
|
8686
|
-
const isSharedLayoutAnimation = snapshotSource !== layoutSource;
|
|
8710
|
+
const isSharedLayoutAnimation = snapshot?.source !== this.layout?.source;
|
|
8687
8711
|
const stack = this.getStack();
|
|
8688
8712
|
const isOnlyMember = !stack || stack.members.length <= 1;
|
|
8689
8713
|
const shouldCrossfadeOpacity = Boolean(isSharedLayoutAnimation &&
|
|
@@ -8698,19 +8722,11 @@
|
|
|
8698
8722
|
this.mixTargetDelta = (latest) => {
|
|
8699
8723
|
const progress = latest / 1000;
|
|
8700
8724
|
const point = interpolate?.(progress);
|
|
8725
|
+
mixAxisDeltaLinear(targetDelta.x, delta.x, progress);
|
|
8726
|
+
mixAxisDeltaLinear(targetDelta.y, delta.y, progress);
|
|
8701
8727
|
if (point) {
|
|
8702
8728
|
targetDelta.x.translate = point.x;
|
|
8703
|
-
targetDelta.x.scale = mixNumber$1(delta.x.scale, 1, progress);
|
|
8704
|
-
targetDelta.x.origin = delta.x.origin;
|
|
8705
|
-
targetDelta.x.originPoint = delta.x.originPoint;
|
|
8706
8729
|
targetDelta.y.translate = point.y;
|
|
8707
|
-
targetDelta.y.scale = mixNumber$1(delta.y.scale, 1, progress);
|
|
8708
|
-
targetDelta.y.origin = delta.y.origin;
|
|
8709
|
-
targetDelta.y.originPoint = delta.y.originPoint;
|
|
8710
|
-
}
|
|
8711
|
-
else {
|
|
8712
|
-
mixAxisDeltaLinear(targetDelta.x, delta.x, progress);
|
|
8713
|
-
mixAxisDeltaLinear(targetDelta.y, delta.y, progress);
|
|
8714
8730
|
}
|
|
8715
8731
|
this.setTargetDelta(targetDelta);
|
|
8716
8732
|
if (this.relativeTarget &&
|
|
@@ -8736,7 +8752,7 @@
|
|
|
8736
8752
|
this.animationValues = mixedValues;
|
|
8737
8753
|
mixValues(mixedValues, snapshotLatestValues, this.latestValues, progress, shouldCrossfadeOpacity, isOnlyMember);
|
|
8738
8754
|
}
|
|
8739
|
-
if (point
|
|
8755
|
+
if (point?.rotate !== undefined) {
|
|
8740
8756
|
// Dedicated `pathRotation` channel, not `rotate`, so an
|
|
8741
8757
|
// animating `rotate` is composed with, never clobbered.
|
|
8742
8758
|
if (!this.animationValues)
|
|
@@ -8801,7 +8817,7 @@
|
|
|
8801
8817
|
}
|
|
8802
8818
|
finishAnimation() {
|
|
8803
8819
|
if (this.currentAnimation) {
|
|
8804
|
-
this.mixTargetDelta
|
|
8820
|
+
this.mixTargetDelta?.(animationTarget);
|
|
8805
8821
|
this.currentAnimation.stop();
|
|
8806
8822
|
}
|
|
8807
8823
|
this.completeAnimation();
|
|
@@ -8821,13 +8837,11 @@
|
|
|
8821
8837
|
this.layout &&
|
|
8822
8838
|
layout &&
|
|
8823
8839
|
shouldAnimatePositionOnly(this.options.animationType, this.layout.layoutBox, layout.layoutBox)) {
|
|
8840
|
+
const { layoutBox } = this.layout;
|
|
8841
|
+
const leadTarget = target;
|
|
8824
8842
|
target = this.target || createBox();
|
|
8825
|
-
|
|
8826
|
-
target.
|
|
8827
|
-
target.x.max = target.x.min + xLength;
|
|
8828
|
-
const yLength = calcLength(this.layout.layoutBox.y);
|
|
8829
|
-
target.y.min = lead.target.y.min;
|
|
8830
|
-
target.y.max = target.y.min + yLength;
|
|
8843
|
+
alignAxis(target.x, leadTarget.x, layoutBox.x);
|
|
8844
|
+
alignAxis(target.y, leadTarget.y, layoutBox.y);
|
|
8831
8845
|
}
|
|
8832
8846
|
copyBoxInto(targetWithTransforms, target);
|
|
8833
8847
|
/**
|
|
@@ -8852,8 +8866,8 @@
|
|
|
8852
8866
|
stack.add(node);
|
|
8853
8867
|
const config = node.options.initialPromotionConfig;
|
|
8854
8868
|
node.promote({
|
|
8855
|
-
transition: config
|
|
8856
|
-
preserveFollowOpacity: config
|
|
8869
|
+
transition: config?.transition,
|
|
8870
|
+
preserveFollowOpacity: config?.shouldPreserveFollowOpacity
|
|
8857
8871
|
? config.shouldPreserveFollowOpacity(node)
|
|
8858
8872
|
: undefined,
|
|
8859
8873
|
});
|
|
@@ -8876,9 +8890,7 @@
|
|
|
8876
8890
|
return this.root.sharedNodes.get(layoutId);
|
|
8877
8891
|
}
|
|
8878
8892
|
promote({ needsReset, transition, preserveFollowOpacity, } = {}) {
|
|
8879
|
-
|
|
8880
|
-
if (stack)
|
|
8881
|
-
stack.promote(this, preserveFollowOpacity);
|
|
8893
|
+
this.getStack()?.promote(this, preserveFollowOpacity);
|
|
8882
8894
|
if (needsReset) {
|
|
8883
8895
|
this.projectionDelta = undefined;
|
|
8884
8896
|
this.needsReset = true;
|
|
@@ -8887,45 +8899,27 @@
|
|
|
8887
8899
|
this.setOptions({ transition });
|
|
8888
8900
|
}
|
|
8889
8901
|
relegate() {
|
|
8890
|
-
|
|
8891
|
-
if (stack) {
|
|
8892
|
-
return stack.relegate(this);
|
|
8893
|
-
}
|
|
8894
|
-
else {
|
|
8895
|
-
return false;
|
|
8896
|
-
}
|
|
8902
|
+
return this.getStack()?.relegate(this) || false;
|
|
8897
8903
|
}
|
|
8898
8904
|
resetSkewAndRotation() {
|
|
8899
8905
|
const { visualElement } = this.options;
|
|
8900
8906
|
if (!visualElement)
|
|
8901
8907
|
return;
|
|
8902
|
-
// If there's no detected skew or rotation values, we can early return without a forced render.
|
|
8903
|
-
let hasDistortingTransform = false;
|
|
8904
8908
|
/**
|
|
8905
8909
|
* An unrolled check for rotation values. Most elements don't have any rotation and
|
|
8906
|
-
* 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.
|
|
8907
8912
|
*/
|
|
8908
8913
|
const { latestValues } = visualElement;
|
|
8909
|
-
if (latestValues
|
|
8910
|
-
latestValues.rotate ||
|
|
8911
|
-
latestValues.rotateX ||
|
|
8912
|
-
latestValues.rotateY ||
|
|
8913
|
-
latestValues.rotateZ ||
|
|
8914
|
-
latestValues.skewX ||
|
|
8915
|
-
latestValues.skewY) {
|
|
8916
|
-
hasDistortingTransform = true;
|
|
8917
|
-
}
|
|
8918
|
-
// If there's no distorting values, we don't need to do any more.
|
|
8919
|
-
if (!hasDistortingTransform)
|
|
8914
|
+
if (!hasDistortingTransform(latestValues))
|
|
8920
8915
|
return;
|
|
8921
8916
|
const resetValues = {};
|
|
8922
|
-
if (latestValues.z) {
|
|
8923
|
-
resetDistortingTransform("z", visualElement, resetValues, this.animationValues);
|
|
8924
|
-
}
|
|
8925
8917
|
// Check the skew and rotate value of all axes and reset to 0
|
|
8926
|
-
for (let i = 0; i <
|
|
8927
|
-
resetDistortingTransform(
|
|
8928
|
-
|
|
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);
|
|
8929
8923
|
}
|
|
8930
8924
|
// Force a render of this element to apply the transform with all skews and rotations
|
|
8931
8925
|
// set to 0.
|
|
@@ -8945,7 +8939,13 @@
|
|
|
8945
8939
|
styleProp) {
|
|
8946
8940
|
if (!this.instance || this.isSVG)
|
|
8947
8941
|
return;
|
|
8948
|
-
|
|
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) {
|
|
8949
8949
|
targetStyle.visibility = "hidden";
|
|
8950
8950
|
return;
|
|
8951
8951
|
}
|
|
@@ -8961,9 +8961,10 @@
|
|
|
8961
8961
|
: "none";
|
|
8962
8962
|
return;
|
|
8963
8963
|
}
|
|
8964
|
-
const lead = this.getLead();
|
|
8965
8964
|
if (!this.projectionDelta || !this.layout || !lead.target) {
|
|
8966
|
-
if (this.options.layoutId) {
|
|
8965
|
+
if (this.options.layoutId || this.needsShow) {
|
|
8966
|
+
this.needsShow = false;
|
|
8967
|
+
targetStyle.visibility = "";
|
|
8967
8968
|
targetStyle.opacity =
|
|
8968
8969
|
this.latestValues.opacity !== undefined
|
|
8969
8970
|
? this.latestValues.opacity
|
|
@@ -9097,52 +9098,49 @@
|
|
|
9097
9098
|
const isShared = snapshot.source !== node.layout.source;
|
|
9098
9099
|
// TODO Maybe we want to also resize the layout snapshot so we don't trigger
|
|
9099
9100
|
// animations for instance if layout="size" and an element has only changed position
|
|
9100
|
-
|
|
9101
|
-
|
|
9102
|
-
|
|
9103
|
-
|
|
9104
|
-
|
|
9105
|
-
|
|
9106
|
-
|
|
9107
|
-
|
|
9108
|
-
|
|
9109
|
-
|
|
9110
|
-
|
|
9111
|
-
|
|
9112
|
-
|
|
9113
|
-
|
|
9114
|
-
|
|
9115
|
-
|
|
9116
|
-
|
|
9117
|
-
|
|
9118
|
-
|
|
9119
|
-
|
|
9120
|
-
|
|
9121
|
-
|
|
9122
|
-
|
|
9123
|
-
|
|
9124
|
-
|
|
9125
|
-
|
|
9126
|
-
|
|
9127
|
-
|
|
9128
|
-
|
|
9129
|
-
node.relativeTarget[axis].min + length;
|
|
9130
|
-
}
|
|
9131
|
-
});
|
|
9132
|
-
}
|
|
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);
|
|
9133
9130
|
const layoutDelta = createDelta();
|
|
9134
9131
|
calcBoxDelta(layoutDelta, layout, snapshot.layoutBox);
|
|
9135
9132
|
const visualDelta = createDelta();
|
|
9136
|
-
|
|
9137
|
-
calcBoxDelta(visualDelta, node.applyTransform(measuredLayout, true), snapshot.measuredBox);
|
|
9138
|
-
}
|
|
9139
|
-
else {
|
|
9140
|
-
calcBoxDelta(visualDelta, layout, snapshot.layoutBox);
|
|
9141
|
-
}
|
|
9133
|
+
calcBoxDelta(visualDelta, isShared ? node.applyTransform(measuredLayout, true) : layout, snapshotBox);
|
|
9142
9134
|
const hasLayoutChanged = !isDeltaZero(layoutDelta);
|
|
9143
9135
|
let hasRelativeLayoutChanged = false;
|
|
9144
|
-
|
|
9145
|
-
|
|
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))) {
|
|
9146
9144
|
/**
|
|
9147
9145
|
* If the relativeParent is itself resuming from a different element then
|
|
9148
9146
|
* the relative snapshot is not relavent
|
|
@@ -9151,10 +9149,22 @@
|
|
|
9151
9149
|
const { snapshot: parentSnapshot, layout: parentLayout } = relativeParent;
|
|
9152
9150
|
if (parentSnapshot && parentLayout) {
|
|
9153
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
|
+
*/
|
|
9154
9157
|
const relativeSnapshot = createBox();
|
|
9155
|
-
calcRelativePosition(relativeSnapshot,
|
|
9158
|
+
calcRelativePosition(relativeSnapshot, isShared
|
|
9159
|
+
? node.resumeFrom.applyTransform(snapshot.layoutBox, true, undefined, true, relativeParent.depth + 1)
|
|
9160
|
+
: snapshot.layoutBox, parentSnapshot.layoutBox, anchor);
|
|
9156
9161
|
const relativeLayout = createBox();
|
|
9157
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);
|
|
9158
9168
|
if (!boxEqualsRounded(relativeSnapshot, relativeLayout)) {
|
|
9159
9169
|
hasRelativeLayoutChanged = true;
|
|
9160
9170
|
}
|
|
@@ -9176,8 +9186,7 @@
|
|
|
9176
9186
|
});
|
|
9177
9187
|
}
|
|
9178
9188
|
else if (node.isLead()) {
|
|
9179
|
-
|
|
9180
|
-
onExitComplete && onExitComplete();
|
|
9189
|
+
node.options.onExitComplete?.();
|
|
9181
9190
|
}
|
|
9182
9191
|
/**
|
|
9183
9192
|
* Clearing transition
|
|
@@ -9237,10 +9246,13 @@
|
|
|
9237
9246
|
* A dragged node doesn't layout animate, so it jumps to its new layout. Shift
|
|
9238
9247
|
* its descendants' snapshots by the same amount, so they only animate layout
|
|
9239
9248
|
* changes relative to it, rather than lagging behind.
|
|
9249
|
+
*
|
|
9250
|
+
* If its drag ended since the snapshot (e.g. drag disabled from onDrag), it
|
|
9251
|
+
* layout animates again, so its descendants mustn't follow its jump.
|
|
9240
9252
|
*/
|
|
9241
9253
|
function followDraggedAncestor(node) {
|
|
9242
9254
|
const { snapshot, dragAncestor } = node;
|
|
9243
|
-
const to = dragAncestor?.layout;
|
|
9255
|
+
const to = dragAncestor?.isAnimationBlocked && dragAncestor.layout;
|
|
9244
9256
|
if (!snapshot || !to || node.resumeFrom || node.isAnimationBlocked)
|
|
9245
9257
|
return;
|
|
9246
9258
|
const from = dragAncestor.dragSnapshot;
|
|
@@ -9280,7 +9292,7 @@
|
|
|
9280
9292
|
}
|
|
9281
9293
|
function resetTransformStyle(node) {
|
|
9282
9294
|
const { visualElement } = node.options;
|
|
9283
|
-
if (visualElement
|
|
9295
|
+
if (visualElement?.getProps().onBeforeLayoutMeasure) {
|
|
9284
9296
|
visualElement.notify("BeforeLayoutMeasure");
|
|
9285
9297
|
}
|
|
9286
9298
|
node.resetTransform();
|
|
@@ -9314,7 +9326,7 @@
|
|
|
9314
9326
|
mixAxis(output.y, from.y, to.y, p);
|
|
9315
9327
|
}
|
|
9316
9328
|
function hasOpacityCrossfade(node) {
|
|
9317
|
-
return
|
|
9329
|
+
return node.animationValues?.opacityExit !== undefined;
|
|
9318
9330
|
}
|
|
9319
9331
|
const defaultLayoutTransition = {
|
|
9320
9332
|
duration: 0.45,
|
|
@@ -9340,6 +9352,13 @@
|
|
|
9340
9352
|
roundAxis(box.x);
|
|
9341
9353
|
roundAxis(box.y);
|
|
9342
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
|
+
}
|
|
9343
9362
|
function shouldAnimatePositionOnly(animationType, snapshot, layout) {
|
|
9344
9363
|
return (animationType === "position" ||
|
|
9345
9364
|
(animationType === "preserve-aspect" &&
|
|
@@ -9354,11 +9373,16 @@
|
|
|
9354
9373
|
return () => target.removeEventListener(eventName, handler, options);
|
|
9355
9374
|
}
|
|
9356
9375
|
|
|
9357
|
-
const DocumentProjectionNode =
|
|
9376
|
+
const DocumentProjectionNode =
|
|
9377
|
+
/* @__PURE__ */ createProjectionNode({
|
|
9358
9378
|
attachResizeListener: (ref, notify) => addDomEvent(ref, "resize", notify),
|
|
9359
9379
|
measureScroll: () => ({
|
|
9360
|
-
x: document.documentElement.scrollLeft ||
|
|
9361
|
-
|
|
9380
|
+
x: document.documentElement.scrollLeft ||
|
|
9381
|
+
document.body?.scrollLeft ||
|
|
9382
|
+
0,
|
|
9383
|
+
y: document.documentElement.scrollTop ||
|
|
9384
|
+
document.body?.scrollTop ||
|
|
9385
|
+
0,
|
|
9362
9386
|
}),
|
|
9363
9387
|
checkIsScrollRoot: () => true,
|
|
9364
9388
|
});
|
|
@@ -9366,7 +9390,9 @@
|
|
|
9366
9390
|
const rootProjectionNode = {
|
|
9367
9391
|
current: undefined,
|
|
9368
9392
|
};
|
|
9369
|
-
|
|
9393
|
+
|
|
9394
|
+
const HTMLProjectionNode =
|
|
9395
|
+
/* @__PURE__ */ createProjectionNode({
|
|
9370
9396
|
measureScroll: (instance) => ({
|
|
9371
9397
|
x: instance.scrollLeft,
|
|
9372
9398
|
y: instance.scrollTop,
|
|
@@ -9383,7 +9409,7 @@
|
|
|
9383
9409
|
resetTransform: (instance, value) => {
|
|
9384
9410
|
instance.style.transform = value !== undefined ? value : "none";
|
|
9385
9411
|
},
|
|
9386
|
-
checkIsScrollRoot: (instance) =>
|
|
9412
|
+
checkIsScrollRoot: (instance) => window.getComputedStyle(instance).position === "fixed",
|
|
9387
9413
|
});
|
|
9388
9414
|
|
|
9389
9415
|
const visualElementStore = new WeakMap();
|
|
@@ -9410,7 +9436,7 @@
|
|
|
9410
9436
|
"whileDrag",
|
|
9411
9437
|
"exit",
|
|
9412
9438
|
];
|
|
9413
|
-
const variantProps = ["initial"
|
|
9439
|
+
const variantProps = /* @__PURE__ */ ["initial"].concat(variantPriorityOrder);
|
|
9414
9440
|
|
|
9415
9441
|
function isControllingVariants(props) {
|
|
9416
9442
|
if (isAnimationControls(props.animate))
|
|
@@ -9669,9 +9695,7 @@
|
|
|
9669
9695
|
}
|
|
9670
9696
|
this.current = instance;
|
|
9671
9697
|
visualElementStore.set(instance, this);
|
|
9672
|
-
|
|
9673
|
-
this.projection.mount(instance);
|
|
9674
|
-
}
|
|
9698
|
+
this.projection?.instance || this.projection?.mount(instance);
|
|
9675
9699
|
/**
|
|
9676
9700
|
* Join the closest variant node above, so its variant changes
|
|
9677
9701
|
* propagate here, unless inherit={false} cuts the chain.
|
|
@@ -9717,12 +9741,12 @@
|
|
|
9717
9741
|
this.hasBeenMounted = true;
|
|
9718
9742
|
}
|
|
9719
9743
|
unmount() {
|
|
9720
|
-
this.projection
|
|
9744
|
+
this.projection?.unmount();
|
|
9721
9745
|
cancelFrame(this.notifyUpdate);
|
|
9722
9746
|
cancelFrame(this.render);
|
|
9723
9747
|
this.valueSubscriptions.forEach((remove) => remove());
|
|
9724
9748
|
this.valueSubscriptions.clear();
|
|
9725
|
-
this.removeFromVariantTree
|
|
9749
|
+
this.removeFromVariantTree?.();
|
|
9726
9750
|
this.parent?.removeChild(this);
|
|
9727
9751
|
for (const key in this.events) {
|
|
9728
9752
|
this.events[key].clear();
|
|
@@ -9738,17 +9762,14 @@
|
|
|
9738
9762
|
}
|
|
9739
9763
|
addChild(child) {
|
|
9740
9764
|
this.children.add(child);
|
|
9741
|
-
this.enteringChildren
|
|
9742
|
-
this.enteringChildren.add(child);
|
|
9765
|
+
(this.enteringChildren || (this.enteringChildren = new Set())).add(child);
|
|
9743
9766
|
}
|
|
9744
9767
|
removeChild(child) {
|
|
9745
9768
|
this.children.delete(child);
|
|
9746
|
-
this.enteringChildren
|
|
9769
|
+
this.enteringChildren?.delete(child);
|
|
9747
9770
|
}
|
|
9748
9771
|
bindToMotionValue(key, value) {
|
|
9749
|
-
|
|
9750
|
-
this.valueSubscriptions.get(key)();
|
|
9751
|
-
}
|
|
9772
|
+
this.valueSubscriptions.get(key)?.();
|
|
9752
9773
|
if (value.accelerate &&
|
|
9753
9774
|
acceleratedValues.has(key) &&
|
|
9754
9775
|
this.current instanceof HTMLElement) {
|
|
@@ -9790,8 +9811,7 @@
|
|
|
9790
9811
|
}
|
|
9791
9812
|
this.valueSubscriptions.set(key, () => {
|
|
9792
9813
|
removeOnChange();
|
|
9793
|
-
|
|
9794
|
-
removeSyncCheck();
|
|
9814
|
+
removeSyncCheck?.();
|
|
9795
9815
|
// Defer to MotionValue.on("change") auto-stop so React 19 remounts
|
|
9796
9816
|
// can resubscribe before the animation is cancelled (#3315).
|
|
9797
9817
|
});
|
|
@@ -9884,10 +9904,8 @@
|
|
|
9884
9904
|
*/
|
|
9885
9905
|
for (let i = 0; i < propEventHandlers.length; i++) {
|
|
9886
9906
|
const key = propEventHandlers[i];
|
|
9887
|
-
|
|
9888
|
-
|
|
9889
|
-
delete this.propEventSubscriptions[key];
|
|
9890
|
-
}
|
|
9907
|
+
this.propEventSubscriptions[key]?.();
|
|
9908
|
+
delete this.propEventSubscriptions[key];
|
|
9891
9909
|
const listenerName = ("on" + key);
|
|
9892
9910
|
const listener = props[listenerName];
|
|
9893
9911
|
if (listener) {
|
|
@@ -9895,9 +9913,7 @@
|
|
|
9895
9913
|
}
|
|
9896
9914
|
}
|
|
9897
9915
|
this.prevMotionValues = updateMotionValuesFromProps(this, this.scrapeMotionValuesFromProps(props, this.prevProps || {}, this), this.prevMotionValues);
|
|
9898
|
-
|
|
9899
|
-
this.handleChildMotionValue();
|
|
9900
|
-
}
|
|
9916
|
+
this.handleChildMotionValue?.();
|
|
9901
9917
|
}
|
|
9902
9918
|
getProps() {
|
|
9903
9919
|
return this.props;
|
|
@@ -9906,7 +9922,7 @@
|
|
|
9906
9922
|
* Returns the variant definition with a given name.
|
|
9907
9923
|
*/
|
|
9908
9924
|
getVariant(name) {
|
|
9909
|
-
return this.props.variants
|
|
9925
|
+
return this.props.variants?.[name];
|
|
9910
9926
|
}
|
|
9911
9927
|
/**
|
|
9912
9928
|
* Returns the defined default transition on this component.
|
|
@@ -9944,11 +9960,8 @@
|
|
|
9944
9960
|
*/
|
|
9945
9961
|
removeValue(key) {
|
|
9946
9962
|
this.values.delete(key);
|
|
9947
|
-
|
|
9948
|
-
|
|
9949
|
-
unsubscribe();
|
|
9950
|
-
this.valueSubscriptions.delete(key);
|
|
9951
|
-
}
|
|
9963
|
+
this.valueSubscriptions.get(key)?.();
|
|
9964
|
+
this.valueSubscriptions.delete(key);
|
|
9952
9965
|
delete this.latestValues[key];
|
|
9953
9966
|
this.removeValueFromRenderState(key, this.renderState);
|
|
9954
9967
|
}
|
|
@@ -9959,10 +9972,10 @@
|
|
|
9959
9972
|
return this.values.has(key);
|
|
9960
9973
|
}
|
|
9961
9974
|
getValue(key, defaultValue) {
|
|
9962
|
-
|
|
9963
|
-
|
|
9964
|
-
|
|
9965
|
-
|
|
9975
|
+
let value = this.props.values?.[key];
|
|
9976
|
+
if (value)
|
|
9977
|
+
return value;
|
|
9978
|
+
value = this.values.get(key);
|
|
9966
9979
|
if (value === undefined && defaultValue !== undefined) {
|
|
9967
9980
|
value = motionValue(defaultValue ?? this.getDefaultValue?.(key), {
|
|
9968
9981
|
owner: this,
|
|
@@ -10012,15 +10025,11 @@
|
|
|
10012
10025
|
this.baseTarget[key] = value;
|
|
10013
10026
|
}
|
|
10014
10027
|
on(eventName, callback) {
|
|
10015
|
-
|
|
10016
|
-
this.events[eventName] = new SubscriptionManager();
|
|
10017
|
-
}
|
|
10018
|
-
return this.events[eventName].add(callback);
|
|
10028
|
+
return (this.events[eventName] ||
|
|
10029
|
+
(this.events[eventName] = new SubscriptionManager())).add(callback);
|
|
10019
10030
|
}
|
|
10020
10031
|
notify(eventName, ...args) {
|
|
10021
|
-
|
|
10022
|
-
this.events[eventName].notify(...args);
|
|
10023
|
-
}
|
|
10032
|
+
this.events[eventName]?.notify(...args);
|
|
10024
10033
|
}
|
|
10025
10034
|
scheduleRenderMicrotask() {
|
|
10026
10035
|
microtask.render(this.render);
|
|
@@ -10193,7 +10202,7 @@
|
|
|
10193
10202
|
let ancestor = element.parentElement;
|
|
10194
10203
|
while (ancestor) {
|
|
10195
10204
|
const node = layoutNodes.get(ancestor);
|
|
10196
|
-
if (node
|
|
10205
|
+
if (node?.instance)
|
|
10197
10206
|
return node;
|
|
10198
10207
|
ancestor = ancestor.parentElement;
|
|
10199
10208
|
}
|
|
@@ -10459,7 +10468,7 @@
|
|
|
10459
10468
|
hasSurvivor &&
|
|
10460
10469
|
!newMemberIds.has(layoutId)) {
|
|
10461
10470
|
if (restore && restore.parent.isConnected) {
|
|
10462
|
-
restore.parent.insertBefore(element, restore.next
|
|
10471
|
+
restore.parent.insertBefore(element, restore.next?.parentNode === restore.parent
|
|
10463
10472
|
? restore.next
|
|
10464
10473
|
: null);
|
|
10465
10474
|
node.isPresent = false;
|
|
@@ -10664,10 +10673,6 @@
|
|
|
10664
10673
|
* Scroll positions of scrollable ancestors and window.
|
|
10665
10674
|
*/
|
|
10666
10675
|
this.scrollPositions = new Map();
|
|
10667
|
-
/**
|
|
10668
|
-
* Cleanup function for scroll listeners.
|
|
10669
|
-
*/
|
|
10670
|
-
this.removeScrollListeners = null;
|
|
10671
10676
|
this.onElementScroll = (event) => {
|
|
10672
10677
|
this.handleScroll(event.target);
|
|
10673
10678
|
};
|
|
@@ -10695,10 +10700,10 @@
|
|
|
10695
10700
|
this.history.push({ ...point, timestamp: time.now() });
|
|
10696
10701
|
const { onStart, onMove } = this.handlers;
|
|
10697
10702
|
if (!isPanStarted) {
|
|
10698
|
-
onStart
|
|
10703
|
+
onStart?.(this.lastMoveEvent, info);
|
|
10699
10704
|
this.startEvent = this.lastMoveEvent;
|
|
10700
10705
|
}
|
|
10701
|
-
onMove
|
|
10706
|
+
onMove?.(this.lastMoveEvent, info);
|
|
10702
10707
|
};
|
|
10703
10708
|
this.handlePointerMove = (event, info) => {
|
|
10704
10709
|
/**
|
|
@@ -10723,17 +10728,15 @@
|
|
|
10723
10728
|
// Resume animation if dragSnapToOrigin is set OR if no drag started (user just clicked)
|
|
10724
10729
|
// This ensures constraint animations continue when interrupted by a click
|
|
10725
10730
|
if (this.dragSnapToOrigin || !this.startEvent) {
|
|
10726
|
-
resumeAnimation
|
|
10731
|
+
resumeAnimation?.();
|
|
10727
10732
|
}
|
|
10728
10733
|
if (!(this.lastMoveEvent && this.lastMoveEventInfo))
|
|
10729
10734
|
return;
|
|
10730
10735
|
const panInfo = getPanInfo(event.type === "pointercancel"
|
|
10731
10736
|
? this.lastMoveEventInfo
|
|
10732
10737
|
: transformPoint(info, this.transformPagePoint), this.history);
|
|
10733
|
-
|
|
10734
|
-
|
|
10735
|
-
}
|
|
10736
|
-
onSessionEnd && onSessionEnd(event, panInfo);
|
|
10738
|
+
this.startEvent && onEnd?.(event, panInfo);
|
|
10739
|
+
onSessionEnd?.(event, panInfo);
|
|
10737
10740
|
};
|
|
10738
10741
|
// If we have more than one touch, don't start detecting this gesture
|
|
10739
10742
|
if (!isPrimaryPointer(event))
|
|
@@ -10746,8 +10749,13 @@
|
|
|
10746
10749
|
const info = extractEventInfo(event);
|
|
10747
10750
|
const initialInfo = transformPoint(info, this.transformPagePoint);
|
|
10748
10751
|
const { point } = initialInfo;
|
|
10749
|
-
|
|
10750
|
-
|
|
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() }];
|
|
10751
10759
|
const { onSessionStart } = handlers;
|
|
10752
10760
|
onSessionStart &&
|
|
10753
10761
|
onSessionStart(event, getPanInfo(initialInfo, this.history));
|
|
@@ -10771,30 +10779,19 @@
|
|
|
10771
10779
|
const style = getComputedStyle(current);
|
|
10772
10780
|
if (overflowStyles.has(style.overflowX) ||
|
|
10773
10781
|
overflowStyles.has(style.overflowY)) {
|
|
10774
|
-
this.scrollPositions.set(current,
|
|
10775
|
-
x: current.scrollLeft,
|
|
10776
|
-
y: current.scrollTop,
|
|
10777
|
-
});
|
|
10782
|
+
this.scrollPositions.set(current, getScroll(current));
|
|
10778
10783
|
}
|
|
10779
10784
|
current = current.parentElement;
|
|
10780
10785
|
}
|
|
10781
10786
|
// Track window scroll
|
|
10782
|
-
this.scrollPositions.set(window,
|
|
10783
|
-
|
|
10784
|
-
y: window.scrollY,
|
|
10785
|
-
});
|
|
10787
|
+
this.scrollPositions.set(window, getScroll(window));
|
|
10788
|
+
this.removeScrollListeners = pipe(
|
|
10786
10789
|
// Capture listener catches element scroll events as they bubble
|
|
10787
|
-
window
|
|
10790
|
+
addDomEvent(window, "scroll", this.onElementScroll, {
|
|
10788
10791
|
capture: true,
|
|
10789
|
-
})
|
|
10792
|
+
}),
|
|
10790
10793
|
// Direct window scroll listener (window scroll doesn't bubble)
|
|
10791
|
-
window
|
|
10792
|
-
this.removeScrollListeners = () => {
|
|
10793
|
-
window.removeEventListener("scroll", this.onElementScroll, {
|
|
10794
|
-
capture: true,
|
|
10795
|
-
});
|
|
10796
|
-
window.removeEventListener("scroll", this.onWindowScroll);
|
|
10797
|
-
};
|
|
10794
|
+
addDomEvent(window, "scroll", this.onWindowScroll, {}));
|
|
10798
10795
|
}
|
|
10799
10796
|
/**
|
|
10800
10797
|
* Handle scroll compensation during drag.
|
|
@@ -10807,12 +10804,7 @@
|
|
|
10807
10804
|
if (!initial)
|
|
10808
10805
|
return;
|
|
10809
10806
|
const isWindow = target === window;
|
|
10810
|
-
const current =
|
|
10811
|
-
? { x: window.scrollX, y: window.scrollY }
|
|
10812
|
-
: {
|
|
10813
|
-
x: target.scrollLeft,
|
|
10814
|
-
y: target.scrollTop,
|
|
10815
|
-
};
|
|
10807
|
+
const current = getScroll(target);
|
|
10816
10808
|
const delta = { x: current.x - initial.x, y: current.y - initial.y };
|
|
10817
10809
|
if (delta.x === 0 && delta.y === 0)
|
|
10818
10810
|
return;
|
|
@@ -10837,12 +10829,18 @@
|
|
|
10837
10829
|
this.handlers = handlers;
|
|
10838
10830
|
}
|
|
10839
10831
|
end() {
|
|
10840
|
-
this.removeListeners
|
|
10841
|
-
this.removeScrollListeners
|
|
10832
|
+
this.removeListeners?.();
|
|
10833
|
+
this.removeScrollListeners?.();
|
|
10842
10834
|
this.scrollPositions.clear();
|
|
10843
10835
|
cancelFrame(this.updatePoint);
|
|
10844
10836
|
}
|
|
10845
10837
|
}
|
|
10838
|
+
const getScroll = (target) => target === window
|
|
10839
|
+
? { x: window.scrollX, y: window.scrollY }
|
|
10840
|
+
: {
|
|
10841
|
+
x: target.scrollLeft,
|
|
10842
|
+
y: target.scrollTop,
|
|
10843
|
+
};
|
|
10846
10844
|
function transformPoint(info, transformPagePoint) {
|
|
10847
10845
|
return transformPagePoint ? { point: transformPagePoint(info.point) } : info;
|
|
10848
10846
|
}
|
|
@@ -11099,6 +11097,25 @@
|
|
|
11099
11097
|
: dragElastic[label] || 0;
|
|
11100
11098
|
}
|
|
11101
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
|
+
}
|
|
11102
11119
|
const elementDragControls = new WeakMap();
|
|
11103
11120
|
class VisualElementDragControls {
|
|
11104
11121
|
constructor(visualElement, readProps = () => visualElement.getProps()) {
|
|
@@ -11130,8 +11147,7 @@
|
|
|
11130
11147
|
/**
|
|
11131
11148
|
* Don't start dragging if this component is exiting
|
|
11132
11149
|
*/
|
|
11133
|
-
|
|
11134
|
-
if (presenceContext && presenceContext.isPresent === false)
|
|
11150
|
+
if (this.visualElement.presenceContext?.isPresent === false)
|
|
11135
11151
|
return;
|
|
11136
11152
|
const onSessionStart = (event) => {
|
|
11137
11153
|
if (snapToCursor)
|
|
@@ -11142,8 +11158,7 @@
|
|
|
11142
11158
|
// Attempt to grab the global drag gesture lock - maybe make this part of PanSession
|
|
11143
11159
|
const { drag, dragPropagation, onDragStart } = this.getProps();
|
|
11144
11160
|
if (drag && !dragPropagation) {
|
|
11145
|
-
|
|
11146
|
-
this.openDragLock();
|
|
11161
|
+
this.openDragLock?.();
|
|
11147
11162
|
this.openDragLock = setDragLock(drag);
|
|
11148
11163
|
// If we don 't have the lock, don't start dragging
|
|
11149
11164
|
if (!this.openDragLock)
|
|
@@ -11154,9 +11169,10 @@
|
|
|
11154
11169
|
this.isDragging = true;
|
|
11155
11170
|
this.currentDirection = null;
|
|
11156
11171
|
this.resolveConstraints();
|
|
11157
|
-
|
|
11158
|
-
|
|
11159
|
-
|
|
11172
|
+
const { projection } = this.visualElement;
|
|
11173
|
+
if (projection) {
|
|
11174
|
+
projection.isAnimationBlocked = true;
|
|
11175
|
+
projection.target = undefined;
|
|
11160
11176
|
}
|
|
11161
11177
|
/**
|
|
11162
11178
|
* Record gesture origin and pointer offset
|
|
@@ -11178,13 +11194,11 @@
|
|
|
11178
11194
|
current = parseFloat(current);
|
|
11179
11195
|
}
|
|
11180
11196
|
else if (percent.test(current)) {
|
|
11181
|
-
const
|
|
11182
|
-
if (
|
|
11183
|
-
|
|
11184
|
-
|
|
11185
|
-
|
|
11186
|
-
current = length * (parseFloat(current) / 100);
|
|
11187
|
-
}
|
|
11197
|
+
const measuredAxis = projection?.layout?.layoutBox[axis];
|
|
11198
|
+
if (measuredAxis) {
|
|
11199
|
+
current =
|
|
11200
|
+
calcLength(measuredAxis) *
|
|
11201
|
+
(parseFloat(current) / 100);
|
|
11188
11202
|
}
|
|
11189
11203
|
}
|
|
11190
11204
|
this.originPoint[axis] = current;
|
|
@@ -11194,8 +11208,7 @@
|
|
|
11194
11208
|
frame.update(() => onDragStart(event, info), false, true);
|
|
11195
11209
|
}
|
|
11196
11210
|
addValueToWillChange(this.visualElement, "transform");
|
|
11197
|
-
|
|
11198
|
-
animationState && animationState.setActive("whileDrag", true);
|
|
11211
|
+
this.visualElement.animationState?.setActive("whileDrag", true);
|
|
11199
11212
|
};
|
|
11200
11213
|
const onMove = (event, info) => {
|
|
11201
11214
|
this.latestPointerEvent = event;
|
|
@@ -11218,8 +11231,8 @@
|
|
|
11218
11231
|
onDirectionLock && onDirectionLock(this.currentDirection);
|
|
11219
11232
|
}
|
|
11220
11233
|
// Update each point with the latest position
|
|
11221
|
-
this.updateAxis("x",
|
|
11222
|
-
this.updateAxis("y",
|
|
11234
|
+
this.updateAxis("x", offset);
|
|
11235
|
+
this.updateAxis("y", offset);
|
|
11223
11236
|
/**
|
|
11224
11237
|
* Ideally we would leave the renderer to fire naturally at the end of
|
|
11225
11238
|
* this frame but if the element is about to change layout as the result
|
|
@@ -11263,7 +11276,11 @@
|
|
|
11263
11276
|
element: this.visualElement.current,
|
|
11264
11277
|
});
|
|
11265
11278
|
}
|
|
11266
|
-
|
|
11279
|
+
/**
|
|
11280
|
+
* @param dragAxes - The axes to animate on release, if not the `drag`
|
|
11281
|
+
* prop's, e.g. when drag was disabled mid-gesture.
|
|
11282
|
+
*/
|
|
11283
|
+
stop(event, panInfo, dragAxes) {
|
|
11267
11284
|
const finalEvent = event || this.latestPointerEvent;
|
|
11268
11285
|
const finalPanInfo = panInfo || this.latestPanInfo;
|
|
11269
11286
|
const isDragging = this.isDragging;
|
|
@@ -11271,7 +11288,7 @@
|
|
|
11271
11288
|
if (!isDragging || !finalPanInfo || !finalEvent)
|
|
11272
11289
|
return;
|
|
11273
11290
|
const { velocity } = finalPanInfo;
|
|
11274
|
-
this.startAnimation(velocity);
|
|
11291
|
+
this.startAnimation(velocity, dragAxes);
|
|
11275
11292
|
const { onDragEnd } = this.getProps();
|
|
11276
11293
|
if (onDragEnd) {
|
|
11277
11294
|
frame.postRender(() => onDragEnd(finalEvent, finalPanInfo));
|
|
@@ -11284,12 +11301,11 @@
|
|
|
11284
11301
|
projection.isAnimationBlocked = false;
|
|
11285
11302
|
}
|
|
11286
11303
|
this.endPanSession();
|
|
11287
|
-
|
|
11288
|
-
if (!dragPropagation && this.openDragLock) {
|
|
11304
|
+
if (!this.getProps().dragPropagation && this.openDragLock) {
|
|
11289
11305
|
this.openDragLock();
|
|
11290
11306
|
this.openDragLock = null;
|
|
11291
11307
|
}
|
|
11292
|
-
animationState
|
|
11308
|
+
animationState?.setActive("whileDrag", false);
|
|
11293
11309
|
}
|
|
11294
11310
|
/**
|
|
11295
11311
|
* Clean up the pan session without modifying other drag state.
|
|
@@ -11297,31 +11313,42 @@
|
|
|
11297
11313
|
* without affecting projection animations or drag locks.
|
|
11298
11314
|
*/
|
|
11299
11315
|
endPanSession() {
|
|
11300
|
-
this.panSession
|
|
11316
|
+
this.panSession?.end();
|
|
11301
11317
|
this.panSession = undefined;
|
|
11302
11318
|
}
|
|
11303
|
-
updateAxis(axis,
|
|
11304
|
-
|
|
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();
|
|
11305
11325
|
// If we're not dragging this axis, do an early return.
|
|
11306
11326
|
if (!offset || !shouldDrag(axis, drag, this.currentDirection))
|
|
11307
11327
|
return;
|
|
11308
11328
|
const axisValue = this.getAxisMotionValue(axis);
|
|
11309
11329
|
let next = this.originPoint[axis] + offset[axis];
|
|
11310
11330
|
// Apply constraints
|
|
11311
|
-
|
|
11312
|
-
|
|
11331
|
+
const constraints = this.constraints && this.constraints[axis];
|
|
11332
|
+
if (constraints) {
|
|
11333
|
+
next = applyConstraints(next, constraints, this.elastic[axis]);
|
|
11313
11334
|
}
|
|
11314
11335
|
axisValue.set(next);
|
|
11315
11336
|
}
|
|
11316
11337
|
resolveConstraints() {
|
|
11317
11338
|
const { dragConstraints, dragElastic } = this.getProps();
|
|
11318
|
-
const
|
|
11319
|
-
|
|
11320
|
-
? this.visualElement.projection.measure(false)
|
|
11321
|
-
: this.visualElement.projection?.layout;
|
|
11339
|
+
const { projection } = this.visualElement;
|
|
11340
|
+
const layout = projection && (projection.layout || projection.measure(false));
|
|
11322
11341
|
const prevConstraints = this.constraints;
|
|
11323
11342
|
if (isElementConstraints(dragConstraints)) {
|
|
11324
|
-
|
|
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)) {
|
|
11325
11352
|
this.constraints = this.resolveRefConstraints();
|
|
11326
11353
|
}
|
|
11327
11354
|
}
|
|
@@ -11364,21 +11391,20 @@
|
|
|
11364
11391
|
if (!projection || !projection.layout)
|
|
11365
11392
|
return false;
|
|
11366
11393
|
/**
|
|
11367
|
-
* Refresh the
|
|
11368
|
-
*
|
|
11369
|
-
*
|
|
11370
|
-
*
|
|
11371
|
-
*
|
|
11372
|
-
*
|
|
11373
|
-
* Clear the cached scroll first so `updateScroll` bypasses its
|
|
11374
|
-
* 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).
|
|
11375
11400
|
*/
|
|
11376
|
-
|
|
11377
|
-
projection.root.scroll = undefined;
|
|
11378
|
-
projection.root.updateScroll();
|
|
11379
|
-
}
|
|
11401
|
+
this.scroll = measureScroll(projection);
|
|
11380
11402
|
const constraintsBox = measurePageBox(constraintsElement, projection.root, this.visualElement.getTransformPagePoint());
|
|
11381
|
-
|
|
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));
|
|
11382
11408
|
/**
|
|
11383
11409
|
* If there's an onMeasureDragConstraints listener we call it and
|
|
11384
11410
|
* if different constraints are returned, set constraints to that
|
|
@@ -11392,14 +11418,14 @@
|
|
|
11392
11418
|
}
|
|
11393
11419
|
return measuredConstraints;
|
|
11394
11420
|
}
|
|
11395
|
-
startAnimation(velocity) {
|
|
11421
|
+
startAnimation(velocity, dragAxes) {
|
|
11396
11422
|
const { drag, dragMomentum, dragElastic, dragTransition, dragSnapToOrigin, onDragTransitionEnd, } = this.getProps();
|
|
11397
11423
|
const constraints = this.constraints || {};
|
|
11398
11424
|
const momentumAnimations = eachAxis((axis) => {
|
|
11399
|
-
if (!shouldDrag(axis, drag, this.currentDirection)) {
|
|
11425
|
+
if (!shouldDrag(axis, dragAxes || drag, this.currentDirection)) {
|
|
11400
11426
|
return;
|
|
11401
11427
|
}
|
|
11402
|
-
let transition =
|
|
11428
|
+
let transition = constraints[axis] || {};
|
|
11403
11429
|
if (dragSnapToOrigin === true ||
|
|
11404
11430
|
dragSnapToOrigin === axis)
|
|
11405
11431
|
transition = { min: 0, max: 0 };
|
|
@@ -11505,7 +11531,7 @@
|
|
|
11505
11531
|
this.visualElement.current.style.transform = transformTemplate
|
|
11506
11532
|
? transformTemplate({}, "")
|
|
11507
11533
|
: "none";
|
|
11508
|
-
projection.root
|
|
11534
|
+
projection.root?.updateScroll();
|
|
11509
11535
|
projection.updateLayout();
|
|
11510
11536
|
/**
|
|
11511
11537
|
* Reset constraints so resolveConstraints() will recalculate them
|
|
@@ -11582,8 +11608,8 @@
|
|
|
11582
11608
|
};
|
|
11583
11609
|
const { projection } = this.visualElement;
|
|
11584
11610
|
const stopMeasureLayoutListener = projection.addEventListener("measure", measureDragConstraints);
|
|
11585
|
-
if (
|
|
11586
|
-
projection.root
|
|
11611
|
+
if (!projection.layout) {
|
|
11612
|
+
projection.root?.updateScroll();
|
|
11587
11613
|
projection.updateLayout();
|
|
11588
11614
|
}
|
|
11589
11615
|
frame.read(measureDragConstraints);
|
|
@@ -11599,9 +11625,15 @@
|
|
|
11599
11625
|
const stopLayoutUpdateListener = projection.addEventListener("didUpdate", (({ delta, hasLayoutChanged }) => {
|
|
11600
11626
|
if (this.isDragging && hasLayoutChanged) {
|
|
11601
11627
|
eachAxis((axis) => {
|
|
11602
|
-
|
|
11603
|
-
|
|
11628
|
+
/**
|
|
11629
|
+
* Only compensate axes this gesture drags. A
|
|
11630
|
+
* non-drag axis follows its layout instead, as
|
|
11631
|
+
* nothing would ever move it back (e.g. snapping
|
|
11632
|
+
* to origin only animates drag axes).
|
|
11633
|
+
*/
|
|
11634
|
+
if (!shouldDrag(axis, this.getProps().drag, this.currentDirection))
|
|
11604
11635
|
return;
|
|
11636
|
+
const motionValue = this.getAxisMotionValue(axis);
|
|
11605
11637
|
this.originPoint[axis] += delta[axis].translate;
|
|
11606
11638
|
motionValue.set(motionValue.get() + delta[axis].translate);
|
|
11607
11639
|
});
|
|
@@ -11612,22 +11644,14 @@
|
|
|
11612
11644
|
stopResizeListener();
|
|
11613
11645
|
stopPointerListener();
|
|
11614
11646
|
stopMeasureLayoutListener();
|
|
11615
|
-
stopLayoutUpdateListener
|
|
11616
|
-
stopResizeObservers
|
|
11647
|
+
stopLayoutUpdateListener();
|
|
11648
|
+
stopResizeObservers?.();
|
|
11617
11649
|
};
|
|
11618
11650
|
}
|
|
11619
11651
|
getProps() {
|
|
11620
11652
|
const props = this.readProps();
|
|
11621
|
-
const {
|
|
11622
|
-
return {
|
|
11623
|
-
...props,
|
|
11624
|
-
drag,
|
|
11625
|
-
dragDirectionLock,
|
|
11626
|
-
dragPropagation,
|
|
11627
|
-
dragConstraints,
|
|
11628
|
-
dragElastic,
|
|
11629
|
-
dragMomentum,
|
|
11630
|
-
};
|
|
11653
|
+
const { dragElastic = defaultElastic, dragMomentum = true } = props;
|
|
11654
|
+
return { ...props, dragElastic, dragMomentum };
|
|
11631
11655
|
}
|
|
11632
11656
|
}
|
|
11633
11657
|
/**
|
|
@@ -13962,6 +13986,12 @@
|
|
|
13962
13986
|
}
|
|
13963
13987
|
|
|
13964
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;
|
|
13965
13995
|
/**
|
|
13966
13996
|
* Diffs the element's animation props (one layer per AnimationType, highest
|
|
13967
13997
|
* priority wins each value) against the previous call and starts animations
|
|
@@ -13983,13 +14013,18 @@
|
|
|
13983
14013
|
let wasReset = false;
|
|
13984
14014
|
function animateChanges(changedActiveType) {
|
|
13985
14015
|
const { props, parent, manuallyAnimateOnMount } = visualElement;
|
|
14016
|
+
const { initial } = props;
|
|
13986
14017
|
const isMounting = isInitialRender || wasReset;
|
|
13987
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;
|
|
13988
14022
|
/**
|
|
13989
|
-
* Values removed from a type
|
|
13990
|
-
*
|
|
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.
|
|
13991
14026
|
*/
|
|
13992
|
-
const removedKeys = new
|
|
14027
|
+
const removedKeys = new Map();
|
|
13993
14028
|
/**
|
|
13994
14029
|
* Values claimed by active, higher-priority types.
|
|
13995
14030
|
*/
|
|
@@ -14007,6 +14042,13 @@
|
|
|
14007
14042
|
do
|
|
14008
14043
|
source = source.props.inherit !== false && source.parent;
|
|
14009
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);
|
|
14010
14052
|
reversePriorityOrder.forEach((type, i) => {
|
|
14011
14053
|
const typeState = state[type];
|
|
14012
14054
|
const ownProp = props[type];
|
|
@@ -14018,8 +14060,6 @@
|
|
|
14018
14060
|
: undefined;
|
|
14019
14061
|
const propIsVariant = isVariantLabel(prop);
|
|
14020
14062
|
const activeDelta = type === changedActiveType ? typeState.isActive : null;
|
|
14021
|
-
if (activeDelta === false)
|
|
14022
|
-
removedVariantIndex = i;
|
|
14023
14063
|
/**
|
|
14024
14064
|
* Inherited labels are animated by the parent through
|
|
14025
14065
|
* variantChildren, unless this element mounted after its parent.
|
|
@@ -14034,6 +14074,13 @@
|
|
|
14034
14074
|
typeof prop === "boolean") {
|
|
14035
14075
|
return;
|
|
14036
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;
|
|
14037
14084
|
/**
|
|
14038
14085
|
* Don't re-resolve a running exit: a changed custom would start
|
|
14039
14086
|
* new value animations that stop the originals, leaving the exit
|
|
@@ -14053,6 +14100,7 @@
|
|
|
14053
14100
|
let handledRemovedValues = false;
|
|
14054
14101
|
const definitionList = Array.isArray(prop) ? prop : [prop];
|
|
14055
14102
|
let resolvedValues = {};
|
|
14103
|
+
let out;
|
|
14056
14104
|
if (activeDelta !== false) {
|
|
14057
14105
|
for (const definition of definitionList) {
|
|
14058
14106
|
const { transition, transitionEnd, ...target } = resolveVariant(visualElement, definition, getTypeCustom(visualElement, type)) || {};
|
|
@@ -14061,6 +14109,11 @@
|
|
|
14061
14109
|
...target,
|
|
14062
14110
|
...transitionEnd,
|
|
14063
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;
|
|
14064
14117
|
}
|
|
14065
14118
|
}
|
|
14066
14119
|
const { prevResolvedValues } = typeState;
|
|
@@ -14076,13 +14129,18 @@
|
|
|
14076
14129
|
const valueHasChanged = Array.isArray(next) && Array.isArray(prev)
|
|
14077
14130
|
? !shallowCompare(next, prev) || variantDidChange
|
|
14078
14131
|
: next !== prev;
|
|
14079
|
-
|
|
14080
|
-
|
|
14081
|
-
|
|
14132
|
+
let outTransition;
|
|
14133
|
+
if (valueHasChanged && (next === undefined || next === null)) {
|
|
14134
|
+
/**
|
|
14135
|
+
* The highest-priority state a value leaves wins.
|
|
14136
|
+
*/
|
|
14137
|
+
removedKeys.has(key) ||
|
|
14138
|
+
removedKeys.set(key, typeState.prevOut);
|
|
14082
14139
|
}
|
|
14083
14140
|
else if (valueHasChanged ||
|
|
14084
14141
|
(next !== undefined && removedKeys.has(key))) {
|
|
14085
14142
|
shouldAnimateType = true;
|
|
14143
|
+
outTransition = removedKeys.get(key) || initialOut;
|
|
14086
14144
|
if (removedKeys.delete(key))
|
|
14087
14145
|
handledRemovedValues = true;
|
|
14088
14146
|
typeState.needsAnimating[key] = true;
|
|
@@ -14093,9 +14151,11 @@
|
|
|
14093
14151
|
else {
|
|
14094
14152
|
typeState.protectedKeys[key] = true;
|
|
14095
14153
|
}
|
|
14154
|
+
typeState.out[key] = outTransition;
|
|
14096
14155
|
}
|
|
14097
14156
|
typeState.prevProp = prop;
|
|
14098
14157
|
typeState.prevResolvedValues = resolvedValues;
|
|
14158
|
+
typeState.prevOut = out;
|
|
14099
14159
|
if (typeState.isActive) {
|
|
14100
14160
|
encounteredKeys = { ...encounteredKeys, ...resolvedValues };
|
|
14101
14161
|
}
|
|
@@ -14126,14 +14186,17 @@
|
|
|
14126
14186
|
* longer defines a value it once did, it stays where it is.
|
|
14127
14187
|
*/
|
|
14128
14188
|
if (removedKeys.size) {
|
|
14129
|
-
const { initial } = props;
|
|
14130
14189
|
const fallbackAnimation = {};
|
|
14131
|
-
const resolvedInitial =
|
|
14132
|
-
|
|
14133
|
-
|
|
14134
|
-
|
|
14135
|
-
|
|
14136
|
-
|
|
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
|
+
}
|
|
14137
14200
|
const motionValue = visualElement.getValue(key);
|
|
14138
14201
|
if (motionValue)
|
|
14139
14202
|
motionValue.liveStyle = true;
|
|
@@ -14164,6 +14227,9 @@
|
|
|
14164
14227
|
visualElement.baseTarget[key] = motionValue?.get();
|
|
14165
14228
|
}
|
|
14166
14229
|
});
|
|
14230
|
+
if (fallbackTransition) {
|
|
14231
|
+
fallbackAnimation.transition = fallbackTransition;
|
|
14232
|
+
}
|
|
14167
14233
|
animations.push({ animation: fallbackAnimation });
|
|
14168
14234
|
}
|
|
14169
14235
|
const blockAnimation = (isMounting && visualElement.blockInitialAnimation) ||
|
|
@@ -14223,6 +14289,7 @@
|
|
|
14223
14289
|
protectedKeys: {},
|
|
14224
14290
|
needsAnimating: {},
|
|
14225
14291
|
prevResolvedValues: {},
|
|
14292
|
+
out: {},
|
|
14226
14293
|
};
|
|
14227
14294
|
}
|
|
14228
14295
|
return state;
|
|
@@ -14341,6 +14408,18 @@
|
|
|
14341
14408
|
return duration * (repeat + 1) + repeatDelay * repeat;
|
|
14342
14409
|
}
|
|
14343
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
|
+
}
|
|
14344
14423
|
/**
|
|
14345
14424
|
* Given a absolute or relative time definition and current/prev time state of the sequence,
|
|
14346
14425
|
* calculate an absolute time for the next keyframes.
|
|
@@ -14359,7 +14438,7 @@
|
|
|
14359
14438
|
return Math.max(0, prev + parseFloat(next.slice(1)));
|
|
14360
14439
|
}
|
|
14361
14440
|
else {
|
|
14362
|
-
return
|
|
14441
|
+
return calcLabelTime(next, labels) ?? current;
|
|
14363
14442
|
}
|
|
14364
14443
|
}
|
|
14365
14444
|
|
|
@@ -14420,7 +14499,7 @@
|
|
|
14420
14499
|
|
|
14421
14500
|
const defaultSegmentEasing = "easeInOut";
|
|
14422
14501
|
const MAX_REPEAT = 20;
|
|
14423
|
-
function createAnimationsFromSequence(sequence, { defaultTransition = {}, ...sequenceTransition } = {}, scope, generators) {
|
|
14502
|
+
function createAnimationsFromSequence(sequence, { defaultTransition = {}, ...sequenceTransition } = {}, scope, generators, info) {
|
|
14424
14503
|
const defaultDuration = defaultTransition.duration || 0.3;
|
|
14425
14504
|
const animationDefinitions = new Map();
|
|
14426
14505
|
const sequences = new Map();
|
|
@@ -14433,196 +14512,233 @@
|
|
|
14433
14512
|
* Build the timeline by mapping over the sequence array and converting
|
|
14434
14513
|
* the definitions into keyframes and offsets with absolute time values.
|
|
14435
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.
|
|
14436
14520
|
*/
|
|
14437
|
-
|
|
14438
|
-
|
|
14439
|
-
|
|
14440
|
-
|
|
14441
|
-
|
|
14442
|
-
|
|
14443
|
-
|
|
14444
|
-
continue;
|
|
14445
|
-
}
|
|
14446
|
-
else if (!Array.isArray(segment)) {
|
|
14447
|
-
timeLabels.set(segment.name, calcNextTime(currentTime, segment.at, prevTime, timeLabels));
|
|
14448
|
-
continue;
|
|
14449
|
-
}
|
|
14450
|
-
let [subject, keyframes, transition = {}] = segment;
|
|
14451
|
-
/**
|
|
14452
|
-
* If a relative or absolute time value has been specified we need to resolve
|
|
14453
|
-
* it in relation to the currentTime.
|
|
14454
|
-
*/
|
|
14455
|
-
if (transition.at !== undefined) {
|
|
14456
|
-
currentTime = calcNextTime(currentTime, transition.at, prevTime, timeLabels);
|
|
14457
|
-
}
|
|
14458
|
-
/**
|
|
14459
|
-
* Keep track of the maximum duration in this definition. This will be
|
|
14460
|
-
* applied to currentTime once the definition has been parsed.
|
|
14461
|
-
*/
|
|
14462
|
-
let maxDuration = 0;
|
|
14463
|
-
const resolveValueSequence = (valueKeyframes, valueTransition, valueSequence, elementIndex = 0, numSubjects = 0) => {
|
|
14464
|
-
const valueKeyframesAsList = keyframesAsList(valueKeyframes);
|
|
14465
|
-
const { delay = 0, times = defaultOffset$1(valueKeyframesAsList), type = defaultTransition.type || "keyframes", repeat, repeatType, repeatDelay = 0, ...remainingTransition } = valueTransition;
|
|
14466
|
-
let { ease = defaultTransition.ease || "easeOut", duration } = valueTransition;
|
|
14467
|
-
/**
|
|
14468
|
-
* Resolve stagger() if defined.
|
|
14469
|
-
*/
|
|
14470
|
-
const calculatedDelay = typeof delay === "function"
|
|
14471
|
-
? delay(elementIndex, numSubjects)
|
|
14472
|
-
: 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];
|
|
14473
14528
|
/**
|
|
14474
|
-
* If this
|
|
14529
|
+
* If this is a timeline label, mark it and skip the rest of this iteration.
|
|
14475
14530
|
*/
|
|
14476
|
-
|
|
14477
|
-
|
|
14478
|
-
|
|
14479
|
-
|
|
14480
|
-
if (
|
|
14481
|
-
|
|
14482
|
-
|
|
14483
|
-
|
|
14484
|
-
|
|
14485
|
-
|
|
14486
|
-
|
|
14487
|
-
|
|
14488
|
-
|
|
14489
|
-
|
|
14490
|
-
|
|
14491
|
-
|
|
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);
|
|
14492
14549
|
}
|
|
14493
|
-
|
|
14494
|
-
|
|
14495
|
-
...remainingTransition,
|
|
14496
|
-
};
|
|
14497
|
-
if (duration !== undefined) {
|
|
14498
|
-
springTransition.duration = secondsToMilliseconds(duration);
|
|
14550
|
+
else {
|
|
14551
|
+
timeLabels.set(segment.name, start);
|
|
14499
14552
|
}
|
|
14500
|
-
|
|
14501
|
-
ease = springEasing.ease;
|
|
14502
|
-
duration = springEasing.duration;
|
|
14553
|
+
continue;
|
|
14503
14554
|
}
|
|
14504
|
-
|
|
14505
|
-
const startTime = currentTime + calculatedDelay;
|
|
14555
|
+
let [subject, keyframes, transition = {}] = segment;
|
|
14506
14556
|
/**
|
|
14507
|
-
* If
|
|
14557
|
+
* If a relative or absolute time value has been specified we need to resolve
|
|
14558
|
+
* it in relation to the currentTime.
|
|
14508
14559
|
*/
|
|
14509
|
-
if (
|
|
14510
|
-
|
|
14560
|
+
if (transition.at !== undefined) {
|
|
14561
|
+
currentTime = resolveTime(transition.at);
|
|
14511
14562
|
}
|
|
14512
14563
|
/**
|
|
14513
|
-
*
|
|
14564
|
+
* Keep track of the maximum duration in this definition. This will be
|
|
14565
|
+
* applied to currentTime once the definition has been parsed.
|
|
14514
14566
|
*/
|
|
14515
|
-
|
|
14516
|
-
|
|
14517
|
-
|
|
14518
|
-
|
|
14519
|
-
|
|
14520
|
-
* that will later be hydrated with the previous value.
|
|
14521
|
-
*/
|
|
14522
|
-
valueKeyframesAsList.length === 1 &&
|
|
14523
|
-
valueKeyframesAsList.unshift(null);
|
|
14524
|
-
/**
|
|
14525
|
-
* Segments can't express `repeat: Infinity` or very large
|
|
14526
|
-
* counts — they'd leave dead time after the segment or
|
|
14527
|
-
* explode the keyframe array. Ignore with a warning.
|
|
14528
|
-
*/
|
|
14529
|
-
if (repeat) {
|
|
14530
|
-
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.`);
|
|
14531
|
-
}
|
|
14532
|
-
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;
|
|
14533
14572
|
/**
|
|
14534
|
-
*
|
|
14535
|
-
* so it can be added to the per-iteration time offsets below
|
|
14536
|
-
* before they're normalized to 0-1.
|
|
14573
|
+
* Resolve stagger() if defined.
|
|
14537
14574
|
*/
|
|
14538
|
-
const
|
|
14539
|
-
|
|
14540
|
-
|
|
14541
|
-
const originalTimes = [...times];
|
|
14542
|
-
ease = Array.isArray(ease) ? [...ease] : [ease];
|
|
14543
|
-
const originalEase = [...ease];
|
|
14575
|
+
const calculatedDelay = typeof delay === "function"
|
|
14576
|
+
? delay(elementIndex, numSubjects)
|
|
14577
|
+
: delay;
|
|
14544
14578
|
/**
|
|
14545
|
-
*
|
|
14546
|
-
* backwards. mirror matches JSAnimation's mirroredGenerator:
|
|
14547
|
-
* reversed keyframes, easings unchanged. reverse matches
|
|
14548
|
-
* JSAnimation's iterationProgress = 1 - p: reversed
|
|
14549
|
-
* keyframes, easing array reversed AND each function easing
|
|
14550
|
-
* mapped through reverseEasing (string easings unchanged —
|
|
14551
|
-
* they're resolved later by the keyframes engine).
|
|
14579
|
+
* If this animation should and can use a spring, generate a spring easing function.
|
|
14552
14580
|
*/
|
|
14553
|
-
const
|
|
14554
|
-
|
|
14555
|
-
|
|
14556
|
-
|
|
14557
|
-
|
|
14558
|
-
|
|
14559
|
-
|
|
14560
|
-
|
|
14561
|
-
|
|
14562
|
-
|
|
14563
|
-
|
|
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);
|
|
14564
14605
|
}
|
|
14606
|
+
const springEasing = createGeneratorEasing(springTransition, absoluteDelta, createGenerator);
|
|
14607
|
+
ease = springEasing.ease;
|
|
14608
|
+
duration = springEasing.duration;
|
|
14565
14609
|
}
|
|
14566
|
-
|
|
14567
|
-
|
|
14568
|
-
|
|
14569
|
-
|
|
14570
|
-
|
|
14571
|
-
|
|
14572
|
-
|
|
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;
|
|
14617
|
+
}
|
|
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];
|
|
14573
14652
|
/**
|
|
14574
|
-
*
|
|
14575
|
-
*
|
|
14576
|
-
*
|
|
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).
|
|
14577
14660
|
*/
|
|
14578
|
-
|
|
14579
|
-
|
|
14580
|
-
|
|
14581
|
-
|
|
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
|
+
}
|
|
14582
14673
|
}
|
|
14583
|
-
|
|
14584
|
-
|
|
14585
|
-
|
|
14586
|
-
|
|
14587
|
-
|
|
14588
|
-
|
|
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
|
+
}
|
|
14589
14698
|
}
|
|
14699
|
+
normalizeTimes(times, repeat, repeatDelayUnits);
|
|
14590
14700
|
}
|
|
14591
|
-
|
|
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));
|
|
14592
14712
|
}
|
|
14593
|
-
|
|
14594
|
-
|
|
14595
|
-
|
|
14596
|
-
*/
|
|
14597
|
-
addKeyframes(valueSequence, valueKeyframesAsList, ease, times, startTime, targetTime);
|
|
14598
|
-
maxDuration = Math.max(calculatedDelay + duration, maxDuration);
|
|
14599
|
-
totalDuration = Math.max(targetTime, totalDuration);
|
|
14600
|
-
};
|
|
14601
|
-
if (isMotionValue(subject)) {
|
|
14602
|
-
const subjectSequence = getSubjectSequence(subject, sequences);
|
|
14603
|
-
resolveValueSequence(keyframes, transition, getValueSequence("default", subjectSequence));
|
|
14604
|
-
}
|
|
14605
|
-
else {
|
|
14606
|
-
const subjects = resolveSubjects(subject, keyframes, scope, elementCache);
|
|
14607
|
-
const numSubjects = subjects.length;
|
|
14608
|
-
/**
|
|
14609
|
-
* For every element in this segment, process the defined values.
|
|
14610
|
-
*/
|
|
14611
|
-
for (let subjectIndex = 0; subjectIndex < numSubjects; subjectIndex++) {
|
|
14713
|
+
else {
|
|
14714
|
+
const subjects = resolveSubjects(subject, keyframes, scope, elementCache);
|
|
14715
|
+
const numSubjects = subjects.length;
|
|
14612
14716
|
/**
|
|
14613
|
-
*
|
|
14717
|
+
* For every element in this segment, process the defined values.
|
|
14614
14718
|
*/
|
|
14615
|
-
|
|
14616
|
-
|
|
14617
|
-
|
|
14618
|
-
|
|
14619
|
-
|
|
14620
|
-
|
|
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
|
+
}
|
|
14621
14730
|
}
|
|
14622
14731
|
}
|
|
14732
|
+
prevTime = currentTime;
|
|
14733
|
+
currentTime += maxDuration;
|
|
14734
|
+
sequenceEnd = Math.max(sequenceEnd, currentTime);
|
|
14623
14735
|
}
|
|
14624
|
-
|
|
14625
|
-
|
|
14736
|
+
return sequenceEnd;
|
|
14737
|
+
};
|
|
14738
|
+
resolveSequence(sequence, 0);
|
|
14739
|
+
if (info) {
|
|
14740
|
+
info.duration = totalDuration;
|
|
14741
|
+
info.labels = timeLabels;
|
|
14626
14742
|
}
|
|
14627
14743
|
/**
|
|
14628
14744
|
* For every element and value combination create a new animation.
|
|
@@ -14770,7 +14886,7 @@
|
|
|
14770
14886
|
return animations;
|
|
14771
14887
|
}
|
|
14772
14888
|
|
|
14773
|
-
function animateSequence(sequence, options, scope) {
|
|
14889
|
+
function animateSequence(sequence, options, scope, info) {
|
|
14774
14890
|
const animations = [];
|
|
14775
14891
|
/**
|
|
14776
14892
|
* Pre-process: replace function segments with MotionValue segments,
|
|
@@ -14793,13 +14909,515 @@
|
|
|
14793
14909
|
}
|
|
14794
14910
|
return segment;
|
|
14795
14911
|
});
|
|
14796
|
-
const animationDefinitions = createAnimationsFromSequence(processedSequence, options, scope, { spring });
|
|
14912
|
+
const animationDefinitions = createAnimationsFromSequence(processedSequence, options, scope, { spring }, info);
|
|
14797
14913
|
animationDefinitions.forEach(({ keyframes, transition }, subject) => {
|
|
14798
14914
|
animations.push(...animateSubject(subject, keyframes, transition));
|
|
14799
14915
|
});
|
|
14800
14916
|
return animations;
|
|
14801
14917
|
}
|
|
14802
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
|
+
|
|
14803
15421
|
function isSequence(value) {
|
|
14804
15422
|
return Array.isArray(value) && value.some(Array.isArray);
|
|
14805
15423
|
}
|
|
@@ -14813,8 +15431,6 @@
|
|
|
14813
15431
|
* Implementation
|
|
14814
15432
|
*/
|
|
14815
15433
|
function scopedAnimate(subjectOrSequence, optionsOrKeyframes, options) {
|
|
14816
|
-
let animations = [];
|
|
14817
|
-
let animationOnComplete;
|
|
14818
15434
|
const motionScope = activeScope.current;
|
|
14819
15435
|
const scopeTransition = motionScope?.transition;
|
|
14820
15436
|
const inherited = {};
|
|
@@ -14822,49 +15438,58 @@
|
|
|
14822
15438
|
inherited.reduceMotion = reduceMotion;
|
|
14823
15439
|
if (skipAnimations !== undefined)
|
|
14824
15440
|
inherited.skipAnimations = skipAnimations;
|
|
15441
|
+
let timeline;
|
|
14825
15442
|
if (isSequence(subjectOrSequence)) {
|
|
14826
|
-
const
|
|
14827
|
-
|
|
14828
|
-
|
|
14829
|
-
|
|
15443
|
+
const sequenceOptions = {
|
|
15444
|
+
...inherited,
|
|
15445
|
+
...optionsOrKeyframes,
|
|
15446
|
+
scope,
|
|
15447
|
+
};
|
|
14830
15448
|
if (scopeTransition) {
|
|
14831
15449
|
sequenceOptions.defaultTransition = {
|
|
14832
15450
|
...scopeTransition,
|
|
14833
15451
|
...sequenceOptions.defaultTransition,
|
|
14834
15452
|
};
|
|
14835
15453
|
}
|
|
14836
|
-
|
|
15454
|
+
timeline = new Timeline(subjectOrSequence, sequenceOptions);
|
|
14837
15455
|
}
|
|
14838
15456
|
else {
|
|
14839
15457
|
// Extract top-level onComplete so it doesn't get applied per-value
|
|
14840
15458
|
const { onComplete, ...rest } = options || {};
|
|
14841
|
-
|
|
14842
|
-
animationOnComplete = onComplete;
|
|
14843
|
-
}
|
|
14844
|
-
animations = animateSubject(subjectOrSequence, optionsOrKeyframes, {
|
|
15459
|
+
const transition = {
|
|
14845
15460
|
...inherited,
|
|
14846
15461
|
...scopeTransition,
|
|
14847
15462
|
...rest,
|
|
14848
|
-
}
|
|
14849
|
-
|
|
14850
|
-
|
|
14851
|
-
|
|
14852
|
-
|
|
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));
|
|
14853
15483
|
}
|
|
14854
15484
|
if (motionScope) {
|
|
14855
|
-
|
|
15485
|
+
timeline.finished.then(motionScope.add(() => timeline.stop()));
|
|
14856
15486
|
}
|
|
14857
|
-
|
|
14858
|
-
scope.animations.push(animation);
|
|
14859
|
-
animation.finished.then(() => {
|
|
14860
|
-
removeItem(scope.animations, animation);
|
|
14861
|
-
});
|
|
14862
|
-
}
|
|
14863
|
-
return animation;
|
|
15487
|
+
return timeline;
|
|
14864
15488
|
}
|
|
14865
15489
|
return scopedAnimate;
|
|
14866
15490
|
}
|
|
14867
|
-
const animate = Object.assign(
|
|
15491
|
+
const animate = /* @__PURE__ */ Object.assign(
|
|
15492
|
+
/* @__PURE__ */ createScopedAnimate(), {
|
|
14868
15493
|
/**
|
|
14869
15494
|
* Register an effect so `animate()` can animate the subjects it
|
|
14870
15495
|
* claims, for instance `animate.addEffect(threeEffect)`. The most
|
|
@@ -14882,7 +15507,9 @@
|
|
|
14882
15507
|
}
|
|
14883
15508
|
const elements = resolveElements(elementOrSelector, scope);
|
|
14884
15509
|
const numElements = elements.length;
|
|
14885
|
-
|
|
15510
|
+
typeof process !== "undefined" &&
|
|
15511
|
+
"development" !== "production" &&
|
|
15512
|
+
exports.invariant(Boolean(numElements), "No valid elements provided.", "no-valid-elements");
|
|
14886
15513
|
/**
|
|
14887
15514
|
* WAAPI doesn't support interrupting animations.
|
|
14888
15515
|
*
|
|
@@ -15191,22 +15818,26 @@
|
|
|
15191
15818
|
const p = (i + progress(at(i), at(i + 1), v)) / n;
|
|
15192
15819
|
return reverse ? 1 - p : p;
|
|
15193
15820
|
}
|
|
15194
|
-
|
|
15195
|
-
|
|
15196
|
-
|
|
15197
|
-
|
|
15198
|
-
|
|
15199
|
-
|
|
15200
|
-
|
|
15201
|
-
|
|
15202
|
-
|
|
15203
|
-
|
|
15204
|
-
|
|
15205
|
-
|
|
15206
|
-
|
|
15207
|
-
|
|
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));
|
|
15208
15840
|
}
|
|
15209
|
-
axisInfo.progress = clamp(0, 1, offsetsToProgress(axisInfo.offset, axisInfo.current));
|
|
15210
15841
|
}
|
|
15211
15842
|
|
|
15212
15843
|
function calcInset(element, container) {
|
|
@@ -15712,6 +16343,7 @@
|
|
|
15712
16343
|
exports.buildSVGAttrs = buildSVGAttrs;
|
|
15713
16344
|
exports.buildSVGPath = buildSVGPath;
|
|
15714
16345
|
exports.buildTransform = buildTransform$1;
|
|
16346
|
+
exports.buildTransformer = buildTransformer;
|
|
15715
16347
|
exports.calcAxisDelta = calcAxisDelta;
|
|
15716
16348
|
exports.calcBoxDelta = calcBoxDelta;
|
|
15717
16349
|
exports.calcChildStagger = calcChildStagger;
|
|
@@ -15890,6 +16522,7 @@
|
|
|
15890
16522
|
exports.positionalKeys = positionalKeys;
|
|
15891
16523
|
exports.prefersReducedMotion = prefersReducedMotion;
|
|
15892
16524
|
exports.press = press;
|
|
16525
|
+
exports.processFrame = processFrame;
|
|
15893
16526
|
exports.progress = progress;
|
|
15894
16527
|
exports.progressPercentage = progressPercentage;
|
|
15895
16528
|
exports.propEffect = propEffect;
|