motion 12.29.1-alpha.0 → 12.29.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/motion.dev.js +232 -340
- package/dist/motion.js +1 -1
- package/package.json +3 -3
package/dist/motion.dev.js
CHANGED
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
/*#__NO_SIDE_EFFECTS__*/
|
|
84
|
-
const noop = (any) => any;
|
|
84
|
+
const noop$1 = (any) => any;
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
87
|
* Pipe
|
|
@@ -227,7 +227,7 @@
|
|
|
227
227
|
function cubicBezier(mX1, mY1, mX2, mY2) {
|
|
228
228
|
// If this is a linear gradient, return linear easing
|
|
229
229
|
if (mX1 === mY1 && mX2 === mY2)
|
|
230
|
-
return noop;
|
|
230
|
+
return noop$1;
|
|
231
231
|
const getTForX = (aX) => binarySubdivide(aX, 0, 1, mX1, mX2);
|
|
232
232
|
// If animation is at start/end, return t without easing
|
|
233
233
|
return (t) => t === 0 || t === 1 ? t : calcBezier(getTForX(t), mY1, mY2);
|
|
@@ -278,7 +278,7 @@
|
|
|
278
278
|
const isBezierDefinition = (easing) => Array.isArray(easing) && typeof easing[0] === "number";
|
|
279
279
|
|
|
280
280
|
const easingLookup = {
|
|
281
|
-
linear: noop,
|
|
281
|
+
linear: noop$1,
|
|
282
282
|
easeIn,
|
|
283
283
|
easeInOut,
|
|
284
284
|
easeOut,
|
|
@@ -479,7 +479,7 @@
|
|
|
479
479
|
return { schedule, cancel, state, steps };
|
|
480
480
|
}
|
|
481
481
|
|
|
482
|
-
const { schedule: frame, cancel: cancelFrame, state: frameData, steps: frameSteps, } = /* @__PURE__ */ createRenderBatcher(typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : noop, true);
|
|
482
|
+
const { schedule: frame, cancel: cancelFrame, state: frameData, steps: frameSteps, } = /* @__PURE__ */ createRenderBatcher(typeof requestAnimationFrame !== "undefined" ? requestAnimationFrame : noop$1, true);
|
|
483
483
|
|
|
484
484
|
let now;
|
|
485
485
|
function clearTime() {
|
|
@@ -1429,7 +1429,7 @@
|
|
|
1429
1429
|
for (let i = 0; i < numMixers; i++) {
|
|
1430
1430
|
let mixer = mixerFactory(output[i], output[i + 1]);
|
|
1431
1431
|
if (ease) {
|
|
1432
|
-
const easingFunction = Array.isArray(ease) ? ease[i] || noop : ease;
|
|
1432
|
+
const easingFunction = Array.isArray(ease) ? ease[i] || noop$1 : ease;
|
|
1433
1433
|
mixer = pipe(easingFunction, mixer);
|
|
1434
1434
|
}
|
|
1435
1435
|
mixers.push(mixer);
|
|
@@ -1921,30 +1921,6 @@
|
|
|
1921
1921
|
this.startTime = 0;
|
|
1922
1922
|
return this.tick(sampleTime, true);
|
|
1923
1923
|
}
|
|
1924
|
-
/**
|
|
1925
|
-
* Returns whether this animation can provide accurate velocity sampling.
|
|
1926
|
-
* This is false for animations using mixKeyframes (non-numeric values)
|
|
1927
|
-
* or animations that aren't currently running.
|
|
1928
|
-
*/
|
|
1929
|
-
get canSampleVelocity() {
|
|
1930
|
-
return (this.state === "running" &&
|
|
1931
|
-
!this.isStopped &&
|
|
1932
|
-
!this.mixKeyframes &&
|
|
1933
|
-
!!this.generator);
|
|
1934
|
-
}
|
|
1935
|
-
/**
|
|
1936
|
-
* Sample the animation's value at a specific time in milliseconds.
|
|
1937
|
-
* Used for velocity calculations via finite differencing.
|
|
1938
|
-
*/
|
|
1939
|
-
sampleAt(t) {
|
|
1940
|
-
return this.generator.next(t).value;
|
|
1941
|
-
}
|
|
1942
|
-
/**
|
|
1943
|
-
* The current elapsed time of the animation in milliseconds.
|
|
1944
|
-
*/
|
|
1945
|
-
get elapsed() {
|
|
1946
|
-
return this.currentTime;
|
|
1947
|
-
}
|
|
1948
1924
|
attachTimeline(timeline) {
|
|
1949
1925
|
if (this.options.allowFlatten) {
|
|
1950
1926
|
this.options.type = "keyframes";
|
|
@@ -2515,7 +2491,7 @@
|
|
|
2515
2491
|
this.animation.onfinish = null;
|
|
2516
2492
|
if (timeline && supportsScrollTimeline()) {
|
|
2517
2493
|
this.animation.timeline = timeline;
|
|
2518
|
-
return noop;
|
|
2494
|
+
return noop$1;
|
|
2519
2495
|
}
|
|
2520
2496
|
else {
|
|
2521
2497
|
return observe(this);
|
|
@@ -2807,7 +2783,7 @@
|
|
|
2807
2783
|
: new JSAnimation(resolvedOptions);
|
|
2808
2784
|
animation.finished.then(() => {
|
|
2809
2785
|
this.notifyFinished();
|
|
2810
|
-
}).catch(noop);
|
|
2786
|
+
}).catch(noop$1);
|
|
2811
2787
|
if (this.pendingTimeline) {
|
|
2812
2788
|
this.stopTimeline = animation.attachTimeline(this.pendingTimeline);
|
|
2813
2789
|
this.pendingTimeline = undefined;
|
|
@@ -5063,25 +5039,16 @@
|
|
|
5063
5039
|
}
|
|
5064
5040
|
};
|
|
5065
5041
|
const startAnimation = () => {
|
|
5042
|
+
stopAnimation();
|
|
5066
5043
|
const currentValue = asNumber$1(value.get());
|
|
5067
5044
|
const targetValue = asNumber$1(latestValue);
|
|
5068
5045
|
// Don't animate if we're already at the target
|
|
5069
5046
|
if (currentValue === targetValue) {
|
|
5070
|
-
stopAnimation();
|
|
5071
5047
|
return;
|
|
5072
5048
|
}
|
|
5073
|
-
// Get velocity from the running animation before stopping it.
|
|
5074
|
-
// This provides the instantaneous velocity from the spring physics,
|
|
5075
|
-
// which is more accurate than the motion value's finite difference.
|
|
5076
|
-
let velocity = value.getVelocity();
|
|
5077
|
-
if (activeAnimation?.canSampleVelocity) {
|
|
5078
|
-
const elapsed = activeAnimation.elapsed;
|
|
5079
|
-
velocity = calcGeneratorVelocity((t) => activeAnimation.sampleAt(t), elapsed, activeAnimation.sampleAt(elapsed));
|
|
5080
|
-
}
|
|
5081
|
-
stopAnimation();
|
|
5082
5049
|
activeAnimation = new JSAnimation({
|
|
5083
5050
|
keyframes: [currentValue, targetValue],
|
|
5084
|
-
velocity,
|
|
5051
|
+
velocity: value.getVelocity(),
|
|
5085
5052
|
// Default to spring if no type specified (matches useSpring behavior)
|
|
5086
5053
|
type: "spring",
|
|
5087
5054
|
restDelta: 0.001,
|
|
@@ -5497,7 +5464,7 @@
|
|
|
5497
5464
|
constructor(update, options = {}) {
|
|
5498
5465
|
this.currentSubject = "root";
|
|
5499
5466
|
this.targets = new Map();
|
|
5500
|
-
this.notifyReady = noop;
|
|
5467
|
+
this.notifyReady = noop$1;
|
|
5501
5468
|
this.readyPromise = new Promise((resolve) => {
|
|
5502
5469
|
this.notifyReady = resolve;
|
|
5503
5470
|
});
|
|
@@ -7554,7 +7521,7 @@
|
|
|
7554
7521
|
: values.borderRadius;
|
|
7555
7522
|
}
|
|
7556
7523
|
const easeCrossfadeIn = /*@__PURE__*/ compress(0, 0.5, circOut);
|
|
7557
|
-
const easeCrossfadeOut = /*@__PURE__*/ compress(0.5, 0.95, noop);
|
|
7524
|
+
const easeCrossfadeOut = /*@__PURE__*/ compress(0.5, 0.95, noop$1);
|
|
7558
7525
|
function compress(min, max, easing) {
|
|
7559
7526
|
return (p) => {
|
|
7560
7527
|
// Could replace ifs with clamp
|
|
@@ -7793,7 +7760,7 @@
|
|
|
7793
7760
|
cancelTreeOptimisedTransformAnimations(parent);
|
|
7794
7761
|
}
|
|
7795
7762
|
}
|
|
7796
|
-
function createProjectionNode
|
|
7763
|
+
function createProjectionNode({ attachResizeListener, defaultParent, measureScroll, checkIsScrollRoot, resetTransform, }) {
|
|
7797
7764
|
return class ProjectionNode {
|
|
7798
7765
|
constructor(latestValues = {}, parent = defaultParent?.()) {
|
|
7799
7766
|
/**
|
|
@@ -9331,7 +9298,7 @@
|
|
|
9331
9298
|
*/
|
|
9332
9299
|
const roundPoint = userAgentContains("applewebkit/") && !userAgentContains("chrome/")
|
|
9333
9300
|
? Math.round
|
|
9334
|
-
: noop;
|
|
9301
|
+
: noop$1;
|
|
9335
9302
|
function roundAxis(axis) {
|
|
9336
9303
|
// Round to the nearest .5 pixels to support subpixel layouts
|
|
9337
9304
|
axis.min = roundPoint(axis.min);
|
|
@@ -9350,7 +9317,7 @@
|
|
|
9350
9317
|
return node !== node.root && node.scroll?.wasRoot;
|
|
9351
9318
|
}
|
|
9352
9319
|
|
|
9353
|
-
const DocumentProjectionNode = createProjectionNode
|
|
9320
|
+
const DocumentProjectionNode = createProjectionNode({
|
|
9354
9321
|
attachResizeListener: (ref, notify) => addDomEvent(ref, "resize", notify),
|
|
9355
9322
|
measureScroll: () => ({
|
|
9356
9323
|
x: document.documentElement.scrollLeft || document.body?.scrollLeft || 0,
|
|
@@ -9385,7 +9352,7 @@
|
|
|
9385
9352
|
const rootProjectionNode = {
|
|
9386
9353
|
current: undefined,
|
|
9387
9354
|
};
|
|
9388
|
-
const HTMLProjectionNode = createProjectionNode
|
|
9355
|
+
const HTMLProjectionNode = createProjectionNode({
|
|
9389
9356
|
measureScroll: (instance) => ({
|
|
9390
9357
|
x: instance.scrollLeft,
|
|
9391
9358
|
y: instance.scrollTop,
|
|
@@ -9405,315 +9372,153 @@
|
|
|
9405
9372
|
checkIsScrollRoot: (instance) => Boolean(window.getComputedStyle(instance).position === "fixed"),
|
|
9406
9373
|
});
|
|
9407
9374
|
|
|
9408
|
-
const
|
|
9409
|
-
|
|
9410
|
-
|
|
9411
|
-
|
|
9412
|
-
if (
|
|
9413
|
-
|
|
9414
|
-
|
|
9415
|
-
|
|
9416
|
-
|
|
9417
|
-
|
|
9418
|
-
return element.getAttribute("data-layout-id");
|
|
9419
|
-
}
|
|
9420
|
-
function hasLayout(element) {
|
|
9421
|
-
return (element.hasAttribute("data-layout") ||
|
|
9422
|
-
element.hasAttribute("data-layout-id"));
|
|
9423
|
-
}
|
|
9424
|
-
|
|
9425
|
-
let scaleCorrectorAdded = false;
|
|
9426
|
-
/**
|
|
9427
|
-
* Track active projection nodes per element to handle animation interruption.
|
|
9428
|
-
* When a new animation starts on an element that already has an active animation,
|
|
9429
|
-
* we need to stop the old animation so the new one can start from the current
|
|
9430
|
-
* visual position.
|
|
9431
|
-
*/
|
|
9432
|
-
const activeProjectionNodes = new WeakMap();
|
|
9433
|
-
function ensureScaleCorrectors() {
|
|
9434
|
-
if (scaleCorrectorAdded)
|
|
9435
|
-
return;
|
|
9436
|
-
scaleCorrectorAdded = true;
|
|
9437
|
-
addScaleCorrector({
|
|
9438
|
-
borderRadius: {
|
|
9439
|
-
...correctBorderRadius,
|
|
9440
|
-
applyTo: [
|
|
9441
|
-
"borderTopLeftRadius",
|
|
9442
|
-
"borderTopRightRadius",
|
|
9443
|
-
"borderBottomLeftRadius",
|
|
9444
|
-
"borderBottomRightRadius",
|
|
9445
|
-
],
|
|
9446
|
-
},
|
|
9447
|
-
borderTopLeftRadius: correctBorderRadius,
|
|
9448
|
-
borderTopRightRadius: correctBorderRadius,
|
|
9449
|
-
borderBottomLeftRadius: correctBorderRadius,
|
|
9450
|
-
borderBottomRightRadius: correctBorderRadius,
|
|
9451
|
-
boxShadow: correctBoxShadow,
|
|
9452
|
-
});
|
|
9453
|
-
}
|
|
9454
|
-
/**
|
|
9455
|
-
* Get DOM depth of an element
|
|
9456
|
-
*/
|
|
9457
|
-
function getDepth(element) {
|
|
9458
|
-
let depth = 0;
|
|
9459
|
-
let current = element.parentElement;
|
|
9460
|
-
while (current) {
|
|
9461
|
-
depth++;
|
|
9462
|
-
current = current.parentElement;
|
|
9463
|
-
}
|
|
9464
|
-
return depth;
|
|
9465
|
-
}
|
|
9466
|
-
/**
|
|
9467
|
-
* Find the closest projection parent for an element
|
|
9468
|
-
*/
|
|
9469
|
-
function findProjectionParent(element, nodeCache) {
|
|
9470
|
-
let parent = element.parentElement;
|
|
9471
|
-
while (parent) {
|
|
9472
|
-
const node = nodeCache.get(parent);
|
|
9473
|
-
if (node)
|
|
9474
|
-
return node;
|
|
9475
|
-
parent = parent.parentElement;
|
|
9476
|
-
}
|
|
9477
|
-
return undefined;
|
|
9478
|
-
}
|
|
9479
|
-
/**
|
|
9480
|
-
* Create or reuse a projection node for an element
|
|
9481
|
-
*/
|
|
9482
|
-
function createProjectionNode(element, parent, options, transition) {
|
|
9483
|
-
// Check for existing active node - reuse it to preserve animation state
|
|
9484
|
-
const existingNode = activeProjectionNodes.get(element);
|
|
9485
|
-
if (existingNode) {
|
|
9486
|
-
const visualElement = existingNode.options.visualElement;
|
|
9487
|
-
// Update transition options for the new animation
|
|
9488
|
-
const nodeTransition = transition
|
|
9489
|
-
? { duration: transition.duration, ease: transition.ease }
|
|
9490
|
-
: { duration: 0.3, ease: "easeOut" };
|
|
9491
|
-
existingNode.setOptions({
|
|
9492
|
-
...existingNode.options,
|
|
9493
|
-
animate: true,
|
|
9494
|
-
transition: nodeTransition,
|
|
9495
|
-
...options,
|
|
9496
|
-
});
|
|
9497
|
-
// Re-mount the node if it was previously unmounted
|
|
9498
|
-
// This re-adds it to root.nodes so didUpdate() will process it
|
|
9499
|
-
if (!existingNode.instance) {
|
|
9500
|
-
existingNode.mount(element);
|
|
9501
|
-
}
|
|
9502
|
-
return { node: existingNode, visualElement };
|
|
9503
|
-
}
|
|
9504
|
-
// No existing node - create a new one
|
|
9505
|
-
const latestValues = {};
|
|
9506
|
-
const visualElement = new HTMLVisualElement({
|
|
9507
|
-
visualState: {
|
|
9508
|
-
latestValues,
|
|
9509
|
-
renderState: {
|
|
9510
|
-
transformOrigin: {},
|
|
9511
|
-
transform: {},
|
|
9512
|
-
style: {},
|
|
9513
|
-
vars: {},
|
|
9514
|
-
},
|
|
9515
|
-
},
|
|
9516
|
-
presenceContext: null,
|
|
9517
|
-
props: {},
|
|
9518
|
-
});
|
|
9519
|
-
const node = new HTMLProjectionNode(latestValues, parent);
|
|
9520
|
-
// Convert AnimationOptions to transition format for the projection system
|
|
9521
|
-
const nodeTransition = transition
|
|
9522
|
-
? { duration: transition.duration, ease: transition.ease }
|
|
9523
|
-
: { duration: 0.3, ease: "easeOut" };
|
|
9524
|
-
node.setOptions({
|
|
9525
|
-
visualElement,
|
|
9526
|
-
layout: true,
|
|
9527
|
-
animate: true,
|
|
9528
|
-
transition: nodeTransition,
|
|
9529
|
-
...options,
|
|
9530
|
-
});
|
|
9531
|
-
node.mount(element);
|
|
9532
|
-
visualElement.projection = node;
|
|
9533
|
-
// Track this node as the active one for this element
|
|
9534
|
-
activeProjectionNodes.set(element, node);
|
|
9535
|
-
return { node, visualElement };
|
|
9536
|
-
}
|
|
9537
|
-
/**
|
|
9538
|
-
* Build a projection tree from a list of elements
|
|
9539
|
-
*/
|
|
9540
|
-
function buildProjectionTree(elements, existingContext, options) {
|
|
9541
|
-
ensureScaleCorrectors();
|
|
9542
|
-
const nodes = existingContext?.nodes ?? new Map();
|
|
9543
|
-
const visualElements = existingContext?.visualElements ?? new Map();
|
|
9544
|
-
const group = existingContext?.group ?? nodeGroup();
|
|
9545
|
-
const defaultTransition = options?.defaultTransition;
|
|
9546
|
-
const sharedTransitions = options?.sharedTransitions;
|
|
9547
|
-
// Sort elements by DOM depth (parents before children)
|
|
9548
|
-
const sorted = [...elements].sort((a, b) => getDepth(a) - getDepth(b));
|
|
9549
|
-
let root = existingContext?.root;
|
|
9550
|
-
for (const element of sorted) {
|
|
9551
|
-
// Skip if already has a node
|
|
9552
|
-
if (nodes.has(element))
|
|
9553
|
-
continue;
|
|
9554
|
-
const parent = findProjectionParent(element, nodes);
|
|
9555
|
-
const layoutId = getLayoutId(element);
|
|
9556
|
-
const layoutMode = element.getAttribute("data-layout");
|
|
9557
|
-
const nodeOptions = {
|
|
9558
|
-
layoutId: layoutId ?? undefined,
|
|
9559
|
-
animationType: parseLayoutMode(layoutMode),
|
|
9560
|
-
};
|
|
9561
|
-
// Use layoutId-specific transition if available, otherwise use default
|
|
9562
|
-
const transition = layoutId && sharedTransitions?.get(layoutId)
|
|
9563
|
-
? sharedTransitions.get(layoutId)
|
|
9564
|
-
: defaultTransition;
|
|
9565
|
-
const { node, visualElement } = createProjectionNode(element, parent, nodeOptions, transition);
|
|
9566
|
-
nodes.set(element, node);
|
|
9567
|
-
visualElements.set(element, visualElement);
|
|
9568
|
-
group.add(node);
|
|
9569
|
-
if (!root) {
|
|
9570
|
-
root = node.root;
|
|
9571
|
-
}
|
|
9572
|
-
}
|
|
9375
|
+
const layoutSelector = "[data-layout], [data-layout-id]";
|
|
9376
|
+
const noop = () => { };
|
|
9377
|
+
function snapshotFromTarget(projection) {
|
|
9378
|
+
const target = projection.targetWithTransforms || projection.target;
|
|
9379
|
+
if (!target)
|
|
9380
|
+
return undefined;
|
|
9381
|
+
const measuredBox = createBox();
|
|
9382
|
+
const layoutBox = createBox();
|
|
9383
|
+
copyBoxInto(measuredBox, target);
|
|
9384
|
+
copyBoxInto(layoutBox, target);
|
|
9573
9385
|
return {
|
|
9574
|
-
|
|
9575
|
-
|
|
9576
|
-
|
|
9577
|
-
|
|
9386
|
+
animationId: projection.root?.animationId ?? 0,
|
|
9387
|
+
measuredBox,
|
|
9388
|
+
layoutBox,
|
|
9389
|
+
latestValues: projection.animationValues || projection.latestValues || {},
|
|
9390
|
+
source: projection.id,
|
|
9578
9391
|
};
|
|
9579
9392
|
}
|
|
9580
|
-
/**
|
|
9581
|
-
* Parse the data-layout attribute value
|
|
9582
|
-
*/
|
|
9583
|
-
function parseLayoutMode(value) {
|
|
9584
|
-
if (value === "position")
|
|
9585
|
-
return "position";
|
|
9586
|
-
if (value === "size")
|
|
9587
|
-
return "size";
|
|
9588
|
-
if (value === "preserve-aspect")
|
|
9589
|
-
return "preserve-aspect";
|
|
9590
|
-
return "both";
|
|
9591
|
-
}
|
|
9592
|
-
/**
|
|
9593
|
-
* Clean up projection nodes for specific elements.
|
|
9594
|
-
* If elementsToCleanup is provided, only those elements are cleaned up.
|
|
9595
|
-
* If not provided, all nodes are cleaned up.
|
|
9596
|
-
*
|
|
9597
|
-
* This allows persisting elements to keep their nodes between animations,
|
|
9598
|
-
* matching React's behavior where nodes persist for elements that remain in the DOM.
|
|
9599
|
-
*/
|
|
9600
|
-
function cleanupProjectionTree(context, elementsToCleanup) {
|
|
9601
|
-
const elementsToProcess = elementsToCleanup
|
|
9602
|
-
? [...context.nodes.entries()].filter(([el]) => elementsToCleanup.has(el))
|
|
9603
|
-
: [...context.nodes.entries()];
|
|
9604
|
-
for (const [element, node] of elementsToProcess) {
|
|
9605
|
-
context.group.remove(node);
|
|
9606
|
-
node.unmount();
|
|
9607
|
-
// Only clear from activeProjectionNodes if this is still the active node.
|
|
9608
|
-
// A newer animation might have already taken over.
|
|
9609
|
-
if (activeProjectionNodes.get(element) === node) {
|
|
9610
|
-
activeProjectionNodes.delete(element);
|
|
9611
|
-
}
|
|
9612
|
-
context.nodes.delete(element);
|
|
9613
|
-
context.visualElements.delete(element);
|
|
9614
|
-
}
|
|
9615
|
-
}
|
|
9616
|
-
|
|
9617
9393
|
class LayoutAnimationBuilder {
|
|
9618
9394
|
constructor(scope, updateDom, defaultOptions) {
|
|
9619
9395
|
this.sharedTransitions = new Map();
|
|
9620
9396
|
this.notifyReady = noop;
|
|
9621
|
-
this.
|
|
9397
|
+
this.rejectReady = noop;
|
|
9622
9398
|
this.scope = scope;
|
|
9623
9399
|
this.updateDom = updateDom;
|
|
9624
9400
|
this.defaultOptions = defaultOptions;
|
|
9625
|
-
this.readyPromise = new Promise((resolve) => {
|
|
9401
|
+
this.readyPromise = new Promise((resolve, reject) => {
|
|
9626
9402
|
this.notifyReady = resolve;
|
|
9403
|
+
this.rejectReady = reject;
|
|
9404
|
+
});
|
|
9405
|
+
frame.postRender(() => {
|
|
9406
|
+
this.start().then(this.notifyReady).catch(this.rejectReady);
|
|
9627
9407
|
});
|
|
9628
|
-
// Queue execution on microtask to allow builder methods to be called
|
|
9629
|
-
queueMicrotask(() => this.execute());
|
|
9630
9408
|
}
|
|
9631
|
-
shared(id,
|
|
9632
|
-
this.sharedTransitions.set(id,
|
|
9409
|
+
shared(id, transition) {
|
|
9410
|
+
this.sharedTransitions.set(id, transition);
|
|
9633
9411
|
return this;
|
|
9634
9412
|
}
|
|
9635
|
-
then(
|
|
9636
|
-
return this.readyPromise.then(
|
|
9413
|
+
then(resolve, reject) {
|
|
9414
|
+
return this.readyPromise.then(resolve, reject);
|
|
9637
9415
|
}
|
|
9638
|
-
async
|
|
9639
|
-
|
|
9640
|
-
|
|
9641
|
-
|
|
9642
|
-
|
|
9643
|
-
|
|
9644
|
-
|
|
9645
|
-
|
|
9646
|
-
|
|
9647
|
-
|
|
9648
|
-
for (const node of context.nodes.values()) {
|
|
9649
|
-
node.isLayoutDirty = false;
|
|
9650
|
-
node.willUpdate();
|
|
9651
|
-
}
|
|
9652
|
-
}
|
|
9653
|
-
// Phase 2: Execute DOM update
|
|
9654
|
-
this.updateDom();
|
|
9655
|
-
// Phase 3: Post-mutation - Compare before/after elements
|
|
9656
|
-
const afterElements = getLayoutElements(this.scope);
|
|
9657
|
-
const beforeSet = new Set(beforeElements);
|
|
9658
|
-
const afterSet = new Set(afterElements);
|
|
9659
|
-
const entering = afterElements.filter((el) => !beforeSet.has(el));
|
|
9660
|
-
const exiting = beforeElements.filter((el) => !afterSet.has(el));
|
|
9661
|
-
// Build projection nodes for entering elements
|
|
9662
|
-
if (entering.length > 0) {
|
|
9663
|
-
context = buildProjectionTree(entering, context, this.getBuildOptions());
|
|
9664
|
-
}
|
|
9665
|
-
// No layout elements - return empty animation
|
|
9666
|
-
if (!context) {
|
|
9667
|
-
this.notifyReady(new GroupAnimation([]));
|
|
9668
|
-
return;
|
|
9669
|
-
}
|
|
9670
|
-
// Handle shared elements
|
|
9671
|
-
for (const element of exiting) {
|
|
9672
|
-
const node = context.nodes.get(element);
|
|
9673
|
-
node?.getStack()?.remove(node);
|
|
9674
|
-
}
|
|
9675
|
-
for (const element of entering) {
|
|
9676
|
-
context.nodes.get(element)?.promote();
|
|
9677
|
-
}
|
|
9678
|
-
// Phase 4: Animate
|
|
9679
|
-
context.root.didUpdate();
|
|
9680
|
-
await new Promise((resolve) => frame.postRender(() => resolve()));
|
|
9681
|
-
const animations = [];
|
|
9682
|
-
for (const node of context.nodes.values()) {
|
|
9683
|
-
if (node.currentAnimation) {
|
|
9684
|
-
animations.push(node.currentAnimation);
|
|
9685
|
-
}
|
|
9686
|
-
}
|
|
9687
|
-
const groupAnimation = new GroupAnimation(animations);
|
|
9688
|
-
groupAnimation.finished.then(() => {
|
|
9689
|
-
// Only clean up nodes for elements no longer in the document.
|
|
9690
|
-
// Elements still in DOM keep their nodes so subsequent animations
|
|
9691
|
-
// can use the stored position snapshots (A→B→A pattern).
|
|
9692
|
-
const elementsToCleanup = new Set();
|
|
9693
|
-
for (const element of context.nodes.keys()) {
|
|
9694
|
-
if (!document.contains(element)) {
|
|
9695
|
-
elementsToCleanup.add(element);
|
|
9416
|
+
async start() {
|
|
9417
|
+
const beforeElements = collectLayoutElements(this.scope);
|
|
9418
|
+
const beforeRecords = this.buildRecords(beforeElements);
|
|
9419
|
+
beforeRecords.forEach(({ projection }) => {
|
|
9420
|
+
const hasCurrentAnimation = Boolean(projection.currentAnimation);
|
|
9421
|
+
const isSharedLayout = Boolean(projection.options.layoutId);
|
|
9422
|
+
if (hasCurrentAnimation && isSharedLayout) {
|
|
9423
|
+
const snapshot = snapshotFromTarget(projection);
|
|
9424
|
+
if (snapshot) {
|
|
9425
|
+
projection.snapshot = snapshot;
|
|
9696
9426
|
}
|
|
9427
|
+
else if (projection.snapshot) {
|
|
9428
|
+
projection.snapshot = undefined;
|
|
9429
|
+
}
|
|
9430
|
+
}
|
|
9431
|
+
else if (projection.snapshot &&
|
|
9432
|
+
(projection.currentAnimation || projection.isProjecting())) {
|
|
9433
|
+
projection.snapshot = undefined;
|
|
9697
9434
|
}
|
|
9698
|
-
|
|
9435
|
+
projection.isPresent = true;
|
|
9436
|
+
projection.willUpdate();
|
|
9699
9437
|
});
|
|
9700
|
-
this.
|
|
9438
|
+
await this.updateDom();
|
|
9439
|
+
const afterElements = collectLayoutElements(this.scope);
|
|
9440
|
+
const afterRecords = this.buildRecords(afterElements);
|
|
9441
|
+
this.handleExitingElements(beforeRecords, afterRecords);
|
|
9442
|
+
afterRecords.forEach(({ projection }) => {
|
|
9443
|
+
const instance = projection.instance;
|
|
9444
|
+
const resumeFromInstance = projection.resumeFrom
|
|
9445
|
+
?.instance;
|
|
9446
|
+
if (!instance || !resumeFromInstance)
|
|
9447
|
+
return;
|
|
9448
|
+
if (!("style" in instance))
|
|
9449
|
+
return;
|
|
9450
|
+
const currentTransform = instance.style.transform;
|
|
9451
|
+
const resumeFromTransform = resumeFromInstance.style.transform;
|
|
9452
|
+
if (currentTransform &&
|
|
9453
|
+
resumeFromTransform &&
|
|
9454
|
+
currentTransform === resumeFromTransform) {
|
|
9455
|
+
instance.style.transform = "";
|
|
9456
|
+
instance.style.transformOrigin = "";
|
|
9457
|
+
}
|
|
9458
|
+
});
|
|
9459
|
+
afterRecords.forEach(({ projection }) => {
|
|
9460
|
+
projection.isPresent = true;
|
|
9461
|
+
});
|
|
9462
|
+
const root = getProjectionRoot(afterRecords, beforeRecords);
|
|
9463
|
+
root?.didUpdate();
|
|
9464
|
+
await new Promise((resolve) => {
|
|
9465
|
+
frame.postRender(() => resolve());
|
|
9466
|
+
});
|
|
9467
|
+
const animations = collectAnimations(afterRecords);
|
|
9468
|
+
const animation = new GroupAnimation(animations);
|
|
9469
|
+
return animation;
|
|
9701
9470
|
}
|
|
9702
|
-
|
|
9703
|
-
|
|
9704
|
-
|
|
9705
|
-
|
|
9706
|
-
|
|
9707
|
-
}
|
|
9708
|
-
|
|
9709
|
-
? this.sharedTransitions
|
|
9710
|
-
: undefined
|
|
9711
|
-
|
|
9471
|
+
buildRecords(elements) {
|
|
9472
|
+
const records = [];
|
|
9473
|
+
const recordMap = new Map();
|
|
9474
|
+
for (const element of elements) {
|
|
9475
|
+
const parentRecord = findParentRecord(element, recordMap, this.scope);
|
|
9476
|
+
const { layout, layoutId } = readLayoutAttributes(element);
|
|
9477
|
+
const override = layoutId
|
|
9478
|
+
? this.sharedTransitions.get(layoutId)
|
|
9479
|
+
: undefined;
|
|
9480
|
+
const transition = override || this.defaultOptions;
|
|
9481
|
+
const record = getOrCreateRecord(element, parentRecord?.projection, {
|
|
9482
|
+
layout,
|
|
9483
|
+
layoutId,
|
|
9484
|
+
animationType: typeof layout === "string" ? layout : "both",
|
|
9485
|
+
transition: transition,
|
|
9486
|
+
});
|
|
9487
|
+
recordMap.set(element, record);
|
|
9488
|
+
records.push(record);
|
|
9489
|
+
}
|
|
9490
|
+
return records;
|
|
9491
|
+
}
|
|
9492
|
+
handleExitingElements(beforeRecords, afterRecords) {
|
|
9493
|
+
const afterElementsSet = new Set(afterRecords.map((record) => record.element));
|
|
9494
|
+
beforeRecords.forEach((record) => {
|
|
9495
|
+
if (afterElementsSet.has(record.element))
|
|
9496
|
+
return;
|
|
9497
|
+
// For shared layout elements, relegate to set up resumeFrom
|
|
9498
|
+
// so the remaining element animates from this position
|
|
9499
|
+
if (record.projection.options.layoutId) {
|
|
9500
|
+
record.projection.isPresent = false;
|
|
9501
|
+
record.projection.relegate();
|
|
9502
|
+
}
|
|
9503
|
+
record.visualElement.unmount();
|
|
9504
|
+
visualElementStore.delete(record.element);
|
|
9505
|
+
});
|
|
9506
|
+
// Clear resumeFrom on EXISTING nodes that point to unmounted projections
|
|
9507
|
+
// This prevents crossfade animation when the source element was removed entirely
|
|
9508
|
+
// But preserve resumeFrom for NEW nodes so they can animate from the old position
|
|
9509
|
+
// Also preserve resumeFrom for lead nodes that were just promoted via relegate
|
|
9510
|
+
const beforeElementsSet = new Set(beforeRecords.map((record) => record.element));
|
|
9511
|
+
afterRecords.forEach(({ element, projection }) => {
|
|
9512
|
+
if (beforeElementsSet.has(element) &&
|
|
9513
|
+
projection.resumeFrom &&
|
|
9514
|
+
!projection.resumeFrom.instance &&
|
|
9515
|
+
!projection.isLead()) {
|
|
9516
|
+
projection.resumeFrom = undefined;
|
|
9517
|
+
projection.snapshot = undefined;
|
|
9518
|
+
}
|
|
9519
|
+
});
|
|
9712
9520
|
}
|
|
9713
9521
|
}
|
|
9714
|
-
/**
|
|
9715
|
-
* Parse arguments for animateLayout overloads
|
|
9716
|
-
*/
|
|
9717
9522
|
function parseAnimateLayoutArgs(scopeOrUpdateDom, updateDomOrOptions, options) {
|
|
9718
9523
|
// animateLayout(updateDom)
|
|
9719
9524
|
if (typeof scopeOrUpdateDom === "function") {
|
|
@@ -9727,11 +9532,98 @@
|
|
|
9727
9532
|
const elements = resolveElements(scopeOrUpdateDom);
|
|
9728
9533
|
const scope = elements[0] || document;
|
|
9729
9534
|
return {
|
|
9730
|
-
scope
|
|
9535
|
+
scope,
|
|
9731
9536
|
updateDom: updateDomOrOptions,
|
|
9732
9537
|
defaultOptions: options,
|
|
9733
9538
|
};
|
|
9734
9539
|
}
|
|
9540
|
+
function collectLayoutElements(scope) {
|
|
9541
|
+
const elements = Array.from(scope.querySelectorAll(layoutSelector));
|
|
9542
|
+
if (scope instanceof Element && scope.matches(layoutSelector)) {
|
|
9543
|
+
if (!elements.includes(scope)) {
|
|
9544
|
+
elements.unshift(scope);
|
|
9545
|
+
}
|
|
9546
|
+
}
|
|
9547
|
+
return elements;
|
|
9548
|
+
}
|
|
9549
|
+
function readLayoutAttributes(element) {
|
|
9550
|
+
const layoutId = element.getAttribute("data-layout-id") || undefined;
|
|
9551
|
+
const rawLayout = element.getAttribute("data-layout");
|
|
9552
|
+
let layout;
|
|
9553
|
+
if (rawLayout === "" || rawLayout === "true") {
|
|
9554
|
+
layout = true;
|
|
9555
|
+
}
|
|
9556
|
+
else if (rawLayout) {
|
|
9557
|
+
layout = rawLayout;
|
|
9558
|
+
}
|
|
9559
|
+
return {
|
|
9560
|
+
layout,
|
|
9561
|
+
layoutId,
|
|
9562
|
+
};
|
|
9563
|
+
}
|
|
9564
|
+
function createVisualState() {
|
|
9565
|
+
return {
|
|
9566
|
+
latestValues: {},
|
|
9567
|
+
renderState: {
|
|
9568
|
+
transform: {},
|
|
9569
|
+
transformOrigin: {},
|
|
9570
|
+
style: {},
|
|
9571
|
+
vars: {},
|
|
9572
|
+
},
|
|
9573
|
+
};
|
|
9574
|
+
}
|
|
9575
|
+
function getOrCreateRecord(element, parentProjection, projectionOptions) {
|
|
9576
|
+
const existing = visualElementStore.get(element);
|
|
9577
|
+
const visualElement = existing ??
|
|
9578
|
+
new HTMLVisualElement({
|
|
9579
|
+
props: {},
|
|
9580
|
+
presenceContext: null,
|
|
9581
|
+
visualState: createVisualState(),
|
|
9582
|
+
}, { allowProjection: true });
|
|
9583
|
+
if (!existing || !visualElement.projection) {
|
|
9584
|
+
visualElement.projection = new HTMLProjectionNode(visualElement.latestValues, parentProjection);
|
|
9585
|
+
}
|
|
9586
|
+
visualElement.projection.setOptions({
|
|
9587
|
+
...projectionOptions,
|
|
9588
|
+
visualElement,
|
|
9589
|
+
});
|
|
9590
|
+
if (!visualElement.current) {
|
|
9591
|
+
visualElement.mount(element);
|
|
9592
|
+
}
|
|
9593
|
+
if (!existing) {
|
|
9594
|
+
visualElementStore.set(element, visualElement);
|
|
9595
|
+
}
|
|
9596
|
+
return {
|
|
9597
|
+
element,
|
|
9598
|
+
visualElement,
|
|
9599
|
+
projection: visualElement.projection,
|
|
9600
|
+
};
|
|
9601
|
+
}
|
|
9602
|
+
function findParentRecord(element, recordMap, scope) {
|
|
9603
|
+
let parent = element.parentElement;
|
|
9604
|
+
while (parent) {
|
|
9605
|
+
const record = recordMap.get(parent);
|
|
9606
|
+
if (record)
|
|
9607
|
+
return record;
|
|
9608
|
+
if (parent === scope)
|
|
9609
|
+
break;
|
|
9610
|
+
parent = parent.parentElement;
|
|
9611
|
+
}
|
|
9612
|
+
return undefined;
|
|
9613
|
+
}
|
|
9614
|
+
function getProjectionRoot(afterRecords, beforeRecords) {
|
|
9615
|
+
const record = afterRecords[0] || beforeRecords[0];
|
|
9616
|
+
return record?.projection.root;
|
|
9617
|
+
}
|
|
9618
|
+
function collectAnimations(afterRecords) {
|
|
9619
|
+
const animations = new Set();
|
|
9620
|
+
afterRecords.forEach((record) => {
|
|
9621
|
+
const animation = record.projection.currentAnimation;
|
|
9622
|
+
if (animation)
|
|
9623
|
+
animations.add(animation);
|
|
9624
|
+
});
|
|
9625
|
+
return Array.from(animations);
|
|
9626
|
+
}
|
|
9735
9627
|
|
|
9736
9628
|
/**
|
|
9737
9629
|
* @deprecated
|
|
@@ -10642,7 +10534,7 @@
|
|
|
10642
10534
|
const getEventTarget = (element) => element === document.scrollingElement ? window : element;
|
|
10643
10535
|
function scrollInfo(onScroll, { container = document.scrollingElement, trackContentSize = false, ...options } = {}) {
|
|
10644
10536
|
if (!container)
|
|
10645
|
-
return noop;
|
|
10537
|
+
return noop$1;
|
|
10646
10538
|
let containerHandlers = onScrollHandlers.get(container);
|
|
10647
10539
|
/**
|
|
10648
10540
|
* Get the onScroll handlers for this container.
|
|
@@ -10802,7 +10694,7 @@
|
|
|
10802
10694
|
|
|
10803
10695
|
function scroll(onScroll, { axis = "y", container = document.scrollingElement, ...options } = {}) {
|
|
10804
10696
|
if (!container)
|
|
10805
|
-
return noop;
|
|
10697
|
+
return noop$1;
|
|
10806
10698
|
const optionsWithDefaults = { axis, container, ...options };
|
|
10807
10699
|
return typeof onScroll === "function"
|
|
10808
10700
|
? attachToFunction(onScroll, optionsWithDefaults)
|
|
@@ -10964,7 +10856,7 @@
|
|
|
10964
10856
|
exports.createBox = createBox;
|
|
10965
10857
|
exports.createDelta = createDelta;
|
|
10966
10858
|
exports.createGeneratorEasing = createGeneratorEasing;
|
|
10967
|
-
exports.createProjectionNode = createProjectionNode
|
|
10859
|
+
exports.createProjectionNode = createProjectionNode;
|
|
10968
10860
|
exports.createRenderBatcher = createRenderBatcher;
|
|
10969
10861
|
exports.createScopedAnimate = createScopedAnimate;
|
|
10970
10862
|
exports.cubicBezier = cubicBezier;
|
|
@@ -11079,7 +10971,7 @@
|
|
|
11079
10971
|
exports.motionValue = motionValue;
|
|
11080
10972
|
exports.moveItem = moveItem;
|
|
11081
10973
|
exports.nodeGroup = nodeGroup;
|
|
11082
|
-
exports.noop = noop;
|
|
10974
|
+
exports.noop = noop$1;
|
|
11083
10975
|
exports.number = number;
|
|
11084
10976
|
exports.numberValueTypes = numberValueTypes;
|
|
11085
10977
|
exports.observeTimeline = observeTimeline;
|