diff --git a/src/compile/render-dom/wrappers/EachBlock.ts b/src/compile/render-dom/wrappers/EachBlock.ts index 603920070d..397a5629a7 100644 --- a/src/compile/render-dom/wrappers/EachBlock.ts +++ b/src/compile/render-dom/wrappers/EachBlock.ts @@ -341,7 +341,7 @@ export default class EachBlockWrapper extends Wrapper { block.builders.update.addBlock(deindent` const ${this.vars.each_block_value} = ${snippet}; - ${this.block.hasOutros && `@groupOutros();`} + ${this.block.hasOutros && `@group_outros();`} ${this.node.hasAnimation && `for (let #i = 0; #i < ${blocks}.length; #i += 1) ${blocks}[#i].r();`} ${blocks} = @updateKeyedEach(${blocks}, #component, changed, ${get_key}, ${dynamic ? '1' : '0'}, ctx, ${this.vars.each_block_value}, ${lookup}, ${updateMountNode}, ${destroy}, ${create_each_block}, "${mountOrIntro}", ${anchor}, ${this.vars.get_each_context}); ${this.node.hasAnimation && `for (let #i = 0; #i < ${blocks}.length; #i += 1) ${blocks}[#i].a();`} @@ -466,7 +466,7 @@ export default class EachBlockWrapper extends Wrapper { if (this.block.hasOutros) { destroy = deindent` - @groupOutros(); + @group_outros(); for (; #i < ${iterations}.length; #i += 1) ${outroBlock}(#i, 1); `; } else { diff --git a/src/compile/render-dom/wrappers/IfBlock.ts b/src/compile/render-dom/wrappers/IfBlock.ts index 4e4d3265b3..594f811090 100644 --- a/src/compile/render-dom/wrappers/IfBlock.ts +++ b/src/compile/render-dom/wrappers/IfBlock.ts @@ -322,7 +322,7 @@ export default class IfBlockWrapper extends Wrapper { const updateMountNode = this.getUpdateMountNode(anchor); const destroyOldBlock = deindent` - @groupOutros(); + @group_outros(); ${name}.o(function() { ${if_blocks}[${previous_block_index}].d(1); ${if_blocks}[${previous_block_index}] = null; @@ -445,7 +445,7 @@ export default class IfBlockWrapper extends Wrapper { // as that will typically result in glitching const exit = branch.block.hasOutroMethod ? deindent` - @groupOutros(); + @group_outros(); ${name}.o(function() { ${name}.d(1); ${name} = null; diff --git a/src/compile/render-dom/wrappers/InlineComponent/index.ts b/src/compile/render-dom/wrappers/InlineComponent/index.ts index 02227b86e3..a5427ef073 100644 --- a/src/compile/render-dom/wrappers/InlineComponent/index.ts +++ b/src/compile/render-dom/wrappers/InlineComponent/index.ts @@ -368,7 +368,7 @@ export default class InlineComponentWrapper extends Wrapper { block.builders.update.addBlock(deindent` if (${switch_value} !== (${switch_value} = ${snippet})) { if (${name}) { - @groupOutros(); + @group_outros(); const old_component = ${name}; old_component.$$.fragment.o(() => { old_component.$destroy(); diff --git a/src/internal/await-block.js b/src/internal/await-block.js index c579ef2242..5db7c8f1e5 100644 --- a/src/internal/await-block.js +++ b/src/internal/await-block.js @@ -1,5 +1,5 @@ import { assign, run_all, isPromise } from './utils.js'; -import { groupOutros } from './transitions.js'; +import { group_outros } from './transitions.js'; import { flush } from '../internal/scheduler.js'; export function handlePromise(promise, info) { @@ -17,7 +17,7 @@ export function handlePromise(promise, info) { if (info.blocks) { info.blocks.forEach((block, i) => { if (i !== index && block) { - groupOutros(); + group_outros(); block.o(() => { block.d(1); info.blocks[i] = null; diff --git a/src/internal/transitions.js b/src/internal/transitions.js index 839801ee4b..ad3b4d5f70 100644 --- a/src/internal/transitions.js +++ b/src/internal/transitions.js @@ -2,179 +2,177 @@ import { identity as linear, noop, run } from './utils.js'; import { loop } from './loop.js'; import { create_rule, delete_rule } from './style_manager.js'; +let promise; + +function wait() { + if (!promise) { + promise = Promise.resolve(); + promise.then(() => { + promise = null; + }); + } + + return promise; +} + +let outros; + +export function group_outros() { + outros = { + remaining: 0, + callbacks: [] + }; +} + export function wrapTransition(component, node, fn, params, intro) { - let obj = fn.call(component, node, params); + let config = fn.call(component, node, params); let duration; let ease; let cssText; let initialised = false; + let t = intro ? 0 : 1; + let running = false; + let running_program = null; + let pending_program = null; + + function start(program) { + node.dispatchEvent(new window.CustomEvent(`${program.b ? 'intro' : 'outro'}start`)); + + program.a = t; + program.delta = program.b - program.a; + program.duration = duration * Math.abs(program.b - program.a); + program.end = program.start + program.duration; + + if (config.css) { + if (config.delay) node.style.cssText = cssText; + + program.name = create_rule(program, ease, config.css); + + node.style.animation = (node.style.animation || '') + .split(', ') + .filter(anim => anim && (program.delta < 0 || !/__svelte/.test(anim))) + .concat(`${program.name} ${program.duration}ms linear 1 forwards`) + .join(', '); + } + + running_program = program; + pending_program = null; + } + + function update(now) { + const program = running_program; + if (!program) return; + + const p = now - program.start; + t = program.a + program.delta * ease(p / program.duration); + if (config.tick) config.tick(t, 1 - t); + } + + function done() { + const program = running_program; + running_program = null; + + t = program.b; + + if (config.tick) config.tick(t, 1 - t); + + node.dispatchEvent(new window.CustomEvent(`${program.b ? 'intro' : 'outro'}end`)); + + if (!program.b && !program.invalidated) { + program.group.callbacks.push(() => { + program.callback(); + if (config.css) delete_rule(node, program.name); + }); + + if (--program.group.remaining === 0) { + program.group.callbacks.forEach(run); + } + } else { + if (config.css) delete_rule(node, program.name); + } + + running = !!pending_program; + } + + function go(b, callback) { + duration = config.duration || 300; + ease = config.easing || linear; + + const program = { + start: window.performance.now() + (config.delay || 0), + b, + callback: callback || noop + }; + + if (intro && !initialised) { + if (config.css && config.delay) { + cssText = node.style.cssText; + node.style.cssText += config.css(0, 1); + } + + if (config.tick) config.tick(0, 1); + initialised = true; + } + + if (!b) { + program.group = outros; + outros.remaining += 1; + } + + if (config.delay) { + pending_program = program; + } else { + start(program); + } + + if (!running) { + running = true; + + const { abort, promise } = loop(now => { + if (running_program && now >= running_program.end) { + done(); + } + + if (pending_program && now >= pending_program.start) { + start(pending_program); + } + + if (running) { + update(now); + return true; + } + }); + } + } + return { - t: intro ? 0 : 1, - running: false, - program: null, - pending: null, - run(b, callback) { - if (typeof obj === 'function') { - transitionManager.wait().then(() => { - obj = obj(); - this._run(b, callback); + if (typeof config === 'function') { + wait().then(() => { + config = config(); + go(b, callback); }); } else { - this._run(b, callback); + go(b, callback); } }, - _run(b, callback) { - duration = obj.duration || 300; - ease = obj.easing || linear; - - const program = { - start: window.performance.now() + (obj.delay || 0), - b, - callback: callback || noop - }; - - if (intro && !initialised) { - if (obj.css && obj.delay) { - cssText = node.style.cssText; - node.style.cssText += obj.css(0, 1); - } - - if (obj.tick) obj.tick(0, 1); - initialised = true; - } - - if (!b) { - program.group = outros.current; - outros.current.remaining += 1; - } - - if (obj.delay) { - this.pending = program; - } else { - this.start(program); - } - - if (!this.running) { - this.running = true; - - const { abort, promise } = loop(now => { - if (this.program && now >= this.program.end) { - this.done(); - } - - if (this.pending && now >= this.pending.start) { - this.start(this.pending); - } - - if (this.running) { - this.update(now); - return true; - } - }); - } - }, - - start(program) { - node.dispatchEvent(new window.CustomEvent(`${program.b ? 'intro' : 'outro'}start`)); - - program.a = this.t; - program.delta = program.b - program.a; - program.duration = duration * Math.abs(program.b - program.a); - program.end = program.start + program.duration; - - if (obj.css) { - if (obj.delay) node.style.cssText = cssText; - - program.name = create_rule(program, ease, obj.css); - - node.style.animation = (node.style.animation || '') - .split(', ') - .filter(anim => anim && (program.delta < 0 || !/__svelte/.test(anim))) - .concat(`${program.name} ${program.duration}ms linear 1 forwards`) - .join(', '); - } - - this.program = program; - this.pending = null; - }, - - update(now) { - const program = this.program; - if (!program) return; - - const p = now - program.start; - this.t = program.a + program.delta * ease(p / program.duration); - if (obj.tick) obj.tick(this.t, 1 - this.t); - }, - - done() { - const program = this.program; - this.program = null; - - this.t = program.b; - - if (obj.tick) obj.tick(this.t, 1 - this.t); - - node.dispatchEvent(new window.CustomEvent(`${program.b ? 'intro' : 'outro'}end`)); - - if (!program.b && !program.invalidated) { - program.group.callbacks.push(() => { - program.callback(); - if (obj.css) delete_rule(node, program.name); - }); - - if (--program.group.remaining === 0) { - program.group.callbacks.forEach(run); - } - } else { - if (obj.css) delete_rule(node, program.name); - } - - this.running = !!this.pending; - }, - abort(reset) { - if (reset && obj.tick) obj.tick(1, 0); + if (reset && config.tick) config.tick(1, 0); - if (this.program) { - if (obj.css) delete_rule(node, this.program.name); - this.program = this.pending = null; - this.running = false; + if (running_program) { + if (config.css) delete_rule(node, running_program.name); + running_program = pending_program = null; + running = false; } }, invalidate() { - if (this.program) { - this.program.invalidated = true; + if (running_program) { + running_program.invalidated = true; } } }; -} - -export let outros = {}; - -export function groupOutros() { - outros.current = { - remaining: 0, - callbacks: [] - }; -} - -export var transitionManager = { - promise: null, - - wait() { - if (!transitionManager.promise) { - transitionManager.promise = Promise.resolve(); - transitionManager.promise.then(() => { - transitionManager.promise = null; - }); - } - - return transitionManager.promise; - } -}; +} \ No newline at end of file diff --git a/test/runtime/index.js b/test/runtime/index.js index 5569b444d1..334426c424 100644 --- a/test/runtime/index.js +++ b/test/runtime/index.js @@ -3,7 +3,7 @@ import * as path from "path"; import * as fs from "fs"; import { rollup } from 'rollup'; import * as virtual from 'rollup-plugin-virtual'; -import { transitionManager, clear_loops } from "../../internal.js"; +import { clear_loops } from "../../internal.js"; import { showOutput, @@ -93,9 +93,7 @@ describe("runtime", () => { return Promise.resolve() .then(() => { - // set of hacks to support transition tests - transitionManager.running = false; - transitionManager.transitions = []; + // hack to support transition tests clear_loops(); const raf = {