framer-motion 12.35.0 → 12.35.2
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/cjs/client.js +1 -1
- package/dist/cjs/dom.js +1 -1
- package/dist/cjs/dom.js.map +1 -1
- package/dist/cjs/{feature-bundle-jADFMKLx.js → feature-bundle-DqHxNjy5.js} +14 -4
- package/dist/cjs/feature-bundle-DqHxNjy5.js.map +1 -0
- package/dist/cjs/index.js +77 -5
- package/dist/cjs/index.js.map +1 -1
- package/dist/dom-mini.d.ts +17 -5
- package/dist/dom-mini.js +1 -1
- package/dist/dom.d.ts +18 -6
- package/dist/dom.js +1 -1
- package/dist/es/components/AnimatePresence/PopChild.mjs +4 -3
- package/dist/es/components/AnimatePresence/PopChild.mjs.map +1 -1
- package/dist/es/events/event-info.mjs +1 -3
- package/dist/es/events/event-info.mjs.map +1 -1
- package/dist/es/gestures/pan/PanSession.mjs +12 -0
- package/dist/es/gestures/pan/PanSession.mjs.map +1 -1
- package/dist/es/index.mjs +1 -0
- package/dist/es/index.mjs.map +1 -1
- package/dist/es/render/dom/scroll/info.mjs +1 -1
- package/dist/es/render/dom/scroll/info.mjs.map +1 -1
- package/dist/es/utils/transform-rotated-parent.mjs +72 -0
- package/dist/es/utils/transform-rotated-parent.mjs.map +1 -0
- package/dist/framer-motion.dev.js +294 -88
- package/dist/framer-motion.js +1 -1
- package/dist/mini.js +1 -1
- package/dist/size-rollup-animate.js +1 -1
- package/dist/size-rollup-animate.js.map +1 -1
- package/dist/size-rollup-dom-animation-assets.js +1 -1
- package/dist/size-rollup-dom-animation.js +1 -1
- package/dist/size-rollup-dom-max-assets.js +1 -1
- package/dist/size-rollup-dom-max.js +1 -1
- package/dist/size-rollup-m.js +1 -1
- package/dist/size-rollup-m.js.map +1 -1
- package/dist/size-rollup-motion.js +1 -1
- package/dist/size-rollup-motion.js.map +1 -1
- package/dist/size-rollup-scroll.js +1 -1
- package/dist/size-rollup-scroll.js.map +1 -1
- package/dist/size-rollup-waapi-animate.js +1 -1
- package/dist/size-rollup-waapi-animate.js.map +1 -1
- package/dist/types/index.d.ts +50 -6
- package/package.json +3 -3
- package/dist/cjs/feature-bundle-jADFMKLx.js.map +0 -1
|
@@ -834,8 +834,7 @@
|
|
|
834
834
|
function parseComplexValue(v) {
|
|
835
835
|
return analyseComplexValue(v).values;
|
|
836
836
|
}
|
|
837
|
-
function
|
|
838
|
-
const { split, types } = analyseComplexValue(source);
|
|
837
|
+
function buildTransformer({ split, types }) {
|
|
839
838
|
const numSections = split.length;
|
|
840
839
|
return (v) => {
|
|
841
840
|
let output = "";
|
|
@@ -857,11 +856,34 @@
|
|
|
857
856
|
return output;
|
|
858
857
|
};
|
|
859
858
|
}
|
|
859
|
+
function createTransformer(source) {
|
|
860
|
+
return buildTransformer(analyseComplexValue(source));
|
|
861
|
+
}
|
|
860
862
|
const convertNumbersToZero = (v) => typeof v === "number" ? 0 : color.test(v) ? color.getAnimatableNone(v) : v;
|
|
863
|
+
/**
|
|
864
|
+
* Convert a parsed value to its zero equivalent, but preserve numbers
|
|
865
|
+
* that act as divisors in CSS calc() expressions.
|
|
866
|
+
*
|
|
867
|
+
* analyseComplexValue extracts numbers from CSS strings and puts the
|
|
868
|
+
* surrounding text into a `split` template array. For example:
|
|
869
|
+
* "calc(var(--gap) / 5)" → values: [var(--gap), 5]
|
|
870
|
+
* split: ["calc(", " / ", ")"]
|
|
871
|
+
*
|
|
872
|
+
* When building a zero-equivalent for animation, naively zeroing all
|
|
873
|
+
* numbers turns the divisor into 0 → "calc(var(--gap) / 0)" → NaN.
|
|
874
|
+
* We detect this by checking whether the text preceding a number
|
|
875
|
+
* (split[i]) ends with "/" — the CSS calc division operator.
|
|
876
|
+
*/
|
|
877
|
+
const convertToZero = (value, splitBefore) => {
|
|
878
|
+
if (typeof value === "number") {
|
|
879
|
+
return splitBefore?.trim().endsWith("/") ? value : 0;
|
|
880
|
+
}
|
|
881
|
+
return convertNumbersToZero(value);
|
|
882
|
+
};
|
|
861
883
|
function getAnimatableNone$1(v) {
|
|
862
|
-
const
|
|
863
|
-
const transformer =
|
|
864
|
-
return transformer(
|
|
884
|
+
const info = analyseComplexValue(v);
|
|
885
|
+
const transformer = buildTransformer(info);
|
|
886
|
+
return transformer(info.values.map((value, i) => convertToZero(value, info.split[i])));
|
|
865
887
|
}
|
|
866
888
|
const complex = {
|
|
867
889
|
test,
|
|
@@ -1140,12 +1162,6 @@
|
|
|
1140
1162
|
};
|
|
1141
1163
|
}
|
|
1142
1164
|
|
|
1143
|
-
const velocitySampleDuration = 5; // ms
|
|
1144
|
-
function calcGeneratorVelocity(resolveValue, t, current) {
|
|
1145
|
-
const prevT = Math.max(t - velocitySampleDuration, 0);
|
|
1146
|
-
return velocityPerSecond(current - resolveValue(prevT), t - prevT);
|
|
1147
|
-
}
|
|
1148
|
-
|
|
1149
1165
|
const springDefaults = {
|
|
1150
1166
|
// Default spring physics
|
|
1151
1167
|
stiffness: 100,
|
|
@@ -1171,7 +1187,20 @@
|
|
|
1171
1187
|
minDamping: 0.05,
|
|
1172
1188
|
maxDamping: 1,
|
|
1173
1189
|
};
|
|
1174
|
-
|
|
1190
|
+
function calcAngularFreq(undampedFreq, dampingRatio) {
|
|
1191
|
+
return undampedFreq * Math.sqrt(1 - dampingRatio * dampingRatio);
|
|
1192
|
+
}
|
|
1193
|
+
const rootIterations = 12;
|
|
1194
|
+
function approximateRoot(envelope, derivative, initialGuess) {
|
|
1195
|
+
let result = initialGuess;
|
|
1196
|
+
for (let i = 1; i < rootIterations; i++) {
|
|
1197
|
+
result = result - envelope(result) / derivative(result);
|
|
1198
|
+
}
|
|
1199
|
+
return result;
|
|
1200
|
+
}
|
|
1201
|
+
/**
|
|
1202
|
+
* This is ported from the Framer implementation of duration-based spring resolution.
|
|
1203
|
+
*/
|
|
1175
1204
|
const safeMin = 0.001;
|
|
1176
1205
|
function findSpring({ duration = springDefaults.duration, bounce = springDefaults.bounce, velocity = springDefaults.velocity, mass = springDefaults.mass, }) {
|
|
1177
1206
|
let envelope;
|
|
@@ -1240,18 +1269,6 @@
|
|
|
1240
1269
|
};
|
|
1241
1270
|
}
|
|
1242
1271
|
}
|
|
1243
|
-
const rootIterations = 12;
|
|
1244
|
-
function approximateRoot(envelope, derivative, initialGuess) {
|
|
1245
|
-
let result = initialGuess;
|
|
1246
|
-
for (let i = 1; i < rootIterations; i++) {
|
|
1247
|
-
result = result - envelope(result) / derivative(result);
|
|
1248
|
-
}
|
|
1249
|
-
return result;
|
|
1250
|
-
}
|
|
1251
|
-
function calcAngularFreq(undampedFreq, dampingRatio) {
|
|
1252
|
-
return undampedFreq * Math.sqrt(1 - dampingRatio * dampingRatio);
|
|
1253
|
-
}
|
|
1254
|
-
|
|
1255
1272
|
const durationKeys = ["duration", "bounce"];
|
|
1256
1273
|
const physicsKeys = ["stiffness", "damping", "mass"];
|
|
1257
1274
|
function isSpringType(options, keys) {
|
|
@@ -1339,19 +1356,37 @@
|
|
|
1339
1356
|
? springDefaults.restDelta.granular
|
|
1340
1357
|
: springDefaults.restDelta.default);
|
|
1341
1358
|
let resolveSpring;
|
|
1359
|
+
let resolveVelocity;
|
|
1360
|
+
// Underdamped coefficients, hoisted for use in the inlined next() hot path
|
|
1361
|
+
let angularFreq;
|
|
1362
|
+
let A;
|
|
1363
|
+
let sinCoeff;
|
|
1364
|
+
let cosCoeff;
|
|
1342
1365
|
if (dampingRatio < 1) {
|
|
1343
|
-
|
|
1366
|
+
angularFreq = calcAngularFreq(undampedAngularFreq, dampingRatio);
|
|
1367
|
+
A =
|
|
1368
|
+
(initialVelocity +
|
|
1369
|
+
dampingRatio * undampedAngularFreq * initialDelta) /
|
|
1370
|
+
angularFreq;
|
|
1344
1371
|
// Underdamped spring
|
|
1345
1372
|
resolveSpring = (t) => {
|
|
1346
1373
|
const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
|
|
1347
1374
|
return (target -
|
|
1348
1375
|
envelope *
|
|
1349
|
-
((
|
|
1350
|
-
dampingRatio * undampedAngularFreq * initialDelta) /
|
|
1351
|
-
angularFreq) *
|
|
1352
|
-
Math.sin(angularFreq * t) +
|
|
1376
|
+
(A * Math.sin(angularFreq * t) +
|
|
1353
1377
|
initialDelta * Math.cos(angularFreq * t)));
|
|
1354
1378
|
};
|
|
1379
|
+
// Analytical derivative of underdamped spring (px/ms)
|
|
1380
|
+
sinCoeff =
|
|
1381
|
+
dampingRatio * undampedAngularFreq * A + initialDelta * angularFreq;
|
|
1382
|
+
cosCoeff =
|
|
1383
|
+
dampingRatio * undampedAngularFreq * initialDelta - A * angularFreq;
|
|
1384
|
+
resolveVelocity = (t) => {
|
|
1385
|
+
const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
|
|
1386
|
+
return envelope *
|
|
1387
|
+
(sinCoeff * Math.sin(angularFreq * t) +
|
|
1388
|
+
cosCoeff * Math.cos(angularFreq * t));
|
|
1389
|
+
};
|
|
1355
1390
|
}
|
|
1356
1391
|
else if (dampingRatio === 1) {
|
|
1357
1392
|
// Critically damped spring
|
|
@@ -1359,6 +1394,10 @@
|
|
|
1359
1394
|
Math.exp(-undampedAngularFreq * t) *
|
|
1360
1395
|
(initialDelta +
|
|
1361
1396
|
(initialVelocity + undampedAngularFreq * initialDelta) * t);
|
|
1397
|
+
// Analytical derivative of critically damped spring (px/ms)
|
|
1398
|
+
const C = initialVelocity + undampedAngularFreq * initialDelta;
|
|
1399
|
+
resolveVelocity = (t) => Math.exp(-undampedAngularFreq * t) *
|
|
1400
|
+
(undampedAngularFreq * C * t - initialVelocity);
|
|
1362
1401
|
}
|
|
1363
1402
|
else {
|
|
1364
1403
|
// Overdamped spring
|
|
@@ -1377,28 +1416,50 @@
|
|
|
1377
1416
|
Math.cosh(freqForT))) /
|
|
1378
1417
|
dampedAngularFreq);
|
|
1379
1418
|
};
|
|
1419
|
+
// Analytical derivative of overdamped spring (px/ms)
|
|
1420
|
+
const P = (initialVelocity +
|
|
1421
|
+
dampingRatio * undampedAngularFreq * initialDelta) /
|
|
1422
|
+
dampedAngularFreq;
|
|
1423
|
+
const sinhCoeff = dampingRatio * undampedAngularFreq * P - initialDelta * dampedAngularFreq;
|
|
1424
|
+
const coshCoeff = dampingRatio * undampedAngularFreq * initialDelta - P * dampedAngularFreq;
|
|
1425
|
+
resolveVelocity = (t) => {
|
|
1426
|
+
const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
|
|
1427
|
+
const freqForT = Math.min(dampedAngularFreq * t, 300);
|
|
1428
|
+
return envelope *
|
|
1429
|
+
(sinhCoeff * Math.sinh(freqForT) +
|
|
1430
|
+
coshCoeff * Math.cosh(freqForT));
|
|
1431
|
+
};
|
|
1380
1432
|
}
|
|
1381
1433
|
const generator = {
|
|
1382
1434
|
calculatedDuration: isResolvedFromDuration ? duration || null : null,
|
|
1435
|
+
velocity: (t) => secondsToMilliseconds(resolveVelocity(t)),
|
|
1383
1436
|
next: (t) => {
|
|
1437
|
+
/**
|
|
1438
|
+
* For underdamped physics springs we need both position and
|
|
1439
|
+
* velocity each tick. Compute shared trig values once to avoid
|
|
1440
|
+
* duplicate Math.exp/sin/cos calls on the hot path.
|
|
1441
|
+
*/
|
|
1442
|
+
if (!isResolvedFromDuration && dampingRatio < 1) {
|
|
1443
|
+
const envelope = Math.exp(-dampingRatio * undampedAngularFreq * t);
|
|
1444
|
+
const sin = Math.sin(angularFreq * t);
|
|
1445
|
+
const cos = Math.cos(angularFreq * t);
|
|
1446
|
+
const current = target -
|
|
1447
|
+
envelope *
|
|
1448
|
+
(A * sin + initialDelta * cos);
|
|
1449
|
+
const currentVelocity = secondsToMilliseconds(envelope *
|
|
1450
|
+
(sinCoeff * sin + cosCoeff * cos));
|
|
1451
|
+
state.done =
|
|
1452
|
+
Math.abs(currentVelocity) <= restSpeed &&
|
|
1453
|
+
Math.abs(target - current) <= restDelta;
|
|
1454
|
+
state.value = state.done ? target : current;
|
|
1455
|
+
return state;
|
|
1456
|
+
}
|
|
1384
1457
|
const current = resolveSpring(t);
|
|
1385
1458
|
if (!isResolvedFromDuration) {
|
|
1386
|
-
|
|
1387
|
-
/**
|
|
1388
|
-
* We only need to calculate velocity for under-damped springs
|
|
1389
|
-
* as over- and critically-damped springs can't overshoot, so
|
|
1390
|
-
* checking only for displacement is enough.
|
|
1391
|
-
*/
|
|
1392
|
-
if (dampingRatio < 1) {
|
|
1393
|
-
currentVelocity =
|
|
1394
|
-
t === 0
|
|
1395
|
-
? secondsToMilliseconds(initialVelocity)
|
|
1396
|
-
: calcGeneratorVelocity(resolveSpring, t, current);
|
|
1397
|
-
}
|
|
1398
|
-
const isBelowVelocityThreshold = Math.abs(currentVelocity) <= restSpeed;
|
|
1399
|
-
const isBelowDisplacementThreshold = Math.abs(target - current) <= restDelta;
|
|
1459
|
+
const currentVelocity = secondsToMilliseconds(resolveVelocity(t));
|
|
1400
1460
|
state.done =
|
|
1401
|
-
|
|
1461
|
+
Math.abs(currentVelocity) <= restSpeed &&
|
|
1462
|
+
Math.abs(target - current) <= restDelta;
|
|
1402
1463
|
}
|
|
1403
1464
|
else {
|
|
1404
1465
|
state.done = t >= duration;
|
|
@@ -1423,6 +1484,12 @@
|
|
|
1423
1484
|
return options;
|
|
1424
1485
|
};
|
|
1425
1486
|
|
|
1487
|
+
const velocitySampleDuration = 5; // ms
|
|
1488
|
+
function getGeneratorVelocity(resolveValue, t, current) {
|
|
1489
|
+
const prevT = Math.max(t - velocitySampleDuration, 0);
|
|
1490
|
+
return velocityPerSecond(current - resolveValue(prevT), t - prevT);
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1426
1493
|
function inertia({ keyframes, velocity = 0.0, power = 0.8, timeConstant = 325, bounceDamping = 10, bounceStiffness = 500, modifyTarget, min, max, restDelta = 0.5, restSpeed, }) {
|
|
1427
1494
|
const origin = keyframes[0];
|
|
1428
1495
|
const state = {
|
|
@@ -1468,7 +1535,7 @@
|
|
|
1468
1535
|
timeReachedBoundary = t;
|
|
1469
1536
|
spring$1 = spring({
|
|
1470
1537
|
keyframes: [state.value, nearestBoundary(state.value)],
|
|
1471
|
-
velocity:
|
|
1538
|
+
velocity: getGeneratorVelocity(calcLatest, t, state.value), // TODO: This should be passing * 1000
|
|
1472
1539
|
damping: bounceDamping,
|
|
1473
1540
|
stiffness: bounceStiffness,
|
|
1474
1541
|
restDelta,
|
|
@@ -1868,7 +1935,7 @@
|
|
|
1868
1935
|
const state = isInDelayPhase
|
|
1869
1936
|
? { done: false, value: keyframes[0] }
|
|
1870
1937
|
: frameGenerator.next(elapsed);
|
|
1871
|
-
if (mixKeyframes) {
|
|
1938
|
+
if (mixKeyframes && !isInDelayPhase) {
|
|
1872
1939
|
state.value = mixKeyframes(state.value);
|
|
1873
1940
|
}
|
|
1874
1941
|
let { done } = state;
|
|
@@ -1921,16 +1988,42 @@
|
|
|
1921
1988
|
else if (this.driver) {
|
|
1922
1989
|
this.startTime = this.driver.now() - newTime / this.playbackSpeed;
|
|
1923
1990
|
}
|
|
1924
|
-
this.driver
|
|
1991
|
+
if (this.driver) {
|
|
1992
|
+
this.driver.start(false);
|
|
1993
|
+
}
|
|
1994
|
+
else {
|
|
1995
|
+
this.startTime = 0;
|
|
1996
|
+
this.state = "paused";
|
|
1997
|
+
this.holdTime = newTime;
|
|
1998
|
+
this.tick(newTime);
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
/**
|
|
2002
|
+
* Returns the generator's velocity at the current time in units/second.
|
|
2003
|
+
* Uses the analytical derivative when available (springs), avoiding
|
|
2004
|
+
* the MotionValue's frame-dependent velocity estimation.
|
|
2005
|
+
*/
|
|
2006
|
+
getGeneratorVelocity() {
|
|
2007
|
+
const t = this.currentTime;
|
|
2008
|
+
if (t <= 0)
|
|
2009
|
+
return this.options.velocity || 0;
|
|
2010
|
+
if (this.generator.velocity) {
|
|
2011
|
+
return this.generator.velocity(t);
|
|
2012
|
+
}
|
|
2013
|
+
// Fallback: finite difference
|
|
2014
|
+
const current = this.generator.next(t).value;
|
|
2015
|
+
return getGeneratorVelocity((s) => this.generator.next(s).value, t, current);
|
|
1925
2016
|
}
|
|
1926
2017
|
get speed() {
|
|
1927
2018
|
return this.playbackSpeed;
|
|
1928
2019
|
}
|
|
1929
2020
|
set speed(newSpeed) {
|
|
1930
|
-
this.updateTime(time.now());
|
|
1931
2021
|
const hasChanged = this.playbackSpeed !== newSpeed;
|
|
2022
|
+
if (hasChanged && this.driver) {
|
|
2023
|
+
this.updateTime(time.now());
|
|
2024
|
+
}
|
|
1932
2025
|
this.playbackSpeed = newSpeed;
|
|
1933
|
-
if (hasChanged) {
|
|
2026
|
+
if (hasChanged && this.driver) {
|
|
1934
2027
|
this.time = millisecondsToSeconds(this.currentTime);
|
|
1935
2028
|
}
|
|
1936
2029
|
}
|
|
@@ -2540,9 +2633,13 @@
|
|
|
2540
2633
|
return millisecondsToSeconds(Number(this.animation.currentTime) || 0);
|
|
2541
2634
|
}
|
|
2542
2635
|
set time(newTime) {
|
|
2636
|
+
const wasFinished = this.finishedTime !== null;
|
|
2543
2637
|
this.manualStartTime = null;
|
|
2544
2638
|
this.finishedTime = null;
|
|
2545
2639
|
this.animation.currentTime = secondsToMilliseconds(newTime);
|
|
2640
|
+
if (wasFinished) {
|
|
2641
|
+
this.animation.pause();
|
|
2642
|
+
}
|
|
2546
2643
|
}
|
|
2547
2644
|
/**
|
|
2548
2645
|
* The playback speed of the animation.
|
|
@@ -2632,7 +2729,12 @@
|
|
|
2632
2729
|
*/
|
|
2633
2730
|
replaceTransitionType(options);
|
|
2634
2731
|
super(options);
|
|
2635
|
-
|
|
2732
|
+
/**
|
|
2733
|
+
* Only set startTime when the animation should autoplay.
|
|
2734
|
+
* Setting startTime on a paused WAAPI animation unpauses it
|
|
2735
|
+
* (per the WAAPI spec), which breaks autoplay: false.
|
|
2736
|
+
*/
|
|
2737
|
+
if (options.startTime !== undefined && options.autoplay !== false) {
|
|
2636
2738
|
this.startTime = options.startTime;
|
|
2637
2739
|
}
|
|
2638
2740
|
this.options = options;
|
|
@@ -2664,7 +2766,16 @@
|
|
|
2664
2766
|
*/
|
|
2665
2767
|
const sampleTime = Math.max(sampleDelta, time.now() - this.startTime);
|
|
2666
2768
|
const delta = clamp(0, sampleDelta, sampleTime - sampleDelta);
|
|
2667
|
-
|
|
2769
|
+
const current = sampleAnimation.sample(sampleTime).value;
|
|
2770
|
+
/**
|
|
2771
|
+
* Write the estimated value to inline style so it persists
|
|
2772
|
+
* after cancel(), covering the async gap before the next
|
|
2773
|
+
* animation starts.
|
|
2774
|
+
*/
|
|
2775
|
+
const { name } = this.options;
|
|
2776
|
+
if (element && name)
|
|
2777
|
+
setStyle(element, name, current);
|
|
2778
|
+
motionValue.setWithVelocity(sampleAnimation.sample(Math.max(0, sampleTime - delta)).value, current, delta);
|
|
2668
2779
|
sampleAnimation.stop();
|
|
2669
2780
|
}
|
|
2670
2781
|
}
|
|
@@ -4405,19 +4516,14 @@
|
|
|
4405
4516
|
get(name) {
|
|
4406
4517
|
return this.values.get(name)?.value;
|
|
4407
4518
|
}
|
|
4408
|
-
destroy() {
|
|
4409
|
-
for (const value of this.values.values()) {
|
|
4410
|
-
value.onRemove();
|
|
4411
|
-
}
|
|
4412
|
-
}
|
|
4413
4519
|
}
|
|
4414
4520
|
|
|
4415
4521
|
function createEffect(addValue) {
|
|
4416
4522
|
const stateCache = new WeakMap();
|
|
4417
|
-
const subscriptions = [];
|
|
4418
4523
|
return (subject, values) => {
|
|
4419
4524
|
const state = stateCache.get(subject) ?? new MotionValueState();
|
|
4420
4525
|
stateCache.set(subject, state);
|
|
4526
|
+
const subscriptions = [];
|
|
4421
4527
|
for (const key in values) {
|
|
4422
4528
|
const value = values[key];
|
|
4423
4529
|
const remove = addValue(subject, state, key, value);
|
|
@@ -4509,8 +4615,7 @@
|
|
|
4509
4615
|
if (!valueIsDefault) {
|
|
4510
4616
|
transformIsDefault = false;
|
|
4511
4617
|
const transformName = translateAlias$1[key] || key;
|
|
4512
|
-
|
|
4513
|
-
transform += `${transformName}(${valueToRender}) `;
|
|
4618
|
+
transform += `${transformName}(${value}) `;
|
|
4514
4619
|
}
|
|
4515
4620
|
}
|
|
4516
4621
|
return transformIsDefault ? "none" : transform.trim();
|
|
@@ -5218,16 +5323,23 @@
|
|
|
5218
5323
|
}
|
|
5219
5324
|
};
|
|
5220
5325
|
const startAnimation = () => {
|
|
5221
|
-
stopAnimation();
|
|
5222
5326
|
const currentValue = asNumber$1(value.get());
|
|
5223
5327
|
const targetValue = asNumber$1(latestValue);
|
|
5224
5328
|
// Don't animate if we're already at the target
|
|
5225
5329
|
if (currentValue === targetValue) {
|
|
5330
|
+
stopAnimation();
|
|
5226
5331
|
return;
|
|
5227
5332
|
}
|
|
5333
|
+
// Use the running animation's analytical velocity for accuracy,
|
|
5334
|
+
// falling back to the MotionValue's velocity for the initial animation.
|
|
5335
|
+
// This prevents systematic velocity loss at high frame rates (240hz+).
|
|
5336
|
+
const velocity = activeAnimation
|
|
5337
|
+
? activeAnimation.getGeneratorVelocity()
|
|
5338
|
+
: value.getVelocity();
|
|
5339
|
+
stopAnimation();
|
|
5228
5340
|
activeAnimation = new JSAnimation({
|
|
5229
5341
|
keyframes: [currentValue, targetValue],
|
|
5230
|
-
velocity
|
|
5342
|
+
velocity,
|
|
5231
5343
|
// Default to spring if no type specified (matches useSpring behavior)
|
|
5232
5344
|
type: "spring",
|
|
5233
5345
|
restDelta: 0.001,
|
|
@@ -5236,16 +5348,19 @@
|
|
|
5236
5348
|
onUpdate: latestSetter,
|
|
5237
5349
|
});
|
|
5238
5350
|
};
|
|
5351
|
+
// Use a stable function reference so the frame loop Set deduplicates
|
|
5352
|
+
// multiple calls within the same frame (e.g. rapid mouse events)
|
|
5353
|
+
const scheduleAnimation = () => {
|
|
5354
|
+
startAnimation();
|
|
5355
|
+
value["events"].animationStart?.notify();
|
|
5356
|
+
activeAnimation?.then(() => {
|
|
5357
|
+
value["events"].animationComplete?.notify();
|
|
5358
|
+
});
|
|
5359
|
+
};
|
|
5239
5360
|
value.attach((v, set) => {
|
|
5240
5361
|
latestValue = v;
|
|
5241
5362
|
latestSetter = (latest) => set(parseValue(latest, unit));
|
|
5242
|
-
frame.postRender(
|
|
5243
|
-
startAnimation();
|
|
5244
|
-
value["events"].animationStart?.notify();
|
|
5245
|
-
activeAnimation?.then(() => {
|
|
5246
|
-
value["events"].animationComplete?.notify();
|
|
5247
|
-
});
|
|
5248
|
-
});
|
|
5363
|
+
frame.postRender(scheduleAnimation);
|
|
5249
5364
|
}, stopAnimation);
|
|
5250
5365
|
if (isMotionValue(source)) {
|
|
5251
5366
|
const removeSourceOnChange = source.on("change", (v) => value.set(parseValue(v, unit)));
|
|
@@ -6541,7 +6656,7 @@
|
|
|
6541
6656
|
applyBoxDelta(box, delta);
|
|
6542
6657
|
}
|
|
6543
6658
|
if (isSharedTransition && hasTransform(node.latestValues)) {
|
|
6544
|
-
transformBox(box, node.latestValues);
|
|
6659
|
+
transformBox(box, node.latestValues, node.layout?.layoutBox);
|
|
6545
6660
|
}
|
|
6546
6661
|
}
|
|
6547
6662
|
/**
|
|
@@ -6580,9 +6695,10 @@
|
|
|
6580
6695
|
/**
|
|
6581
6696
|
* Apply a transform to a box from the latest resolved motion values.
|
|
6582
6697
|
*/
|
|
6583
|
-
function transformBox(box, transform) {
|
|
6584
|
-
|
|
6585
|
-
transformAxis(box.
|
|
6698
|
+
function transformBox(box, transform, sourceBox) {
|
|
6699
|
+
const resolveBox = sourceBox ?? box;
|
|
6700
|
+
transformAxis(box.x, resolveAxisTranslate(transform.x, resolveBox.x), transform.scaleX, transform.scale, transform.originX);
|
|
6701
|
+
transformAxis(box.y, resolveAxisTranslate(transform.y, resolveBox.y), transform.scaleY, transform.scale, transform.originY);
|
|
6586
6702
|
}
|
|
6587
6703
|
|
|
6588
6704
|
function measureViewportBox(instance, transformPoint) {
|
|
@@ -8338,6 +8454,14 @@
|
|
|
8338
8454
|
for (let i = 0; i < this.path.length; i++) {
|
|
8339
8455
|
const node = this.path[i];
|
|
8340
8456
|
node.shouldResetTransform = true;
|
|
8457
|
+
/**
|
|
8458
|
+
* Percentage translates resolve against layoutBox dimensions,
|
|
8459
|
+
* so ancestors with them must be re-measured after transform reset.
|
|
8460
|
+
*/
|
|
8461
|
+
if (typeof node.latestValues.x === "string" ||
|
|
8462
|
+
typeof node.latestValues.y === "string") {
|
|
8463
|
+
node.isLayoutDirty = true;
|
|
8464
|
+
}
|
|
8341
8465
|
node.updateScroll("snapshot");
|
|
8342
8466
|
if (node.options.layoutRoot) {
|
|
8343
8467
|
node.willUpdate(false);
|
|
@@ -8603,10 +8727,10 @@
|
|
|
8603
8727
|
}
|
|
8604
8728
|
if (!hasTransform(node.latestValues))
|
|
8605
8729
|
continue;
|
|
8606
|
-
transformBox(withTransforms, node.latestValues);
|
|
8730
|
+
transformBox(withTransforms, node.latestValues, node.layout?.layoutBox);
|
|
8607
8731
|
}
|
|
8608
8732
|
if (hasTransform(this.latestValues)) {
|
|
8609
|
-
transformBox(withTransforms, this.latestValues);
|
|
8733
|
+
transformBox(withTransforms, this.latestValues, this.layout?.layoutBox);
|
|
8610
8734
|
}
|
|
8611
8735
|
return withTransforms;
|
|
8612
8736
|
}
|
|
@@ -8615,15 +8739,15 @@
|
|
|
8615
8739
|
copyBoxInto(boxWithoutTransform, box);
|
|
8616
8740
|
for (let i = 0; i < this.path.length; i++) {
|
|
8617
8741
|
const node = this.path[i];
|
|
8618
|
-
if (!node.instance)
|
|
8619
|
-
continue;
|
|
8620
8742
|
if (!hasTransform(node.latestValues))
|
|
8621
8743
|
continue;
|
|
8622
|
-
|
|
8623
|
-
|
|
8624
|
-
|
|
8625
|
-
|
|
8626
|
-
|
|
8744
|
+
let sourceBox;
|
|
8745
|
+
if (node.instance) {
|
|
8746
|
+
hasScale(node.latestValues) && node.updateSnapshot();
|
|
8747
|
+
sourceBox = createBox();
|
|
8748
|
+
copyBoxInto(sourceBox, node.measurePageBox());
|
|
8749
|
+
}
|
|
8750
|
+
removeBoxTransforms(boxWithoutTransform, node.latestValues, node.snapshot?.layoutBox, sourceBox);
|
|
8627
8751
|
}
|
|
8628
8752
|
if (hasTransform(this.latestValues)) {
|
|
8629
8753
|
removeBoxTransforms(boxWithoutTransform, this.latestValues);
|
|
@@ -9944,7 +10068,7 @@
|
|
|
9944
10068
|
class PopChildMeasure extends React__namespace.Component {
|
|
9945
10069
|
getSnapshotBeforeUpdate(prevProps) {
|
|
9946
10070
|
const element = this.props.childRef.current;
|
|
9947
|
-
if (element && prevProps.isPresent && !this.props.isPresent && this.props.pop !== false) {
|
|
10071
|
+
if (isHTMLElement(element) && prevProps.isPresent && !this.props.isPresent && this.props.pop !== false) {
|
|
9948
10072
|
const parent = element.offsetParent;
|
|
9949
10073
|
const parentWidth = isHTMLElement(parent)
|
|
9950
10074
|
? parent.offsetWidth || 0
|
|
@@ -9952,9 +10076,10 @@
|
|
|
9952
10076
|
const parentHeight = isHTMLElement(parent)
|
|
9953
10077
|
? parent.offsetHeight || 0
|
|
9954
10078
|
: 0;
|
|
10079
|
+
const computedStyle = getComputedStyle(element);
|
|
9955
10080
|
const size = this.props.sizeRef.current;
|
|
9956
|
-
size.height =
|
|
9957
|
-
size.width =
|
|
10081
|
+
size.height = parseFloat(computedStyle.height);
|
|
10082
|
+
size.width = parseFloat(computedStyle.width);
|
|
9958
10083
|
size.top = element.offsetTop;
|
|
9959
10084
|
size.left = element.offsetLeft;
|
|
9960
10085
|
size.right = parentWidth - size.width - size.left;
|
|
@@ -11356,9 +11481,7 @@
|
|
|
11356
11481
|
},
|
|
11357
11482
|
};
|
|
11358
11483
|
}
|
|
11359
|
-
const addPointerInfo = (handler) =>
|
|
11360
|
-
return (event) => isPrimaryPointer(event) && handler(event, extractEventInfo(event));
|
|
11361
|
-
};
|
|
11484
|
+
const addPointerInfo = (handler) => (event) => isPrimaryPointer(event) && handler(event, extractEventInfo(event));
|
|
11362
11485
|
|
|
11363
11486
|
function addPointerEvent(target, eventName, handler, options) {
|
|
11364
11487
|
return addDomEvent(target, eventName, addPointerInfo(handler), options);
|
|
@@ -11395,6 +11518,12 @@
|
|
|
11395
11518
|
* @internal
|
|
11396
11519
|
*/
|
|
11397
11520
|
this.lastMoveEventInfo = null;
|
|
11521
|
+
/**
|
|
11522
|
+
* Raw (untransformed) event info, re-transformed each frame
|
|
11523
|
+
* so transformPagePoint sees the current parent matrix.
|
|
11524
|
+
* @internal
|
|
11525
|
+
*/
|
|
11526
|
+
this.lastRawMoveEventInfo = null;
|
|
11398
11527
|
/**
|
|
11399
11528
|
* @internal
|
|
11400
11529
|
*/
|
|
@@ -11422,6 +11551,11 @@
|
|
|
11422
11551
|
this.updatePoint = () => {
|
|
11423
11552
|
if (!(this.lastMoveEvent && this.lastMoveEventInfo))
|
|
11424
11553
|
return;
|
|
11554
|
+
// Re-transform raw point through current transformPagePoint so
|
|
11555
|
+
// animated parent transforms (e.g. rotation) are picked up each frame
|
|
11556
|
+
if (this.lastRawMoveEventInfo) {
|
|
11557
|
+
this.lastMoveEventInfo = transformPoint(this.lastRawMoveEventInfo, this.transformPagePoint);
|
|
11558
|
+
}
|
|
11425
11559
|
const info = getPanInfo(this.lastMoveEventInfo, this.history);
|
|
11426
11560
|
const isPanStarted = this.startEvent !== null;
|
|
11427
11561
|
// Only start panning if the offset is larger than 3 pixels. If we make it
|
|
@@ -11442,6 +11576,7 @@
|
|
|
11442
11576
|
};
|
|
11443
11577
|
this.handlePointerMove = (event, info) => {
|
|
11444
11578
|
this.lastMoveEvent = event;
|
|
11579
|
+
this.lastRawMoveEventInfo = info;
|
|
11445
11580
|
this.lastMoveEventInfo = transformPoint(info, this.transformPagePoint);
|
|
11446
11581
|
// Throttle mouse move event to once per frame
|
|
11447
11582
|
frame.update(this.updatePoint, true);
|
|
@@ -13814,7 +13949,7 @@
|
|
|
13814
13949
|
const { length, position } = keys[axisName];
|
|
13815
13950
|
const prev = axis.current;
|
|
13816
13951
|
const prevTime = info.time;
|
|
13817
|
-
axis.current = element[`scroll${position}`];
|
|
13952
|
+
axis.current = Math.abs(element[`scroll${position}`]);
|
|
13818
13953
|
axis.scrollLength = element[`scroll${length}`] - element[`client${length}`];
|
|
13819
13954
|
axis.offset.length = 0;
|
|
13820
13955
|
axis.offset[0] = 0;
|
|
@@ -15191,6 +15326,76 @@
|
|
|
15191
15326
|
};
|
|
15192
15327
|
}
|
|
15193
15328
|
|
|
15329
|
+
/**
|
|
15330
|
+
* Creates a `transformPagePoint` function that corrects pointer coordinates
|
|
15331
|
+
* for a parent container with CSS transforms (rotation, scale, skew).
|
|
15332
|
+
*
|
|
15333
|
+
* When dragging elements inside a transformed parent, pointer coordinates
|
|
15334
|
+
* need to be transformed through the inverse of the parent's transform
|
|
15335
|
+
* so the drag offset is in local space.
|
|
15336
|
+
*
|
|
15337
|
+
* Works with both static and continuously animating transforms.
|
|
15338
|
+
*
|
|
15339
|
+
* @example
|
|
15340
|
+
* ```jsx
|
|
15341
|
+
* function App() {
|
|
15342
|
+
* const ref = useRef(null)
|
|
15343
|
+
*
|
|
15344
|
+
* return (
|
|
15345
|
+
* <motion.div ref={ref} style={{ rotate: 90 }}>
|
|
15346
|
+
* <MotionConfig transformPagePoint={correctParentTransform(ref)}>
|
|
15347
|
+
* <motion.div drag />
|
|
15348
|
+
* </MotionConfig>
|
|
15349
|
+
* </motion.div>
|
|
15350
|
+
* )
|
|
15351
|
+
* }
|
|
15352
|
+
* ```
|
|
15353
|
+
*
|
|
15354
|
+
* @param parentRef - A React ref to the transformed parent element
|
|
15355
|
+
* @returns A transformPagePoint function for use with MotionConfig
|
|
15356
|
+
*
|
|
15357
|
+
* @public
|
|
15358
|
+
*/
|
|
15359
|
+
function correctParentTransform(parentRef) {
|
|
15360
|
+
return (point) => {
|
|
15361
|
+
const parent = parentRef.current;
|
|
15362
|
+
if (!parent)
|
|
15363
|
+
return point;
|
|
15364
|
+
const inv = getInverseMatrix(parent);
|
|
15365
|
+
if (!inv)
|
|
15366
|
+
return point;
|
|
15367
|
+
// Get center of rotation in page space
|
|
15368
|
+
const rect = parent.getBoundingClientRect();
|
|
15369
|
+
const cx = rect.left + window.scrollX + rect.width / 2;
|
|
15370
|
+
const cy = rect.top + window.scrollY + rect.height / 2;
|
|
15371
|
+
// Transform (point - center) through inverse, then add center back
|
|
15372
|
+
const dx = point.x - cx;
|
|
15373
|
+
const dy = point.y - cy;
|
|
15374
|
+
return {
|
|
15375
|
+
x: cx + inv.a * dx + inv.c * dy,
|
|
15376
|
+
y: cy + inv.b * dx + inv.d * dy,
|
|
15377
|
+
};
|
|
15378
|
+
};
|
|
15379
|
+
}
|
|
15380
|
+
function getInverseMatrix(element) {
|
|
15381
|
+
const { transform } = getComputedStyle(element);
|
|
15382
|
+
if (!transform || transform === "none")
|
|
15383
|
+
return null;
|
|
15384
|
+
const match = transform.match(/^matrix3d\((.*)\)$/u) ||
|
|
15385
|
+
transform.match(/^matrix\((.*)\)$/u);
|
|
15386
|
+
if (!match)
|
|
15387
|
+
return null;
|
|
15388
|
+
const v = match[1].split(",").map(Number);
|
|
15389
|
+
const is3d = transform.startsWith("matrix3d");
|
|
15390
|
+
const a = v[0], b = v[1];
|
|
15391
|
+
const c = is3d ? v[4] : v[2];
|
|
15392
|
+
const d = is3d ? v[5] : v[3];
|
|
15393
|
+
const det = a * d - b * c;
|
|
15394
|
+
if (Math.abs(det) < 1e-10)
|
|
15395
|
+
return null;
|
|
15396
|
+
return { a: d / det, b: -b / det, c: -c / det, d: a / det };
|
|
15397
|
+
}
|
|
15398
|
+
|
|
15194
15399
|
const appearAnimationStore = new Map();
|
|
15195
15400
|
const appearComplete = new Map();
|
|
15196
15401
|
|
|
@@ -15625,6 +15830,7 @@
|
|
|
15625
15830
|
exports.copyBoxInto = copyBoxInto;
|
|
15626
15831
|
exports.correctBorderRadius = correctBorderRadius;
|
|
15627
15832
|
exports.correctBoxShadow = correctBoxShadow;
|
|
15833
|
+
exports.correctParentTransform = correctParentTransform;
|
|
15628
15834
|
exports.createAnimationState = createAnimationState;
|
|
15629
15835
|
exports.createAxis = createAxis;
|
|
15630
15836
|
exports.createAxisDelta = createAxisDelta;
|