fix: improve outro behavior with transitions (#10139)

* fix: improve outro behavior with transitions

* debug

* revise
pull/10137/head
Dominic Gannaway 1 year ago committed by GitHub
parent 4ad5b73341
commit 0e9b83c20d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,5 @@
---
'svelte': patch
---
fix: improve outro behavior with transitions

@ -1012,10 +1012,11 @@ export function mutate_store(store, expression, new_value) {
/** /**
* @param {import('./types.js').ComputationSignal} signal * @param {import('./types.js').ComputationSignal} signal
* @param {import('./types.js').ComputationSignal} root
* @param {boolean} inert * @param {boolean} inert
* @returns {void} * @returns {void}
*/ */
export function mark_subtree_inert(signal, inert) { export function mark_subtree_inert(signal, root, inert, visited_blocks = new Set()) {
const flags = signal.f; const flags = signal.f;
const is_already_inert = (flags & INERT) !== 0; const is_already_inert = (flags & INERT) !== 0;
if (is_already_inert !== inert) { if (is_already_inert !== inert) {
@ -1025,22 +1026,28 @@ export function mark_subtree_inert(signal, inert) {
} }
// Nested if block effects // Nested if block effects
const block = signal.b; const block = signal.b;
if (block !== null) { if (block !== null && !visited_blocks.has(block)) {
visited_blocks.add(block);
const type = block.t; const type = block.t;
if (type === IF_BLOCK) { if (type === IF_BLOCK) {
const condition_effect = block.e;
const root_block = root.b?.p;
if (condition_effect !== null && root_block?.t === IF_BLOCK) {
mark_subtree_inert(condition_effect, root, inert);
}
const consequent_effect = block.ce; const consequent_effect = block.ce;
if (consequent_effect !== null) { if (consequent_effect !== null) {
mark_subtree_inert(consequent_effect, inert); mark_subtree_inert(consequent_effect, root, inert, visited_blocks);
} }
const alternate_effect = block.ae; const alternate_effect = block.ae;
if (alternate_effect !== null) { if (alternate_effect !== null) {
mark_subtree_inert(alternate_effect, inert); mark_subtree_inert(alternate_effect, root, inert, visited_blocks);
} }
} else if (type === EACH_BLOCK) { } else if (type === EACH_BLOCK) {
const items = block.v; const items = block.v;
for (let { e: each_item_effect } of items) { for (let { e: each_item_effect } of items) {
if (each_item_effect !== null) { if (each_item_effect !== null) {
mark_subtree_inert(each_item_effect, inert); mark_subtree_inert(each_item_effect, root, inert, visited_blocks);
} }
} }
} }
@ -1050,7 +1057,7 @@ export function mark_subtree_inert(signal, inert) {
if (references !== null) { if (references !== null) {
let i; let i;
for (i = 0; i < references.length; i++) { for (i = 0; i < references.length; i++) {
mark_subtree_inert(references[i], inert); mark_subtree_inert(references[i], root, inert, visited_blocks);
} }
} }
} }

@ -371,22 +371,6 @@ function create_transition(dom, init, direction, effect) {
o() { o() {
// @ts-ignore // @ts-ignore
const has_keyed_transition = dom.__animate; const has_keyed_transition = dom.__animate;
const needs_reverse = direction === 'both' && curr_direction !== 'out';
curr_direction = 'out';
if (animation === null || cancelled) {
cancelled = false;
create_animation();
}
if (animation === null) {
transition.x();
} else {
dispatch_event(dom, 'outrostart');
if (needs_reverse) {
/** @type {Animation | TickAnimation} */ (animation).reverse();
} else {
/** @type {Animation | TickAnimation} */ (animation).play();
}
}
// If we're outroing an element that has an animation, then we need to fix // If we're outroing an element that has an animation, then we need to fix
// its position to ensure it behaves nicely without causing layout shift. // its position to ensure it behaves nicely without causing layout shift.
if (has_keyed_transition) { if (has_keyed_transition) {
@ -402,20 +386,46 @@ function create_transition(dom, init, direction, effect) {
dom.style.height = height; dom.style.height = height;
const b = dom.getBoundingClientRect(); const b = dom.getBoundingClientRect();
if (a.left !== b.left || a.top !== b.top) { if (a.left !== b.left || a.top !== b.top) {
// Previously, in the Svelte 4, we'd just apply the transform the the DOM element. However, const translate = `translate(${a.left - b.left}px, ${a.top - b.top}px)`;
// because we're now using Web Animations, we can't do that as it won't work properly if the const existing_transform = style.transform;
// animation is also making use of the same transformations. So instead, we apply an instantaneous if (existing_transform === 'none') {
// animation and pause it on the first frame, just applying the same behavior. dom.style.transform = translate;
const style = getComputedStyle(dom); } else {
const transform = style.transform === 'none' ? '' : style.transform; // Previously, in the Svelte 4, we'd just apply the transform the the DOM element. However,
const frame = { // because we're now using Web Animations, we can't do that as it won't work properly if the
transform: `${transform} translate(${a.left - b.left}px, ${a.top - b.top}px)` // animation is also making use of the same transformations. So instead, we apply an
}; // instantaneous animation and pause it on the first frame, just applying the same behavior.
const animation = dom.animate([frame, frame], { duration: 1 }); // We also need to take into consideration matrix transforms and how they might combine with
animation.pause(); // an existing behavior that is already in progress (such as scale).
// > Follow the white rabbit.
const transform = existing_transform.startsWith('matrix(1,')
? translate
: `matrix(1,0,0,1,0,0)`;
const frame = {
transform
};
const animation = dom.animate([frame, frame], { duration: 1 });
animation.pause();
}
} }
} }
} }
const needs_reverse = direction === 'both' && curr_direction !== 'out';
curr_direction = 'out';
if (animation === null || cancelled) {
cancelled = false;
create_animation();
}
if (animation === null) {
transition.x();
} else {
dispatch_event(dom, 'outrostart');
if (needs_reverse) {
/** @type {Animation | TickAnimation} */ (animation).reverse();
} else {
/** @type {Animation | TickAnimation} */ (animation).play();
}
}
}, },
// cancel // cancel
c() { c() {
@ -584,6 +594,7 @@ export function trigger_transitions(transitions, target_direction, from) {
const outros = []; const outros = [];
for (const transition of transitions) { for (const transition of transitions) {
const direction = transition.r; const direction = transition.r;
const effect = transition.e;
if (target_direction === 'in') { if (target_direction === 'in') {
if (direction === 'in' || direction === 'both') { if (direction === 'in' || direction === 'both') {
transition.in(); transition.in();
@ -591,7 +602,7 @@ export function trigger_transitions(transitions, target_direction, from) {
transition.c(); transition.c();
} }
transition.d.inert = false; transition.d.inert = false;
mark_subtree_inert(transition.e, false); mark_subtree_inert(effect, effect, false);
} else if (target_direction === 'key') { } else if (target_direction === 'key') {
if (direction === 'key') { if (direction === 'key') {
transition.p = transition.i(/** @type {DOMRect} */ (from)); transition.p = transition.i(/** @type {DOMRect} */ (from));
@ -603,7 +614,7 @@ export function trigger_transitions(transitions, target_direction, from) {
outros.push(transition.o); outros.push(transition.o);
} }
transition.d.inert = true; transition.d.inert = true;
mark_subtree_inert(transition.e, true); mark_subtree_inert(effect, effect, true);
} }
} }
if (outros.length > 0) { if (outros.length > 0) {

Loading…
Cancel
Save