motion 12.29.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 +241 -309
- 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);
|
|
@@ -2491,7 +2491,7 @@
|
|
|
2491
2491
|
this.animation.onfinish = null;
|
|
2492
2492
|
if (timeline && supportsScrollTimeline()) {
|
|
2493
2493
|
this.animation.timeline = timeline;
|
|
2494
|
-
return noop;
|
|
2494
|
+
return noop$1;
|
|
2495
2495
|
}
|
|
2496
2496
|
else {
|
|
2497
2497
|
return observe(this);
|
|
@@ -2783,7 +2783,7 @@
|
|
|
2783
2783
|
: new JSAnimation(resolvedOptions);
|
|
2784
2784
|
animation.finished.then(() => {
|
|
2785
2785
|
this.notifyFinished();
|
|
2786
|
-
}).catch(noop);
|
|
2786
|
+
}).catch(noop$1);
|
|
2787
2787
|
if (this.pendingTimeline) {
|
|
2788
2788
|
this.stopTimeline = animation.attachTimeline(this.pendingTimeline);
|
|
2789
2789
|
this.pendingTimeline = undefined;
|
|
@@ -3612,6 +3612,7 @@
|
|
|
3612
3612
|
}
|
|
3613
3613
|
function animateTarget(visualElement, targetAndTransition, { delay = 0, transitionOverride, type } = {}) {
|
|
3614
3614
|
let { transition = visualElement.getDefaultTransition(), transitionEnd, ...target } = targetAndTransition;
|
|
3615
|
+
const reduceMotion = transition?.reduceMotion;
|
|
3615
3616
|
if (transitionOverride)
|
|
3616
3617
|
transition = transitionOverride;
|
|
3617
3618
|
const animations = [];
|
|
@@ -3657,7 +3658,8 @@
|
|
|
3657
3658
|
}
|
|
3658
3659
|
}
|
|
3659
3660
|
addValueToWillChange(visualElement, key);
|
|
3660
|
-
|
|
3661
|
+
const shouldReduceMotion = reduceMotion ?? visualElement.shouldReduceMotion;
|
|
3662
|
+
value.start(animateMotionValue(key, value, valueTarget, shouldReduceMotion && positionalKeys.has(key)
|
|
3661
3663
|
? { type: false }
|
|
3662
3664
|
: valueTransition, visualElement, isHandoff));
|
|
3663
3665
|
const animation = value.animation;
|
|
@@ -5462,7 +5464,7 @@
|
|
|
5462
5464
|
constructor(update, options = {}) {
|
|
5463
5465
|
this.currentSubject = "root";
|
|
5464
5466
|
this.targets = new Map();
|
|
5465
|
-
this.notifyReady = noop;
|
|
5467
|
+
this.notifyReady = noop$1;
|
|
5466
5468
|
this.readyPromise = new Promise((resolve) => {
|
|
5467
5469
|
this.notifyReady = resolve;
|
|
5468
5470
|
});
|
|
@@ -7519,7 +7521,7 @@
|
|
|
7519
7521
|
: values.borderRadius;
|
|
7520
7522
|
}
|
|
7521
7523
|
const easeCrossfadeIn = /*@__PURE__*/ compress(0, 0.5, circOut);
|
|
7522
|
-
const easeCrossfadeOut = /*@__PURE__*/ compress(0.5, 0.95, noop);
|
|
7524
|
+
const easeCrossfadeOut = /*@__PURE__*/ compress(0.5, 0.95, noop$1);
|
|
7523
7525
|
function compress(min, max, easing) {
|
|
7524
7526
|
return (p) => {
|
|
7525
7527
|
// Could replace ifs with clamp
|
|
@@ -7758,7 +7760,7 @@
|
|
|
7758
7760
|
cancelTreeOptimisedTransformAnimations(parent);
|
|
7759
7761
|
}
|
|
7760
7762
|
}
|
|
7761
|
-
function createProjectionNode
|
|
7763
|
+
function createProjectionNode({ attachResizeListener, defaultParent, measureScroll, checkIsScrollRoot, resetTransform, }) {
|
|
7762
7764
|
return class ProjectionNode {
|
|
7763
7765
|
constructor(latestValues = {}, parent = defaultParent?.()) {
|
|
7764
7766
|
/**
|
|
@@ -9296,7 +9298,7 @@
|
|
|
9296
9298
|
*/
|
|
9297
9299
|
const roundPoint = userAgentContains("applewebkit/") && !userAgentContains("chrome/")
|
|
9298
9300
|
? Math.round
|
|
9299
|
-
: noop;
|
|
9301
|
+
: noop$1;
|
|
9300
9302
|
function roundAxis(axis) {
|
|
9301
9303
|
// Round to the nearest .5 pixels to support subpixel layouts
|
|
9302
9304
|
axis.min = roundPoint(axis.min);
|
|
@@ -9315,7 +9317,7 @@
|
|
|
9315
9317
|
return node !== node.root && node.scroll?.wasRoot;
|
|
9316
9318
|
}
|
|
9317
9319
|
|
|
9318
|
-
const DocumentProjectionNode = createProjectionNode
|
|
9320
|
+
const DocumentProjectionNode = createProjectionNode({
|
|
9319
9321
|
attachResizeListener: (ref, notify) => addDomEvent(ref, "resize", notify),
|
|
9320
9322
|
measureScroll: () => ({
|
|
9321
9323
|
x: document.documentElement.scrollLeft || document.body?.scrollLeft || 0,
|
|
@@ -9350,7 +9352,7 @@
|
|
|
9350
9352
|
const rootProjectionNode = {
|
|
9351
9353
|
current: undefined,
|
|
9352
9354
|
};
|
|
9353
|
-
const HTMLProjectionNode = createProjectionNode
|
|
9355
|
+
const HTMLProjectionNode = createProjectionNode({
|
|
9354
9356
|
measureScroll: (instance) => ({
|
|
9355
9357
|
x: instance.scrollLeft,
|
|
9356
9358
|
y: instance.scrollTop,
|
|
@@ -9370,315 +9372,153 @@
|
|
|
9370
9372
|
checkIsScrollRoot: (instance) => Boolean(window.getComputedStyle(instance).position === "fixed"),
|
|
9371
9373
|
});
|
|
9372
9374
|
|
|
9373
|
-
const
|
|
9374
|
-
|
|
9375
|
-
|
|
9376
|
-
|
|
9377
|
-
if (
|
|
9378
|
-
|
|
9379
|
-
|
|
9380
|
-
|
|
9381
|
-
|
|
9382
|
-
|
|
9383
|
-
return element.getAttribute("data-layout-id");
|
|
9384
|
-
}
|
|
9385
|
-
function hasLayout(element) {
|
|
9386
|
-
return (element.hasAttribute("data-layout") ||
|
|
9387
|
-
element.hasAttribute("data-layout-id"));
|
|
9388
|
-
}
|
|
9389
|
-
|
|
9390
|
-
let scaleCorrectorAdded = false;
|
|
9391
|
-
/**
|
|
9392
|
-
* Track active projection nodes per element to handle animation interruption.
|
|
9393
|
-
* When a new animation starts on an element that already has an active animation,
|
|
9394
|
-
* we need to stop the old animation so the new one can start from the current
|
|
9395
|
-
* visual position.
|
|
9396
|
-
*/
|
|
9397
|
-
const activeProjectionNodes = new WeakMap();
|
|
9398
|
-
function ensureScaleCorrectors() {
|
|
9399
|
-
if (scaleCorrectorAdded)
|
|
9400
|
-
return;
|
|
9401
|
-
scaleCorrectorAdded = true;
|
|
9402
|
-
addScaleCorrector({
|
|
9403
|
-
borderRadius: {
|
|
9404
|
-
...correctBorderRadius,
|
|
9405
|
-
applyTo: [
|
|
9406
|
-
"borderTopLeftRadius",
|
|
9407
|
-
"borderTopRightRadius",
|
|
9408
|
-
"borderBottomLeftRadius",
|
|
9409
|
-
"borderBottomRightRadius",
|
|
9410
|
-
],
|
|
9411
|
-
},
|
|
9412
|
-
borderTopLeftRadius: correctBorderRadius,
|
|
9413
|
-
borderTopRightRadius: correctBorderRadius,
|
|
9414
|
-
borderBottomLeftRadius: correctBorderRadius,
|
|
9415
|
-
borderBottomRightRadius: correctBorderRadius,
|
|
9416
|
-
boxShadow: correctBoxShadow,
|
|
9417
|
-
});
|
|
9418
|
-
}
|
|
9419
|
-
/**
|
|
9420
|
-
* Get DOM depth of an element
|
|
9421
|
-
*/
|
|
9422
|
-
function getDepth(element) {
|
|
9423
|
-
let depth = 0;
|
|
9424
|
-
let current = element.parentElement;
|
|
9425
|
-
while (current) {
|
|
9426
|
-
depth++;
|
|
9427
|
-
current = current.parentElement;
|
|
9428
|
-
}
|
|
9429
|
-
return depth;
|
|
9430
|
-
}
|
|
9431
|
-
/**
|
|
9432
|
-
* Find the closest projection parent for an element
|
|
9433
|
-
*/
|
|
9434
|
-
function findProjectionParent(element, nodeCache) {
|
|
9435
|
-
let parent = element.parentElement;
|
|
9436
|
-
while (parent) {
|
|
9437
|
-
const node = nodeCache.get(parent);
|
|
9438
|
-
if (node)
|
|
9439
|
-
return node;
|
|
9440
|
-
parent = parent.parentElement;
|
|
9441
|
-
}
|
|
9442
|
-
return undefined;
|
|
9443
|
-
}
|
|
9444
|
-
/**
|
|
9445
|
-
* Create or reuse a projection node for an element
|
|
9446
|
-
*/
|
|
9447
|
-
function createProjectionNode(element, parent, options, transition) {
|
|
9448
|
-
// Check for existing active node - reuse it to preserve animation state
|
|
9449
|
-
const existingNode = activeProjectionNodes.get(element);
|
|
9450
|
-
if (existingNode) {
|
|
9451
|
-
const visualElement = existingNode.options.visualElement;
|
|
9452
|
-
// Update transition options for the new animation
|
|
9453
|
-
const nodeTransition = transition
|
|
9454
|
-
? { duration: transition.duration, ease: transition.ease }
|
|
9455
|
-
: { duration: 0.3, ease: "easeOut" };
|
|
9456
|
-
existingNode.setOptions({
|
|
9457
|
-
...existingNode.options,
|
|
9458
|
-
animate: true,
|
|
9459
|
-
transition: nodeTransition,
|
|
9460
|
-
...options,
|
|
9461
|
-
});
|
|
9462
|
-
// Re-mount the node if it was previously unmounted
|
|
9463
|
-
// This re-adds it to root.nodes so didUpdate() will process it
|
|
9464
|
-
if (!existingNode.instance) {
|
|
9465
|
-
existingNode.mount(element);
|
|
9466
|
-
}
|
|
9467
|
-
return { node: existingNode, visualElement };
|
|
9468
|
-
}
|
|
9469
|
-
// No existing node - create a new one
|
|
9470
|
-
const latestValues = {};
|
|
9471
|
-
const visualElement = new HTMLVisualElement({
|
|
9472
|
-
visualState: {
|
|
9473
|
-
latestValues,
|
|
9474
|
-
renderState: {
|
|
9475
|
-
transformOrigin: {},
|
|
9476
|
-
transform: {},
|
|
9477
|
-
style: {},
|
|
9478
|
-
vars: {},
|
|
9479
|
-
},
|
|
9480
|
-
},
|
|
9481
|
-
presenceContext: null,
|
|
9482
|
-
props: {},
|
|
9483
|
-
});
|
|
9484
|
-
const node = new HTMLProjectionNode(latestValues, parent);
|
|
9485
|
-
// Convert AnimationOptions to transition format for the projection system
|
|
9486
|
-
const nodeTransition = transition
|
|
9487
|
-
? { duration: transition.duration, ease: transition.ease }
|
|
9488
|
-
: { duration: 0.3, ease: "easeOut" };
|
|
9489
|
-
node.setOptions({
|
|
9490
|
-
visualElement,
|
|
9491
|
-
layout: true,
|
|
9492
|
-
animate: true,
|
|
9493
|
-
transition: nodeTransition,
|
|
9494
|
-
...options,
|
|
9495
|
-
});
|
|
9496
|
-
node.mount(element);
|
|
9497
|
-
visualElement.projection = node;
|
|
9498
|
-
// Track this node as the active one for this element
|
|
9499
|
-
activeProjectionNodes.set(element, node);
|
|
9500
|
-
return { node, visualElement };
|
|
9501
|
-
}
|
|
9502
|
-
/**
|
|
9503
|
-
* Build a projection tree from a list of elements
|
|
9504
|
-
*/
|
|
9505
|
-
function buildProjectionTree(elements, existingContext, options) {
|
|
9506
|
-
ensureScaleCorrectors();
|
|
9507
|
-
const nodes = existingContext?.nodes ?? new Map();
|
|
9508
|
-
const visualElements = existingContext?.visualElements ?? new Map();
|
|
9509
|
-
const group = existingContext?.group ?? nodeGroup();
|
|
9510
|
-
const defaultTransition = options?.defaultTransition;
|
|
9511
|
-
const sharedTransitions = options?.sharedTransitions;
|
|
9512
|
-
// Sort elements by DOM depth (parents before children)
|
|
9513
|
-
const sorted = [...elements].sort((a, b) => getDepth(a) - getDepth(b));
|
|
9514
|
-
let root = existingContext?.root;
|
|
9515
|
-
for (const element of sorted) {
|
|
9516
|
-
// Skip if already has a node
|
|
9517
|
-
if (nodes.has(element))
|
|
9518
|
-
continue;
|
|
9519
|
-
const parent = findProjectionParent(element, nodes);
|
|
9520
|
-
const layoutId = getLayoutId(element);
|
|
9521
|
-
const layoutMode = element.getAttribute("data-layout");
|
|
9522
|
-
const nodeOptions = {
|
|
9523
|
-
layoutId: layoutId ?? undefined,
|
|
9524
|
-
animationType: parseLayoutMode(layoutMode),
|
|
9525
|
-
};
|
|
9526
|
-
// Use layoutId-specific transition if available, otherwise use default
|
|
9527
|
-
const transition = layoutId && sharedTransitions?.get(layoutId)
|
|
9528
|
-
? sharedTransitions.get(layoutId)
|
|
9529
|
-
: defaultTransition;
|
|
9530
|
-
const { node, visualElement } = createProjectionNode(element, parent, nodeOptions, transition);
|
|
9531
|
-
nodes.set(element, node);
|
|
9532
|
-
visualElements.set(element, visualElement);
|
|
9533
|
-
group.add(node);
|
|
9534
|
-
if (!root) {
|
|
9535
|
-
root = node.root;
|
|
9536
|
-
}
|
|
9537
|
-
}
|
|
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);
|
|
9538
9385
|
return {
|
|
9539
|
-
|
|
9540
|
-
|
|
9541
|
-
|
|
9542
|
-
|
|
9386
|
+
animationId: projection.root?.animationId ?? 0,
|
|
9387
|
+
measuredBox,
|
|
9388
|
+
layoutBox,
|
|
9389
|
+
latestValues: projection.animationValues || projection.latestValues || {},
|
|
9390
|
+
source: projection.id,
|
|
9543
9391
|
};
|
|
9544
9392
|
}
|
|
9545
|
-
/**
|
|
9546
|
-
* Parse the data-layout attribute value
|
|
9547
|
-
*/
|
|
9548
|
-
function parseLayoutMode(value) {
|
|
9549
|
-
if (value === "position")
|
|
9550
|
-
return "position";
|
|
9551
|
-
if (value === "size")
|
|
9552
|
-
return "size";
|
|
9553
|
-
if (value === "preserve-aspect")
|
|
9554
|
-
return "preserve-aspect";
|
|
9555
|
-
return "both";
|
|
9556
|
-
}
|
|
9557
|
-
/**
|
|
9558
|
-
* Clean up projection nodes for specific elements.
|
|
9559
|
-
* If elementsToCleanup is provided, only those elements are cleaned up.
|
|
9560
|
-
* If not provided, all nodes are cleaned up.
|
|
9561
|
-
*
|
|
9562
|
-
* This allows persisting elements to keep their nodes between animations,
|
|
9563
|
-
* matching React's behavior where nodes persist for elements that remain in the DOM.
|
|
9564
|
-
*/
|
|
9565
|
-
function cleanupProjectionTree(context, elementsToCleanup) {
|
|
9566
|
-
const elementsToProcess = elementsToCleanup
|
|
9567
|
-
? [...context.nodes.entries()].filter(([el]) => elementsToCleanup.has(el))
|
|
9568
|
-
: [...context.nodes.entries()];
|
|
9569
|
-
for (const [element, node] of elementsToProcess) {
|
|
9570
|
-
context.group.remove(node);
|
|
9571
|
-
node.unmount();
|
|
9572
|
-
// Only clear from activeProjectionNodes if this is still the active node.
|
|
9573
|
-
// A newer animation might have already taken over.
|
|
9574
|
-
if (activeProjectionNodes.get(element) === node) {
|
|
9575
|
-
activeProjectionNodes.delete(element);
|
|
9576
|
-
}
|
|
9577
|
-
context.nodes.delete(element);
|
|
9578
|
-
context.visualElements.delete(element);
|
|
9579
|
-
}
|
|
9580
|
-
}
|
|
9581
|
-
|
|
9582
9393
|
class LayoutAnimationBuilder {
|
|
9583
9394
|
constructor(scope, updateDom, defaultOptions) {
|
|
9584
9395
|
this.sharedTransitions = new Map();
|
|
9585
9396
|
this.notifyReady = noop;
|
|
9586
|
-
this.
|
|
9397
|
+
this.rejectReady = noop;
|
|
9587
9398
|
this.scope = scope;
|
|
9588
9399
|
this.updateDom = updateDom;
|
|
9589
9400
|
this.defaultOptions = defaultOptions;
|
|
9590
|
-
this.readyPromise = new Promise((resolve) => {
|
|
9401
|
+
this.readyPromise = new Promise((resolve, reject) => {
|
|
9591
9402
|
this.notifyReady = resolve;
|
|
9403
|
+
this.rejectReady = reject;
|
|
9404
|
+
});
|
|
9405
|
+
frame.postRender(() => {
|
|
9406
|
+
this.start().then(this.notifyReady).catch(this.rejectReady);
|
|
9592
9407
|
});
|
|
9593
|
-
// Queue execution on microtask to allow builder methods to be called
|
|
9594
|
-
queueMicrotask(() => this.execute());
|
|
9595
9408
|
}
|
|
9596
|
-
shared(id,
|
|
9597
|
-
this.sharedTransitions.set(id,
|
|
9409
|
+
shared(id, transition) {
|
|
9410
|
+
this.sharedTransitions.set(id, transition);
|
|
9598
9411
|
return this;
|
|
9599
9412
|
}
|
|
9600
|
-
then(
|
|
9601
|
-
return this.readyPromise.then(
|
|
9413
|
+
then(resolve, reject) {
|
|
9414
|
+
return this.readyPromise.then(resolve, reject);
|
|
9602
9415
|
}
|
|
9603
|
-
async
|
|
9604
|
-
|
|
9605
|
-
|
|
9606
|
-
|
|
9607
|
-
|
|
9608
|
-
|
|
9609
|
-
|
|
9610
|
-
|
|
9611
|
-
|
|
9612
|
-
|
|
9613
|
-
for (const node of context.nodes.values()) {
|
|
9614
|
-
node.isLayoutDirty = false;
|
|
9615
|
-
node.willUpdate();
|
|
9616
|
-
}
|
|
9617
|
-
}
|
|
9618
|
-
// Phase 2: Execute DOM update
|
|
9619
|
-
this.updateDom();
|
|
9620
|
-
// Phase 3: Post-mutation - Compare before/after elements
|
|
9621
|
-
const afterElements = getLayoutElements(this.scope);
|
|
9622
|
-
const beforeSet = new Set(beforeElements);
|
|
9623
|
-
const afterSet = new Set(afterElements);
|
|
9624
|
-
const entering = afterElements.filter((el) => !beforeSet.has(el));
|
|
9625
|
-
const exiting = beforeElements.filter((el) => !afterSet.has(el));
|
|
9626
|
-
// Build projection nodes for entering elements
|
|
9627
|
-
if (entering.length > 0) {
|
|
9628
|
-
context = buildProjectionTree(entering, context, this.getBuildOptions());
|
|
9629
|
-
}
|
|
9630
|
-
// No layout elements - return empty animation
|
|
9631
|
-
if (!context) {
|
|
9632
|
-
this.notifyReady(new GroupAnimation([]));
|
|
9633
|
-
return;
|
|
9634
|
-
}
|
|
9635
|
-
// Handle shared elements
|
|
9636
|
-
for (const element of exiting) {
|
|
9637
|
-
const node = context.nodes.get(element);
|
|
9638
|
-
node?.getStack()?.remove(node);
|
|
9639
|
-
}
|
|
9640
|
-
for (const element of entering) {
|
|
9641
|
-
context.nodes.get(element)?.promote();
|
|
9642
|
-
}
|
|
9643
|
-
// Phase 4: Animate
|
|
9644
|
-
context.root.didUpdate();
|
|
9645
|
-
await new Promise((resolve) => frame.postRender(() => resolve()));
|
|
9646
|
-
const animations = [];
|
|
9647
|
-
for (const node of context.nodes.values()) {
|
|
9648
|
-
if (node.currentAnimation) {
|
|
9649
|
-
animations.push(node.currentAnimation);
|
|
9650
|
-
}
|
|
9651
|
-
}
|
|
9652
|
-
const groupAnimation = new GroupAnimation(animations);
|
|
9653
|
-
groupAnimation.finished.then(() => {
|
|
9654
|
-
// Only clean up nodes for elements no longer in the document.
|
|
9655
|
-
// Elements still in DOM keep their nodes so subsequent animations
|
|
9656
|
-
// can use the stored position snapshots (A→B→A pattern).
|
|
9657
|
-
const elementsToCleanup = new Set();
|
|
9658
|
-
for (const element of context.nodes.keys()) {
|
|
9659
|
-
if (!document.contains(element)) {
|
|
9660
|
-
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;
|
|
9661
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;
|
|
9434
|
+
}
|
|
9435
|
+
projection.isPresent = true;
|
|
9436
|
+
projection.willUpdate();
|
|
9437
|
+
});
|
|
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 = "";
|
|
9662
9457
|
}
|
|
9663
|
-
cleanupProjectionTree(context, elementsToCleanup);
|
|
9664
9458
|
});
|
|
9665
|
-
|
|
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;
|
|
9666
9470
|
}
|
|
9667
|
-
|
|
9668
|
-
|
|
9669
|
-
|
|
9670
|
-
|
|
9671
|
-
|
|
9672
|
-
}
|
|
9673
|
-
|
|
9674
|
-
? this.sharedTransitions
|
|
9675
|
-
: undefined
|
|
9676
|
-
|
|
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
|
+
});
|
|
9677
9520
|
}
|
|
9678
9521
|
}
|
|
9679
|
-
/**
|
|
9680
|
-
* Parse arguments for animateLayout overloads
|
|
9681
|
-
*/
|
|
9682
9522
|
function parseAnimateLayoutArgs(scopeOrUpdateDom, updateDomOrOptions, options) {
|
|
9683
9523
|
// animateLayout(updateDom)
|
|
9684
9524
|
if (typeof scopeOrUpdateDom === "function") {
|
|
@@ -9692,11 +9532,98 @@
|
|
|
9692
9532
|
const elements = resolveElements(scopeOrUpdateDom);
|
|
9693
9533
|
const scope = elements[0] || document;
|
|
9694
9534
|
return {
|
|
9695
|
-
scope
|
|
9535
|
+
scope,
|
|
9696
9536
|
updateDom: updateDomOrOptions,
|
|
9697
9537
|
defaultOptions: options,
|
|
9698
9538
|
};
|
|
9699
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
|
+
}
|
|
9700
9627
|
|
|
9701
9628
|
/**
|
|
9702
9629
|
* @deprecated
|
|
@@ -10161,7 +10088,8 @@
|
|
|
10161
10088
|
* Creates an animation function that is optionally scoped
|
|
10162
10089
|
* to a specific element.
|
|
10163
10090
|
*/
|
|
10164
|
-
function createScopedAnimate(
|
|
10091
|
+
function createScopedAnimate(options = {}) {
|
|
10092
|
+
const { scope, reduceMotion } = options;
|
|
10165
10093
|
/**
|
|
10166
10094
|
* Implementation
|
|
10167
10095
|
*/
|
|
@@ -10169,7 +10097,9 @@
|
|
|
10169
10097
|
let animations = [];
|
|
10170
10098
|
let animationOnComplete;
|
|
10171
10099
|
if (isSequence(subjectOrSequence)) {
|
|
10172
|
-
animations = animateSequence(subjectOrSequence,
|
|
10100
|
+
animations = animateSequence(subjectOrSequence, reduceMotion !== undefined
|
|
10101
|
+
? { reduceMotion, ...optionsOrKeyframes }
|
|
10102
|
+
: optionsOrKeyframes, scope);
|
|
10173
10103
|
}
|
|
10174
10104
|
else {
|
|
10175
10105
|
// Extract top-level onComplete so it doesn't get applied per-value
|
|
@@ -10177,7 +10107,9 @@
|
|
|
10177
10107
|
if (typeof onComplete === "function") {
|
|
10178
10108
|
animationOnComplete = onComplete;
|
|
10179
10109
|
}
|
|
10180
|
-
animations = animateSubject(subjectOrSequence, optionsOrKeyframes,
|
|
10110
|
+
animations = animateSubject(subjectOrSequence, optionsOrKeyframes, (reduceMotion !== undefined
|
|
10111
|
+
? { reduceMotion, ...rest }
|
|
10112
|
+
: rest), scope);
|
|
10181
10113
|
}
|
|
10182
10114
|
const animation = new GroupAnimationWithThen(animations);
|
|
10183
10115
|
if (animationOnComplete) {
|
|
@@ -10602,7 +10534,7 @@
|
|
|
10602
10534
|
const getEventTarget = (element) => element === document.scrollingElement ? window : element;
|
|
10603
10535
|
function scrollInfo(onScroll, { container = document.scrollingElement, trackContentSize = false, ...options } = {}) {
|
|
10604
10536
|
if (!container)
|
|
10605
|
-
return noop;
|
|
10537
|
+
return noop$1;
|
|
10606
10538
|
let containerHandlers = onScrollHandlers.get(container);
|
|
10607
10539
|
/**
|
|
10608
10540
|
* Get the onScroll handlers for this container.
|
|
@@ -10762,7 +10694,7 @@
|
|
|
10762
10694
|
|
|
10763
10695
|
function scroll(onScroll, { axis = "y", container = document.scrollingElement, ...options } = {}) {
|
|
10764
10696
|
if (!container)
|
|
10765
|
-
return noop;
|
|
10697
|
+
return noop$1;
|
|
10766
10698
|
const optionsWithDefaults = { axis, container, ...options };
|
|
10767
10699
|
return typeof onScroll === "function"
|
|
10768
10700
|
? attachToFunction(onScroll, optionsWithDefaults)
|
|
@@ -10924,7 +10856,7 @@
|
|
|
10924
10856
|
exports.createBox = createBox;
|
|
10925
10857
|
exports.createDelta = createDelta;
|
|
10926
10858
|
exports.createGeneratorEasing = createGeneratorEasing;
|
|
10927
|
-
exports.createProjectionNode = createProjectionNode
|
|
10859
|
+
exports.createProjectionNode = createProjectionNode;
|
|
10928
10860
|
exports.createRenderBatcher = createRenderBatcher;
|
|
10929
10861
|
exports.createScopedAnimate = createScopedAnimate;
|
|
10930
10862
|
exports.cubicBezier = cubicBezier;
|
|
@@ -11039,7 +10971,7 @@
|
|
|
11039
10971
|
exports.motionValue = motionValue;
|
|
11040
10972
|
exports.moveItem = moveItem;
|
|
11041
10973
|
exports.nodeGroup = nodeGroup;
|
|
11042
|
-
exports.noop = noop;
|
|
10974
|
+
exports.noop = noop$1;
|
|
11043
10975
|
exports.number = number;
|
|
11044
10976
|
exports.numberValueTypes = numberValueTypes;
|
|
11045
10977
|
exports.observeTimeline = observeTimeline;
|