Skip to content

Commit 0e9b83c

Browse files
authored
fix: improve outro behavior with transitions (#10139)
* fix: improve outro behavior with transitions * debug * revise
1 parent 4ad5b73 commit 0e9b83c

File tree

3 files changed

+58
-35
lines changed

3 files changed

+58
-35
lines changed

.changeset/empty-bulldogs-exercise.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
fix: improve outro behavior with transitions

packages/svelte/src/internal/client/runtime.js

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,10 +1012,11 @@ export function mutate_store(store, expression, new_value) {
10121012

10131013
/**
10141014
* @param {import('./types.js').ComputationSignal} signal
1015+
* @param {import('./types.js').ComputationSignal} root
10151016
* @param {boolean} inert
10161017
* @returns {void}
10171018
*/
1018-
export function mark_subtree_inert(signal, inert) {
1019+
export function mark_subtree_inert(signal, root, inert, visited_blocks = new Set()) {
10191020
const flags = signal.f;
10201021
const is_already_inert = (flags & INERT) !== 0;
10211022
if (is_already_inert !== inert) {
@@ -1025,22 +1026,28 @@ export function mark_subtree_inert(signal, inert) {
10251026
}
10261027
// Nested if block effects
10271028
const block = signal.b;
1028-
if (block !== null) {
1029+
if (block !== null && !visited_blocks.has(block)) {
1030+
visited_blocks.add(block);
10291031
const type = block.t;
10301032
if (type === IF_BLOCK) {
1033+
const condition_effect = block.e;
1034+
const root_block = root.b?.p;
1035+
if (condition_effect !== null && root_block?.t === IF_BLOCK) {
1036+
mark_subtree_inert(condition_effect, root, inert);
1037+
}
10311038
const consequent_effect = block.ce;
10321039
if (consequent_effect !== null) {
1033-
mark_subtree_inert(consequent_effect, inert);
1040+
mark_subtree_inert(consequent_effect, root, inert, visited_blocks);
10341041
}
10351042
const alternate_effect = block.ae;
10361043
if (alternate_effect !== null) {
1037-
mark_subtree_inert(alternate_effect, inert);
1044+
mark_subtree_inert(alternate_effect, root, inert, visited_blocks);
10381045
}
10391046
} else if (type === EACH_BLOCK) {
10401047
const items = block.v;
10411048
for (let { e: each_item_effect } of items) {
10421049
if (each_item_effect !== null) {
1043-
mark_subtree_inert(each_item_effect, inert);
1050+
mark_subtree_inert(each_item_effect, root, inert, visited_blocks);
10441051
}
10451052
}
10461053
}
@@ -1050,7 +1057,7 @@ export function mark_subtree_inert(signal, inert) {
10501057
if (references !== null) {
10511058
let i;
10521059
for (i = 0; i < references.length; i++) {
1053-
mark_subtree_inert(references[i], inert);
1060+
mark_subtree_inert(references[i], root, inert, visited_blocks);
10541061
}
10551062
}
10561063
}

packages/svelte/src/internal/client/transitions.js

Lines changed: 40 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -371,22 +371,6 @@ function create_transition(dom, init, direction, effect) {
371371
o() {
372372
// @ts-ignore
373373
const has_keyed_transition = dom.__animate;
374-
const needs_reverse = direction === 'both' && curr_direction !== 'out';
375-
curr_direction = 'out';
376-
if (animation === null || cancelled) {
377-
cancelled = false;
378-
create_animation();
379-
}
380-
if (animation === null) {
381-
transition.x();
382-
} else {
383-
dispatch_event(dom, 'outrostart');
384-
if (needs_reverse) {
385-
/** @type {Animation | TickAnimation} */ (animation).reverse();
386-
} else {
387-
/** @type {Animation | TickAnimation} */ (animation).play();
388-
}
389-
}
390374
// If we're outroing an element that has an animation, then we need to fix
391375
// its position to ensure it behaves nicely without causing layout shift.
392376
if (has_keyed_transition) {
@@ -402,20 +386,46 @@ function create_transition(dom, init, direction, effect) {
402386
dom.style.height = height;
403387
const b = dom.getBoundingClientRect();
404388
if (a.left !== b.left || a.top !== b.top) {
405-
// Previously, in the Svelte 4, we'd just apply the transform the the DOM element. However,
406-
// because we're now using Web Animations, we can't do that as it won't work properly if the
407-
// animation is also making use of the same transformations. So instead, we apply an instantaneous
408-
// animation and pause it on the first frame, just applying the same behavior.
409-
const style = getComputedStyle(dom);
410-
const transform = style.transform === 'none' ? '' : style.transform;
411-
const frame = {
412-
transform: `${transform} translate(${a.left - b.left}px, ${a.top - b.top}px)`
413-
};
414-
const animation = dom.animate([frame, frame], { duration: 1 });
415-
animation.pause();
389+
const translate = `translate(${a.left - b.left}px, ${a.top - b.top}px)`;
390+
const existing_transform = style.transform;
391+
if (existing_transform === 'none') {
392+
dom.style.transform = translate;
393+
} else {
394+
// Previously, in the Svelte 4, we'd just apply the transform the the DOM element. However,
395+
// because we're now using Web Animations, we can't do that as it won't work properly if the
396+
// animation is also making use of the same transformations. So instead, we apply an
397+
// instantaneous animation and pause it on the first frame, just applying the same behavior.
398+
// We also need to take into consideration matrix transforms and how they might combine with
399+
// an existing behavior that is already in progress (such as scale).
400+
// > Follow the white rabbit.
401+
const transform = existing_transform.startsWith('matrix(1,')
402+
? translate
403+
: `matrix(1,0,0,1,0,0)`;
404+
const frame = {
405+
transform
406+
};
407+
const animation = dom.animate([frame, frame], { duration: 1 });
408+
animation.pause();
409+
}
416410
}
417411
}
418412
}
413+
const needs_reverse = direction === 'both' && curr_direction !== 'out';
414+
curr_direction = 'out';
415+
if (animation === null || cancelled) {
416+
cancelled = false;
417+
create_animation();
418+
}
419+
if (animation === null) {
420+
transition.x();
421+
} else {
422+
dispatch_event(dom, 'outrostart');
423+
if (needs_reverse) {
424+
/** @type {Animation | TickAnimation} */ (animation).reverse();
425+
} else {
426+
/** @type {Animation | TickAnimation} */ (animation).play();
427+
}
428+
}
419429
},
420430
// cancel
421431
c() {
@@ -584,14 +594,15 @@ export function trigger_transitions(transitions, target_direction, from) {
584594
const outros = [];
585595
for (const transition of transitions) {
586596
const direction = transition.r;
597+
const effect = transition.e;
587598
if (target_direction === 'in') {
588599
if (direction === 'in' || direction === 'both') {
589600
transition.in();
590601
} else {
591602
transition.c();
592603
}
593604
transition.d.inert = false;
594-
mark_subtree_inert(transition.e, false);
605+
mark_subtree_inert(effect, effect, false);
595606
} else if (target_direction === 'key') {
596607
if (direction === 'key') {
597608
transition.p = transition.i(/** @type {DOMRect} */ (from));
@@ -603,7 +614,7 @@ export function trigger_transitions(transitions, target_direction, from) {
603614
outros.push(transition.o);
604615
}
605616
transition.d.inert = true;
606-
mark_subtree_inert(transition.e, true);
617+
mark_subtree_inert(effect, effect, true);
607618
}
608619
}
609620
if (outros.length > 0) {

0 commit comments

Comments
 (0)