diff --git a/src/runtime/internal/transitions.js b/src/runtime/internal/transitions.js index c303e819a8..942e54ec3b 100644 --- a/src/runtime/internal/transitions.js +++ b/src/runtime/internal/transitions.js @@ -12,13 +12,13 @@ let promise; /** * @returns {Promise} */ function wait() { - if (!promise) { - promise = Promise.resolve(); - promise.then(() => { - promise = null; - }); - } - return promise; + if (!promise) { + promise = Promise.resolve(); + promise.then(() => { + promise = null; + }); + } + return promise; } /** @@ -28,7 +28,7 @@ function wait() { * @returns {void} */ function dispatch(node, direction, kind) { - node.dispatchEvent(custom_event(`${direction ? 'intro' : 'outro'}${kind}`)); + node.dispatchEvent(custom_event(`${direction ? 'intro' : 'outro'}${kind}`)); } const outroing = new Set(); @@ -39,20 +39,20 @@ let outros; /** * @returns {void} */ export function group_outros() { - outros = { - r: 0, - c: [], - p: outros // parent group - }; + outros = { + r: 0, + c: [], + p: outros // parent group + }; } /** * @returns {void} */ export function check_outros() { - if (!outros.r) { - run_all(outros.c); - } - outros = outros.p; + if (!outros.r) { + run_all(outros.c); + } + outros = outros.p; } /** @@ -61,10 +61,10 @@ export function check_outros() { * @returns {void} */ export function transition_in(block, local) { - if (block && block.i) { - outroing.delete(block); - block.i(local); - } + if (block && block.i) { + outroing.delete(block); + block.i(local); + } } /** @@ -74,23 +74,20 @@ export function transition_in(block, local) { * @returns {void} */ export function transition_out(block, local, detach, callback) { - if (block && block.o) { - if (outroing.has(block)) - return; - outroing.add(block); - outros.c.push(() => { - outroing.delete(block); - if (callback) { - if (detach) - block.d(1); - callback(); - } - }); - block.o(local); - } - else if (callback) { - callback(); - } + if (block && block.o) { + if (outroing.has(block)) return; + outroing.add(block); + outros.c.push(() => { + outroing.delete(block); + if (callback) { + if (detach) block.d(1); + callback(); + } + }); + block.o(local); + } else if (callback) { + callback(); + } } /** @@ -104,77 +101,77 @@ const null_transition = { duration: 0 }; * @returns {{ start(): void; invalidate(): void; end(): void; }} */ export function create_in_transition(node, fn, params) { + /** + * @type {TransitionOptions} */ + const options = { direction: 'in' }; + let config = fn(node, params, options); + let running = false; + let animation_name; + let task; + let uid = 0; - /** - * @type {TransitionOptions} */ - const options = { direction: 'in' }; - let config = fn(node, params, options); - let running = false; - let animation_name; - let task; - let uid = 0; + /** + * @returns {void} */ + function cleanup() { + if (animation_name) delete_rule(node, animation_name); + } - /** - * @returns {void} */ - function cleanup() { - if (animation_name) - delete_rule(node, animation_name); - } - - /** - * @returns {void} */ - function go() { - const { delay = 0, duration = 300, easing = linear, tick = noop, css } = config || null_transition; - if (css) - animation_name = create_rule(node, 0, 1, duration, delay, easing, css, uid++); - tick(0, 1); - const start_time = now() + delay; - const end_time = start_time + duration; - if (task) - task.abort(); - running = true; - add_render_callback(() => dispatch(node, true, 'start')); - task = loop((now) => { - if (running) { - if (now >= end_time) { - tick(1, 0); - dispatch(node, true, 'end'); - cleanup(); - return (running = false); - } - if (now >= start_time) { - const t = easing((now - start_time) / duration); - tick(t, 1 - t); - } - } - return running; - }); - } - let started = false; - return { - start() { - if (started) - return; - started = true; - delete_rule(node); - if (is_function(config)) { - config = config(options); - wait().then(go); - } - else { - go(); - } - }, - invalidate() { - started = false; - }, - end() { - if (running) { - cleanup(); - running = false; - } - } - }; + /** + * @returns {void} */ + function go() { + const { + delay = 0, + duration = 300, + easing = linear, + tick = noop, + css + } = config || null_transition; + if (css) animation_name = create_rule(node, 0, 1, duration, delay, easing, css, uid++); + tick(0, 1); + const start_time = now() + delay; + const end_time = start_time + duration; + if (task) task.abort(); + running = true; + add_render_callback(() => dispatch(node, true, 'start')); + task = loop((now) => { + if (running) { + if (now >= end_time) { + tick(1, 0); + dispatch(node, true, 'end'); + cleanup(); + return (running = false); + } + if (now >= start_time) { + const t = easing((now - start_time) / duration); + tick(t, 1 - t); + } + } + return running; + }); + } + let started = false; + return { + start() { + if (started) return; + started = true; + delete_rule(node); + if (is_function(config)) { + config = config(options); + wait().then(go); + } else { + go(); + } + }, + invalidate() { + started = false; + }, + end() { + if (running) { + cleanup(); + running = false; + } + } + }; } /** @@ -184,67 +181,69 @@ export function create_in_transition(node, fn, params) { * @returns {{ end(reset: any): void; }} */ export function create_out_transition(node, fn, params) { + /** + * @type {TransitionOptions} */ + const options = { direction: 'out' }; + let config = fn(node, params, options); + let running = true; + let animation_name; + const group = outros; + group.r += 1; - /** - * @type {TransitionOptions} */ - const options = { direction: 'out' }; - let config = fn(node, params, options); - let running = true; - let animation_name; - const group = outros; - group.r += 1; - - /** - * @returns {void} */ - function go() { - const { delay = 0, duration = 300, easing = linear, tick = noop, css } = config || null_transition; - if (css) - animation_name = create_rule(node, 1, 0, duration, delay, easing, css); - const start_time = now() + delay; - const end_time = start_time + duration; - add_render_callback(() => dispatch(node, false, 'start')); - loop((now) => { - if (running) { - if (now >= end_time) { - tick(0, 1); - dispatch(node, false, 'end'); - if (!--group.r) { - // this will result in `end()` being called, - // so we don't need to clean up here - run_all(group.c); - } - return false; - } - if (now >= start_time) { - const t = easing((now - start_time) / duration); - tick(1 - t, t); - } - } - return running; - }); - } - if (is_function(config)) { - wait().then(() => { - // @ts-ignore - config = config(options); - go(); - }); - } - else { - go(); - } - return { - end(reset) { - if (reset && config.tick) { - config.tick(1, 0); - } - if (running) { - if (animation_name) - delete_rule(node, animation_name); - running = false; - } - } - }; + /** + * @returns {void} */ + function go() { + const { + delay = 0, + duration = 300, + easing = linear, + tick = noop, + css + } = config || null_transition; + if (css) animation_name = create_rule(node, 1, 0, duration, delay, easing, css); + const start_time = now() + delay; + const end_time = start_time + duration; + add_render_callback(() => dispatch(node, false, 'start')); + loop((now) => { + if (running) { + if (now >= end_time) { + tick(0, 1); + dispatch(node, false, 'end'); + if (!--group.r) { + // this will result in `end()` being called, + // so we don't need to clean up here + run_all(group.c); + } + return false; + } + if (now >= start_time) { + const t = easing((now - start_time) / duration); + tick(1 - t, t); + } + } + return running; + }); + } + if (is_function(config)) { + wait().then(() => { + // @ts-ignore + config = config(options); + go(); + }); + } else { + go(); + } + return { + end(reset) { + if (reset && config.tick) { + config.tick(1, 0); + } + if (running) { + if (animation_name) delete_rule(node, animation_name); + running = false; + } + } + }; } /** @@ -255,165 +254,170 @@ export function create_out_transition(node, fn, params) { * @returns {{ run(b: 0 | 1): void; end(): void; }} */ export function create_bidirectional_transition(node, fn, params, intro) { + /** + * @type {TransitionOptions} */ + const options = { direction: 'both' }; + let config = fn(node, params, options); + let t = intro ? 0 : 1; - /** - * @type {TransitionOptions} */ - const options = { direction: 'both' }; - let config = fn(node, params, options); - let t = intro ? 0 : 1; - - /** - * @type {Program | null} */ - let running_program = null; + /** + * @type {Program | null} */ + let running_program = null; - /** - * @type {PendingProgram | null} */ - let pending_program = null; - let animation_name = null; + /** + * @type {PendingProgram | null} */ + let pending_program = null; + let animation_name = null; - /** - * @returns {void} */ - function clear_animation() { - if (animation_name) - delete_rule(node, animation_name); - } + /** + * @returns {void} */ + function clear_animation() { + if (animation_name) delete_rule(node, animation_name); + } - /** - * @param {PendingProgram} program - * @param {number} duration - * @returns {Program} - */ - function init(program, duration) { - const d = (program.b - t); - duration *= Math.abs(d); - return { - a: t, - b: program.b, - d, - duration, - start: program.start, - end: program.start + duration, - group: program.group - }; - } + /** + * @param {PendingProgram} program + * @param {number} duration + * @returns {Program} + */ + function init(program, duration) { + const d = program.b - t; + duration *= Math.abs(d); + return { + a: t, + b: program.b, + d, + duration, + start: program.start, + end: program.start + duration, + group: program.group + }; + } - /** - * @param {INTRO | OUTRO} b - * @returns {void} - */ - function go(b) { - const { delay = 0, duration = 300, easing = linear, tick = noop, css } = config || null_transition; + /** + * @param {INTRO | OUTRO} b + * @returns {void} + */ + function go(b) { + const { + delay = 0, + duration = 300, + easing = linear, + tick = noop, + css + } = config || null_transition; - /** - * @type {PendingProgram} */ - const program = { - start: now() + delay, - b - }; - if (!b) { - // @ts-ignore todo: improve typings - program.group = outros; - outros.r += 1; - } - if (running_program || pending_program) { - pending_program = program; - } - else { - // if this is an intro, and there's a delay, we need to do - // an initial tick and/or apply CSS animation immediately - if (css) { - clear_animation(); - animation_name = create_rule(node, t, b, duration, delay, easing, css); - } - if (b) - tick(0, 1); - running_program = init(program, duration); - add_render_callback(() => dispatch(node, b, 'start')); - loop((now) => { - if (pending_program && now > pending_program.start) { - running_program = init(pending_program, duration); - pending_program = null; - dispatch(node, running_program.b, 'start'); - if (css) { - clear_animation(); - animation_name = create_rule(node, t, running_program.b, running_program.duration, 0, easing, config.css); - } - } - if (running_program) { - if (now >= running_program.end) { - tick((t = running_program.b), 1 - t); - dispatch(node, running_program.b, 'end'); - if (!pending_program) { - // we're done - if (running_program.b) { - // intro — we can tidy up immediately - clear_animation(); - } - else { - // outro — needs to be coordinated - if (!--running_program.group.r) - run_all(running_program.group.c); - } - } - running_program = null; - } - else if (now >= running_program.start) { - const p = now - running_program.start; - t = running_program.a + running_program.d * easing(p / running_program.duration); - tick(t, 1 - t); - } - } - return !!(running_program || pending_program); - }); - } - } - return { - run(b) { - if (is_function(config)) { - wait().then(() => { - const opts = { direction: b ? 'in' : 'out' }; - // @ts-ignore - config = config(opts); - go(b); - }); - } - else { - go(b); - } - }, - end() { - clear_animation(); - running_program = pending_program = null; - } - }; + /** + * @type {PendingProgram} */ + const program = { + start: now() + delay, + b + }; + if (!b) { + // @ts-ignore todo: improve typings + program.group = outros; + outros.r += 1; + } + if (running_program || pending_program) { + pending_program = program; + } else { + // if this is an intro, and there's a delay, we need to do + // an initial tick and/or apply CSS animation immediately + if (css) { + clear_animation(); + animation_name = create_rule(node, t, b, duration, delay, easing, css); + } + if (b) tick(0, 1); + running_program = init(program, duration); + add_render_callback(() => dispatch(node, b, 'start')); + loop((now) => { + if (pending_program && now > pending_program.start) { + running_program = init(pending_program, duration); + pending_program = null; + dispatch(node, running_program.b, 'start'); + if (css) { + clear_animation(); + animation_name = create_rule( + node, + t, + running_program.b, + running_program.duration, + 0, + easing, + config.css + ); + } + } + if (running_program) { + if (now >= running_program.end) { + tick((t = running_program.b), 1 - t); + dispatch(node, running_program.b, 'end'); + if (!pending_program) { + // we're done + if (running_program.b) { + // intro — we can tidy up immediately + clear_animation(); + } else { + // outro — needs to be coordinated + if (!--running_program.group.r) run_all(running_program.group.c); + } + } + running_program = null; + } else if (now >= running_program.start) { + const p = now - running_program.start; + t = running_program.a + running_program.d * easing(p / running_program.duration); + tick(t, 1 - t); + } + } + return !!(running_program || pending_program); + }); + } + } + return { + run(b) { + if (is_function(config)) { + wait().then(() => { + const opts = { direction: b ? 'in' : 'out' }; + // @ts-ignore + config = config(opts); + go(b); + }); + } else { + go(b); + } + }, + end() { + clear_animation(); + running_program = pending_program = null; + } + }; } - /** @typedef {1} INTRO */ /** @typedef {0} OUTRO */ /** @typedef {{ direction: 'in' | 'out' | 'both' }} TransitionOptions */ /** @typedef {(node: Element, params: any, options: TransitionOptions) => TransitionConfig} TransitionFn */ /** @typedef {Object} Outro - * @property {number} r + * @property {number} r * remaining outros - * @property {Function[]} c + * @property {Function[]} c * callbacks - * @property {Object} p + * @property {Object} p * parent outro */ /** @typedef {Object} PendingProgram * @property {number} start * @property {INTRO|OUTRO} b - * @property {Outro} [group] + * @property {Outro} [group] */ /** @typedef {Object} Program * @property {number} a * @property {INTRO|OUTRO} b - * @property {1|-1} d + * @property {1|-1} d * direction * @property {number} duration * @property {number} start * @property {number} end - * @property {Outro} [group] - */ \ No newline at end of file + * @property {Outro} [group] + */