pull/10798/head
Rich Harris 2 years ago
parent 21f9f3c268
commit e5cc4c029b

@ -138,7 +138,7 @@ export function transition(flags, element, get_fn, get_params) {
/** @type {'in' | 'out' | 'both'} */
var direction = is_intro && is_outro ? 'both' : is_intro ? 'in' : 'out';
/** @type {import('#client').TransitionConfig | ((opts: { direction: 'in' | 'out' }) => import('#client').TransitionConfig) | undefined} */
/** @type {import('#client').AnimationConfig | ((opts: { direction: 'in' | 'out' }) => import('#client').AnimationConfig) | undefined} */
var current_options;
var inert = element.inert;
@ -183,6 +183,8 @@ export function transition(flags, element, get_fn, get_params) {
fn?.();
});
// TODO arguably the outro should never null itself out until _all_ outros for this effect have completed...
// in that case we wouldn't need to store `reset` separately
reset = outro.reset;
} else {
fn?.();
@ -213,15 +215,19 @@ export function transition(flags, element, get_fn, get_params) {
}
/**
* Animates an element, according to the provided configuration
* @param {Element} element
* @param {import('#client').TransitionConfig | ((opts: { direction: 'in' | 'out' }) => import('#client').TransitionConfig)} options
* @param {import('#client').Animation | undefined} counterpart
* @param {number} t2
* @param {import('#client').AnimationConfig | ((opts: { direction: 'in' | 'out' }) => import('#client').AnimationConfig)} options
* @param {import('#client').Animation | undefined} counterpart The corresponding intro/outro to this outro/intro
* @param {number} t2 The target `t` value `1` for intro, `0` for outro
* @param {(() => void) | undefined} callback
* @returns {import('#client').Animation}
*/
function animate(element, options, counterpart, t2, callback) {
if (is_function(options)) {
// In the case of a deferred transition (such as `crossfade`), `option` will be
// a function rather than an `AnimationConfig`. We need to call this function
// once DOM has been updated...
/** @type {import('#client').Animation} */
var a;
@ -230,23 +236,25 @@ function animate(element, options, counterpart, t2, callback) {
a = animate(element, o, counterpart, t2, callback);
});
// ...but we want to do so without using `async`/`await` everywhere, so
// we return a facade that allows everything to remain synchronous
return {
abort: () => a.abort(),
neuter: () => a.neuter(),
deactivate: () => a.deactivate(),
reset: () => a.reset(),
p: (now) => a.p(now)
t: (now) => a.t(now)
};
}
counterpart?.neuter();
counterpart?.deactivate();
if (!options?.duration) {
callback?.();
return {
abort: noop,
neuter: noop,
deactivate: noop,
reset: noop,
p: () => t2
t: () => t2
};
}
@ -255,7 +263,7 @@ function animate(element, options, counterpart, t2, callback) {
var { delay = 0, duration, css, tick, easing = linear } = options;
var start = raf.now() + delay;
var t1 = counterpart?.p(start) ?? 1 - t2;
var t1 = counterpart?.t(start) ?? 1 - t2;
var delta = t2 - t1;
duration *= Math.abs(delta);
@ -319,7 +327,7 @@ function animate(element, options, counterpart, t2, callback) {
animation?.cancel();
task?.abort();
},
neuter: () => {
deactivate: () => {
callback = undefined;
},
reset: () => {
@ -327,7 +335,7 @@ function animate(element, options, counterpart, t2, callback) {
tick?.(1, 0);
}
},
p: (now) => {
t: (now) => {
var t = t1 + delta * easing((now - start) / duration);
return Math.min(1, Math.max(0, t));
}

@ -169,31 +169,38 @@ export interface TransitionManager {
}
export interface AnimationManager {
/** An element with an `animate:` directive */
element: Element;
/** Called during keyed each block reconciliation, before updates */
measure: () => void;
/** Called during keyed each block reconciliation, after updates — this triggers the animation */
apply: () => void;
}
export interface Animation {
/** Abort the animation */
abort: () => void;
neuter: () => void;
/** Allow the animation to continue running, but remove any callback. This prevents the removal of an outroing block if the corresponding intro has a `delay` */
deactivate: () => void;
/** Resets an animation to its starting state, if it uses `tick`. Exposed as a separate method so that an aborted `out:` can still reset even if the `outro` had already completed */
reset: () => void;
p: (now: number) => number;
/** Get the `t` value (between `0` and `1`) of the animation, so that its counterpart can start from the right place */
t: (now: number) => number;
}
export type TransitionFn<P> = (
element: Element,
props: P,
options: { direction?: 'in' | 'out' | 'both' }
) => TransitionConfig | ((options: { direction?: 'in' | 'out' }) => TransitionConfig);
) => AnimationConfig | ((options: { direction?: 'in' | 'out' }) => AnimationConfig);
export type AnimateFn<P> = (
element: Element,
rects: { from: DOMRect; to: DOMRect },
props: P
) => TransitionConfig;
) => AnimationConfig;
export type TransitionConfig = {
export type AnimationConfig = {
delay?: number;
duration?: number;
easing?: (t: number) => number;

Loading…
Cancel
Save