From f297102725708dc1ed3e648c229d7e78ef26c91b Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Sat, 16 Mar 2024 14:35:54 -0400 Subject: [PATCH] fix --- .../client/dom/elements/transitions.js | 63 ++++++++++++------- .../src/internal/client/reactivity/effects.js | 37 ++++++----- .../svelte/src/internal/client/types.d.ts | 1 + 3 files changed, 65 insertions(+), 36 deletions(-) diff --git a/packages/svelte/src/internal/client/dom/elements/transitions.js b/packages/svelte/src/internal/client/dom/elements/transitions.js index 9fc43a80b6..0f1c193b71 100644 --- a/packages/svelte/src/internal/client/dom/elements/transitions.js +++ b/packages/svelte/src/internal/client/dom/elements/transitions.js @@ -163,22 +163,12 @@ export function bind_transition(element, get_fn, get_params, direction, global) let callbacks = []; /** @param {number} target */ - function go(target) { + function start(target) { // TODO if this is an `in:` transition and we just called `out()`, do nothing (let it play until the block is destroyed, probably immediately) // TODO if this is an `out:` transition and we just called `in()`, abort everything and reset to 1 immediately // TODO add a `transition.abort()` method to cancel an outro transition immediately when an effect is destroyed before the transition finishes running — don't want tickers etc to continue - if (current_task) { - current_task.abort(); - current_task = null; - } - - if (current_animation && current_options) { - const time = /** @type {number} */ (current_animation.currentTime); - const duration = /** @type {number} */ (current_options.duration); - p = (Math.abs(current_delta) * time) / duration; - current_animation.cancel(); - } + stop(); current_options ??= get_fn()(element, get_params?.(), { direction }); @@ -251,6 +241,21 @@ export function bind_transition(element, get_fn, get_params, direction, global) } } + function stop() { + if (current_task) { + current_task.abort(); + current_task = null; + } + + if (current_animation && current_options) { + const time = /** @type {number} */ (current_animation.currentTime); + const duration = /** @type {number} */ (current_options.duration); + p = (Math.abs(current_delta) * time) / duration; + current_animation.cancel(); + current_animation = null; + } + } + /** @type {import('#client').Transition2} */ // TODO this needs to be `in()` and `out()` rather than `to()`, and both // need to be idempotent (because of `{#if ...}{#if ...}`). `out()` should @@ -259,19 +264,35 @@ export function bind_transition(element, get_fn, get_params, direction, global) const transition = { global, in() { - if (current_direction === TRANSITION_IN) return; - - current_direction = TRANSITION_IN; callbacks = []; - go(1); + + if (direction === 'in' || direction === 'both') { + if (current_direction !== TRANSITION_IN) { + current_direction = TRANSITION_IN; + start(1); + } + } else { + current_direction = 0; + stop(); + } }, out(callback) { - if (callback) callbacks.push(callback); - if (current_direction === TRANSITION_OUT) return; + if (direction === 'out' || direction === 'both') { + if (callback) { + callbacks.push(callback); + } - current_direction = TRANSITION_OUT; - go(0); - } + if (current_direction !== TRANSITION_OUT) { + current_direction = TRANSITION_OUT; + start(0); + } + } else { + if (callback) { + callback(); + } + } + }, + stop }; (effect.transitions ??= []).push(transition); diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js index 9f06f8fb92..78cab71f9f 100644 --- a/packages/svelte/src/internal/client/reactivity/effects.js +++ b/packages/svelte/src/internal/client/reactivity/effects.js @@ -232,23 +232,30 @@ export function render_effect(fn, block = current_block, managed = false, sync = } /** - * @param {import('#client').Effect} signal + * @param {import('#client').Effect} effect * @returns {void} */ -export function destroy_effect(signal) { - destroy_children(signal); - remove_reactions(signal, 0); - set_signal_status(signal, DESTROYED); - - signal.teardown?.(); - signal.ondestroy?.(); - signal.fn = - signal.effects = - signal.teardown = - signal.ondestroy = - signal.ctx = - signal.block = - signal.deps = +export function destroy_effect(effect) { + destroy_children(effect); + remove_reactions(effect, 0); + set_signal_status(effect, DESTROYED); + + if (effect.transitions) { + for (const transition of effect.transitions) { + transition.stop(); + } + } + + effect.teardown?.(); + effect.ondestroy?.(); + + effect.fn = + effect.effects = + effect.teardown = + effect.ondestroy = + effect.ctx = + effect.block = + effect.deps = null; } diff --git a/packages/svelte/src/internal/client/types.d.ts b/packages/svelte/src/internal/client/types.d.ts index 99f25092a0..fa56216eb2 100644 --- a/packages/svelte/src/internal/client/types.d.ts +++ b/packages/svelte/src/internal/client/types.d.ts @@ -331,6 +331,7 @@ export interface Transition2 { global: boolean; in: () => void; out: (callback?: () => void) => void; + stop: () => void; } export * from './reactivity/types';