diff --git a/src/compiler/compile/render_dom/Block.ts b/src/compiler/compile/render_dom/Block.ts index b77cf6111253..f864dc22465c 100644 --- a/src/compiler/compile/render_dom/Block.ts +++ b/src/compiler/compile/render_dom/Block.ts @@ -205,6 +205,10 @@ export default class Block { this.has_animation = true; } + group_transition_out(fn) { + return b`@group_transition_out((#transition_out) => { ${fn(x`#transition_out`)} })`; + } + add_variable(id: Identifier, init?: Node) { if (this.variables.has(id.name)) { throw new Error( @@ -230,17 +234,14 @@ export default class Block { get_contents(key?: any) { const { dev } = this.renderer.options; - if (this.has_outros) { + if (this.has_outros || this.has_intros) { this.add_variable({ type: 'Identifier', name: '#current' }); - if (this.chunks.intro.length > 0) { - this.chunks.intro.push(b`#current = true;`); - this.chunks.mount.push(b`#current = true;`); - } + this.chunks.intro.push(b`#current = true;`); + this.chunks.mount.push(b`#current = true;`); + - if (this.chunks.outro.length > 0) { - this.chunks.outro.push(b`#current = false;`); - } + this.chunks.outro.push(b`#current = false;`); } if (this.autofocus) { @@ -341,7 +342,7 @@ export default class Block { properties.intro = noop; } else { properties.intro = x`function #intro(#local) { - ${this.has_outros && b`if (#current) return;`} + ${b`if (#current) return;`} ${this.chunks.intro} }`; } diff --git a/src/compiler/compile/render_dom/wrappers/EachBlock.ts b/src/compiler/compile/render_dom/wrappers/EachBlock.ts index 1efadfb90cf2..3a6ad03240a5 100644 --- a/src/compiler/compile/render_dom/wrappers/EachBlock.ts +++ b/src/compiler/compile/render_dom/wrappers/EachBlock.ts @@ -7,6 +7,7 @@ import FragmentWrapper from './Fragment'; import { b, x } from 'code-red'; import ElseBlock from '../../nodes/ElseBlock'; import { Identifier, Node } from 'estree'; +import bit_state from '../../utils/bit_state'; export class ElseBlockWrapper extends Wrapper { node: ElseBlock; @@ -423,25 +424,18 @@ export default class EachBlockWrapper extends Wrapper { const dynamic = this.block.has_update_method; - const destroy = this.node.has_animation - ? (this.block.has_outros - ? `@fix_and_outro_and_destroy_block` - : `@fix_and_destroy_block`) - : this.block.has_outros - ? `@outro_and_destroy_block` - : `@destroy_block`; + const transition_state = bit_state([dynamic, this.node.has_animation, this.block.has_outros]); + const update_keyed_each = (transition_out) => + b`${iterations} = @update_keyed_each(${iterations}, #dirty, #ctx, ${transition_state}, ${get_key}, ${this.vars.each_block_value}, ${lookup}, ${update_mount_node}, ${create_each_block}, ${update_anchor_node}, ${this.vars.get_each_context}, ${transition_out});`; if (this.dependencies.size) { this.updates.push(b` const ${this.vars.each_block_value} = ${snippet}; ${this.renderer.options.dev && b`@validate_each_argument(${this.vars.each_block_value});`} - - ${this.block.has_outros && b`@group_outros();`} ${this.node.has_animation && b`for (let #i = 0; #i < ${view_length}; #i += 1) ${iterations}[#i].r();`} ${this.renderer.options.dev && b`@validate_each_keys(#ctx, ${this.vars.each_block_value}, ${this.vars.get_each_context}, ${get_key});`} - ${iterations} = @update_keyed_each(${iterations}, #dirty, ${get_key}, ${dynamic ? 1 : 0}, #ctx, ${this.vars.each_block_value}, ${lookup}, ${update_mount_node}, ${destroy}, ${create_each_block}, ${update_anchor_node}, ${this.vars.get_each_context}); + ${(this.block.has_outros ? this.block.group_transition_out : v => v(null))(update_keyed_each)} ${this.node.has_animation && b`for (let #i = 0; #i < ${view_length}; #i += 1) ${iterations}[#i].a();`} - ${this.block.has_outros && b`@check_outros();`} `); } @@ -552,20 +546,14 @@ export default class EachBlockWrapper extends Wrapper { let remove_old_blocks; if (this.block.has_outros) { - const out = block.get_unique_name('out'); - - block.chunks.init.push(b` - const ${out} = i => @transition_out(${iterations}[i], 1, 1, () => { - ${iterations}[i] = null; - }); - `); - remove_old_blocks = b` - @group_outros(); - for (#i = ${data_length}; #i < ${view_length}; #i += 1) { - ${out}(#i); - } - @check_outros(); - `; + remove_old_blocks = this.block.group_transition_out((transition_out) => + b`for (#i = ${data_length}; #i < ${view_length}; #i += 1) { + const #index = #i; + ${transition_out}(${iterations}[#index], () => { + ${iterations}[#index] = null; + }); + }` + ); } else { remove_old_blocks = b` for (${this.block.has_update_method ? null : x`#i = ${data_length}`}; #i < ${this.block.has_update_method ? view_length : '#old_length'}; #i += 1) { diff --git a/src/compiler/compile/render_dom/wrappers/Element/index.ts b/src/compiler/compile/render_dom/wrappers/Element/index.ts index 83bc8be94e66..8e2eadf401f8 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/index.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/index.ts @@ -26,6 +26,7 @@ import { Identifier } from 'estree'; import EventHandler from './EventHandler'; import { extract_names } from 'periscopic'; import Action from '../../../nodes/Action'; +import Transition from '../../../nodes/Transition'; const events = [ { @@ -379,8 +380,18 @@ export default class ElementWrapper extends Wrapper { this.add_attributes(block); this.add_directives_in_order(block); - this.add_transitions(block); - this.add_animation(block); + const { intro, outro } = this.node; + if (intro || outro) { + if (intro === outro) { + this.add_bidi_transition(block, intro); + } else { + this.add_intro(block, intro, outro); + this.add_outro(block, intro, outro); + } + } + if (this.node.animation) { + this.add_animation(block, intro); + } this.add_classes(block); this.add_manual_style_scoping(block); @@ -728,165 +739,98 @@ export default class ElementWrapper extends Wrapper { } } - add_transitions( - block: Block - ) { - const { intro, outro } = this.node; - if (!intro && !outro) return; - - if (intro === outro) { - // bidirectional transition - const name = block.get_unique_name(`${this.var.name}_transition`); - const snippet = intro.expression - ? intro.expression.manipulate(block) - : x`{}`; - - block.add_variable(name); - - const fn = this.renderer.reference(intro.name); - - const intro_block = b` - @add_render_callback(() => { - if (!${name}) ${name} = @create_bidirectional_transition(${this.var}, ${fn}, ${snippet}, true); - ${name}.run(1); - }); - `; - - const outro_block = b` - if (!${name}) ${name} = @create_bidirectional_transition(${this.var}, ${fn}, ${snippet}, false); - ${name}.run(0); - `; - - if (intro.is_local) { - block.chunks.intro.push(b` - if (#local) { - ${intro_block} - } - `); - - block.chunks.outro.push(b` - if (#local) { - ${outro_block} - } - `); - } else { - block.chunks.intro.push(intro_block); - block.chunks.outro.push(outro_block); - } - - block.chunks.destroy.push(b`if (detaching && ${name}) ${name}.end();`); + add_bidi_transition(block: Block, intro: Transition) { + const transition = block.get_unique_name(`${this.var.name}_transition`); + const snippet = intro.expression ? intro.expression.manipulate(block) : null; + const fn = this.renderer.reference(intro.name); + + + block.add_variable(transition); + block.chunks.hydrate.push(b`${transition} = @create_bidirectional_transition(${this.var}, ${fn}, ${snippet});`); + if (intro.expression) { + const dirty = block.renderer.dirty([intro.name,...Array.from(intro.expression.dependencies)]); + block.chunks.update.push(b`if (${dirty}) ${transition}.u(${fn}, ${snippet});`); } + let intro_block = b`${transition}.i();`; + let outro_block = b`${transition}.o();`; - else { - const intro_name = intro && block.get_unique_name(`${this.var.name}_intro`); - const outro_name = outro && block.get_unique_name(`${this.var.name}_outro`); - - if (intro) { - block.add_variable(intro_name); - const snippet = intro.expression - ? intro.expression.manipulate(block) - : x`{}`; - - const fn = this.renderer.reference(intro.name); - - let intro_block; - - if (outro) { - intro_block = b` - @add_render_callback(() => { - if (${outro_name}) ${outro_name}.end(1); - if (!${intro_name}) ${intro_name} = @create_in_transition(${this.var}, ${fn}, ${snippet}); - ${intro_name}.start(); - }); - `; - - block.chunks.outro.push(b`if (${intro_name}) ${intro_name}.invalidate();`); - } else { - intro_block = b` - if (!${intro_name}) { - @add_render_callback(() => { - ${intro_name} = @create_in_transition(${this.var}, ${fn}, ${snippet}); - ${intro_name}.start(); - }); - } - `; - } + if (intro.is_local) { + intro_block = b`if (#local) { ${intro_block} }`; + outro_block = b`if (#local) { ${outro_block} }`; + } + block.chunks.intro.push(intro_block); + block.chunks.outro.push(outro_block); - if (intro.is_local) { - intro_block = b` - if (#local) { - ${intro_block} - } - `; + block.chunks.destroy.push(b`if (detaching && ${transition}) ${transition}.d();`); + } + add_intro(block: Block, intro: Transition, outro: Transition) { + if (outro) { + const outro_var = block.alias(`${this.var.name}_outro`); + block.chunks.intro.push(b`${outro_var}(1);`); + } + if (this.node.animation) { + const [unfreeze_var] = run_animation(this, block); + block.chunks.intro.push(b` + if (${unfreeze_var}) { + ${unfreeze_var}(); + ${unfreeze_var} = void 0; } + `); + } + if (!intro) return; - block.chunks.intro.push(intro_block); - } - - if (outro) { - block.add_variable(outro_name); - const snippet = outro.expression - ? outro.expression.manipulate(block) - : x`{}`; - - const fn = this.renderer.reference(outro.name); - - if (!intro) { - block.chunks.intro.push(b` - if (${outro_name}) ${outro_name}.end(1); - `); - } + const [intro_var, node, transitionFn, params] = run_transition(this, block, intro, `intro`); + block.add_variable(intro_var, x`@noop`); - // TODO hide elements that have outro'd (unless they belong to a still-outroing - // group) prior to their removal from the DOM - let outro_block = b` - ${outro_name} = @create_out_transition(${this.var}, ${fn}, ${snippet}); - `; + let start_intro = b` + ${intro_var}(); + ${intro_var} = @run_in(${node}, ${transitionFn}, ${params}); + `; + if (intro.is_local) start_intro = b`if (#local) { ${start_intro} }`; + block.chunks.intro.push(start_intro); + } + // TODO + // hide elements that have outro'd prior to their removal from the DOM + // ( ...unless they belong to a still-outroing group ) + add_outro(block: Block, _intro: Transition, outro: Transition) { + if (!outro) return; - if (outro.is_local) { - outro_block = b` - if (#local) { - ${outro_block} - } - `; - } + const [outro_var, node, transitionFn, params] = run_transition(this, block, outro, `outro`); + block.add_variable(outro_var, x`@noop`); - block.chunks.outro.push(outro_block); + let start_outro = b`${outro_var} = @run_out(${node}, ${transitionFn}, ${params});`; + if (outro.is_local) start_outro = b`if (#local) { ${start_outro} }`; + block.chunks.outro.push(start_outro); - block.chunks.destroy.push(b`if (detaching && ${outro_name}) ${outro_name}.end();`); - } - } + block.chunks.destroy.push(b`if (detaching) ${outro_var}();`); } - add_animation(block: Block) { - if (!this.node.animation) return; + add_animation(block: Block, _intro: Transition) { - const { outro } = this.node; + const [unfreeze_var, rect_var, stop_animation_var, name_var, params_var] = run_animation(this, block); - const rect = block.get_unique_name('rect'); - const stop_animation = block.get_unique_name('stop_animation'); - - block.add_variable(rect); - block.add_variable(stop_animation, x`@noop`); + block.add_variable(unfreeze_var); + block.add_variable(rect_var); + block.add_variable(stop_animation_var, x`@noop`); block.chunks.measure.push(b` - ${rect} = ${this.var}.getBoundingClientRect(); + ${rect_var} = ${this.var}.getBoundingClientRect(); `); block.chunks.fix.push(b` - @fix_position(${this.var}); - ${stop_animation}(); - ${outro && b`@add_transform(${this.var}, ${rect});`} + ${stop_animation_var}(); + ${unfreeze_var} = @fix_position(${this.var}, ${rect_var}); `); - const params = this.node.animation.expression ? this.node.animation.expression.manipulate(block) : x`{}`; - - const name = this.renderer.reference(this.node.animation.name); - block.chunks.animate.push(b` - ${stop_animation}(); - ${stop_animation} = @create_animation(${this.var}, ${rect}, ${name}, ${params}); + if (${unfreeze_var} || !${rect_var}) return + else { + ${stop_animation_var}(); + ${stop_animation_var} = @run_animation(${this.var}, ${rect_var}, ${name_var}, ${params_var}); + } `); + + block.chunks.destroy.push(b`${unfreeze_var} = void 0;`); } add_classes(block: Block) { @@ -995,3 +939,20 @@ function to_html(wrappers: Array, blo } }); } +function run_animation(element: ElementWrapper, block: Block) { + return [ + block.alias('unfreeze'), + block.alias('rect'), + block.alias('stop_animation'), + element.renderer.reference(element.node.animation.name), + element.node.animation.expression ? element.node.animation.expression.manipulate(block) : null, + ]; +} +function run_transition(element: ElementWrapper, block: Block, transition: Transition, type: string) { + return [ + /* node_intro */ block.alias(`${element.var.name}_${type}`), + /* node */ element.var, + /* transitionFn */ element.renderer.reference(transition.name), + /* params */ transition.expression ? transition.expression.manipulate(block) : null, + ]; +} diff --git a/src/compiler/compile/render_dom/wrappers/IfBlock.ts b/src/compiler/compile/render_dom/wrappers/IfBlock.ts index 220b52990201..412b5bb37dec 100644 --- a/src/compiler/compile/render_dom/wrappers/IfBlock.ts +++ b/src/compiler/compile/render_dom/wrappers/IfBlock.ts @@ -434,13 +434,10 @@ export default class IfBlockWrapper extends Wrapper { if (this.needs_update) { const update_mount_node = this.get_update_mount_node(anchor); - const destroy_old_block = b` - @group_outros(); - @transition_out(${if_blocks}[${previous_block_index}], 1, 1, () => { - ${if_blocks}[${previous_block_index}] = null; - }); - @check_outros(); - `; + const destroy_old_block = block.group_transition_out( + (transition_out) => + b`${transition_out}(${if_blocks}[${previous_block_index}], () => {${if_blocks}[${previous_block_index}] = null;})` + ); const create_new_block = b` ${name} = ${if_blocks}[${current_block_type_index}]; @@ -556,11 +553,7 @@ export default class IfBlockWrapper extends Wrapper { if (${branch.condition}) { ${enter} } else if (${name}) { - @group_outros(); - @transition_out(${name}, 1, 1, () => { - ${name} = null; - }); - @check_outros(); + ${block.group_transition_out((transition_out) => b`${transition_out}(${name},() => {${name} = null;})`)} } `); } else { diff --git a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts index 00f803bbbd48..cf6a15c9fa61 100644 --- a/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts +++ b/src/compiler/compile/render_dom/wrappers/InlineComponent/index.ts @@ -451,12 +451,11 @@ export default class InlineComponentWrapper extends Wrapper { block.chunks.update.push(b` if (${switch_value} !== (${switch_value} = ${snippet})) { if (${name}) { - @group_outros(); const old_component = ${name}; - @transition_out(old_component.$$.fragment, 1, 0, () => { - @destroy_component(old_component, 1); - }); - @check_outros(); + ${block.group_transition_out( + (transition_out) => + b`${transition_out}(old_component.$$.fragment, () => { @destroy_component(old_component, 1); }, 0);` + )} } if (${switch_value}) { diff --git a/src/compiler/compile/utils/bit_state.ts b/src/compiler/compile/utils/bit_state.ts new file mode 100644 index 000000000000..7523606c50cf --- /dev/null +++ b/src/compiler/compile/utils/bit_state.ts @@ -0,0 +1 @@ +export default (arr) => arr.reduce((state, bool, index) => (bool ? (state |= 1 << index) : state), 0); \ No newline at end of file diff --git a/src/runtime/animate/index.ts b/src/runtime/animate/index.ts index 087c0f7141c1..b1d4e509a6a4 100644 --- a/src/runtime/animate/index.ts +++ b/src/runtime/animate/index.ts @@ -1,41 +1,21 @@ -import { cubicOut } from 'svelte/easing'; -import { is_function } from 'svelte/internal'; - -// todo: same as Transition, should it be shared? -export interface AnimationConfig { - delay?: number; - duration?: number; - easing?: (t: number) => number; - css?: (t: number, u: number) => string; - tick?: (t: number, u: number) => void; -} - -interface FlipParams { - delay: number; - duration: number | ((len: number) => number); - easing: (t: number) => number; -} - -export function flip(node: Element, animation: { from: DOMRect; to: DOMRect }, params: FlipParams): AnimationConfig { - const style = getComputedStyle(node); - const transform = style.transform === 'none' ? '' : style.transform; +import { cubicOut } from "svelte/easing"; +import { run_duration, TimeableConfig, CssTransitionConfig } from "svelte/internal"; +export function flip( + node: Element, + animation: { from: DOMRect; to: DOMRect }, + { delay = 0, duration = (d: number) => Math.sqrt(d) * 30, easing = cubicOut }: TimeableConfig +): CssTransitionConfig { + const style = getComputedStyle(node).transform; + const transform = style === "none" ? "" : style; const scaleX = animation.from.width / node.clientWidth; const scaleY = animation.from.height / node.clientHeight; const dx = (animation.from.left - animation.to.left) / scaleX; const dy = (animation.from.top - animation.to.top) / scaleY; - const d = Math.sqrt(dx * dx + dy * dy); - - const { - delay = 0, - duration = (d: number) => Math.sqrt(d) * 120, - easing = cubicOut - } = params; - return { delay, - duration: is_function(duration) ? duration(d) : duration, + duration: run_duration(duration, Math.sqrt(dx * dx + dy * dy)), easing, css: (_t, u) => `transform: ${transform} translate(${u * dx}px, ${u * dy}px);` }; diff --git a/src/runtime/internal/Component.ts b/src/runtime/internal/Component.ts index 7d2a92fa1bce..f3af2f33e423 100644 --- a/src/runtime/internal/Component.ts +++ b/src/runtime/internal/Component.ts @@ -1,10 +1,10 @@ -import { add_render_callback, flush, schedule_update, dirty_components } from './scheduler'; +import { add_render_callback, flush, schedule_update } from './scheduler'; import { current_component, set_current_component } from './lifecycle'; import { blank_object, is_function, run, run_all, noop } from './utils'; import { children, detach } from './dom'; import { transition_in } from './transitions'; -interface Fragment { +export interface Fragment { key: string|null; first: null; /* create */ c: () => void; @@ -20,7 +20,7 @@ interface Fragment { /* destroy */ d: (detaching: 0|1) => void; } // eslint-disable-next-line @typescript-eslint/class-name-casing -interface T$$ { +export interface T$$ { dirty: number[]; ctx: null|any; bound: any; @@ -87,15 +87,6 @@ export function destroy_component(component, detaching) { } } -function make_dirty(component, i) { - if (component.$$.dirty[0] === -1) { - dirty_components.push(component); - schedule_update(); - component.$$.dirty.fill(0); - } - component.$$.dirty[(i / 31) | 0] |= (1 << (i % 31)); -} - export function init(component, options, instance, create_fragment, not_equal, props, dirty = [-1]) { const parent_component = current_component; set_current_component(component); @@ -127,13 +118,18 @@ export function init(component, options, instance, create_fragment, not_equal, p let ready = false; $$.ctx = instance - ? instance(component, prop_values, (i, ret, ...rest) => { - const value = rest.length ? rest[0] : ret; - if ($$.ctx && not_equal($$.ctx[i], $$.ctx[i] = value)) { - if ($$.bound[i]) $$.bound[i](value); - if (ready) make_dirty(component, i); - } - return ret; + ? instance(component, prop_values, (i, res, ...rest) => { + if ($$.ctx && not_equal($$.ctx[i], ($$.ctx[i] = rest.length ? rest[0] : res))) { + if (i in $$.bound) $$.bound[i]($$.ctx[i]); + if (ready) { + if (-1 === $$.dirty[0]) { + schedule_update(component); + $$.dirty.fill(0); + } + $$.dirty[(i / 31) | 0] |= 1 << i % 31; + } + } + return res; }) : []; diff --git a/src/runtime/internal/animations.ts b/src/runtime/internal/animations.ts deleted file mode 100644 index 6dc6a446f6b3..000000000000 --- a/src/runtime/internal/animations.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { identity as linear, noop } from './utils'; -import { now } from './environment'; -import { loop } from './loop'; -import { create_rule, delete_rule } from './style_manager'; -import { AnimationConfig } from '../animate'; - - -//todo: documentation says it is DOMRect, but in IE it would be ClientRect -type PositionRect = DOMRect|ClientRect; - -type AnimationFn = (node: Element, { from, to }: { from: PositionRect; to: PositionRect }, params: any) => AnimationConfig; - -export function create_animation(node: Element & ElementCSSInlineStyle, from: PositionRect, fn: AnimationFn, params) { - if (!from) return noop; - - const to = node.getBoundingClientRect(); - if (from.left === to.left && from.right === to.right && from.top === to.top && from.bottom === to.bottom) return noop; - - - const { - delay = 0, - duration = 300, - easing = linear, - // @ts-ignore todo: should this be separated from destructuring? Or start/end added to public api and documentation? - start: start_time = now() + delay, - // @ts-ignore todo: - end = start_time + duration, - tick = noop, - css - } = fn(node, { from, to }, params); - - let running = true; - let started = false; - let name; - - function start() { - if (css) { - name = create_rule(node, 0, 1, duration, delay, easing, css); - } - - if (!delay) { - started = true; - } - } - - function stop() { - if (css) delete_rule(node, name); - running = false; - } - - loop(now => { - if (!started && now >= start_time) { - started = true; - } - - if (started && now >= end) { - tick(1, 0); - stop(); - } - - if (!running) { - return false; - } - - if (started) { - const p = now - start_time; - const t = 0 + 1 * easing(p / duration); - tick(t, 1 - t); - } - - return true; - }); - - start(); - - tick(0, 1); - - return stop; -} - -export function fix_position(node: Element & ElementCSSInlineStyle) { - const style = getComputedStyle(node); - - if (style.position !== 'absolute' && style.position !== 'fixed') { - const { width, height } = style; - const a = node.getBoundingClientRect(); - node.style.position = 'absolute'; - node.style.width = width; - node.style.height = height; - add_transform(node, a); - } -} - -export function add_transform(node: Element & ElementCSSInlineStyle, a: PositionRect) { - const b = node.getBoundingClientRect(); - - if (a.left !== b.left || a.top !== b.top) { - const style = getComputedStyle(node); - const transform = style.transform === 'none' ? '' : style.transform; - - node.style.transform = `${transform} translate(${a.left - b.left}px, ${a.top - b.top}px)`; - } -} diff --git a/src/runtime/internal/await_block.ts b/src/runtime/internal/await_block.ts index f70cbd6c2c7d..e88dc159fc24 100644 --- a/src/runtime/internal/await_block.ts +++ b/src/runtime/internal/await_block.ts @@ -1,5 +1,5 @@ import { is_promise } from './utils'; -import { check_outros, group_outros, transition_in, transition_out } from './transitions'; +import { transition_in, group_transition_out } from './transitions'; import { flush } from './scheduler'; import { get_current_component, set_current_component } from './lifecycle'; @@ -26,11 +26,11 @@ export function handle_promise(promise, info) { if (info.blocks) { info.blocks.forEach((block, i) => { if (i !== index && block) { - group_outros(); - transition_out(block, 1, 1, () => { - info.blocks[i] = null; + group_transition_out((transition_out) => { + transition_out(block, () => { + info.blocks[i] = null; + }); }); - check_outros(); } }); } else { diff --git a/src/runtime/internal/dev.ts b/src/runtime/internal/dev.ts index 751f1f802bc0..f1bdfc9ca28d 100644 --- a/src/runtime/internal/dev.ts +++ b/src/runtime/internal/dev.ts @@ -140,4 +140,4 @@ export function loop_guard(timeout) { throw new Error(`Infinite loop detected`); } }; -} +} \ No newline at end of file diff --git a/src/runtime/internal/dom.ts b/src/runtime/internal/dom.ts index f67fd13b2d7c..07d0896a271a 100644 --- a/src/runtime/internal/dom.ts +++ b/src/runtime/internal/dom.ts @@ -354,4 +354,4 @@ export class HtmlTag { d() { this.n.forEach(detach); } -} +} \ No newline at end of file diff --git a/src/runtime/internal/environment.ts b/src/runtime/internal/environment.ts index 7123399180d2..178f167a9c5f 100644 --- a/src/runtime/internal/environment.ts +++ b/src/runtime/internal/environment.ts @@ -1,12 +1,12 @@ -import { noop } from './utils'; +import { noop } from "./utils"; -export const is_client = typeof window !== 'undefined'; +export const is_client = typeof window !== "undefined"; export let now: () => number = is_client ? () => window.performance.now() : () => Date.now(); -export let raf = is_client ? cb => requestAnimationFrame(cb) : noop; +export let raf = /*#__PURE__*/ is_client ? (cb: FrameRequestCallback) => requestAnimationFrame(cb) : noop; // used internally for testing export function set_now(fn) { diff --git a/src/runtime/internal/globals.ts b/src/runtime/internal/globals.ts index b97f81ab9f47..ab75e38db6ad 100644 --- a/src/runtime/internal/globals.ts +++ b/src/runtime/internal/globals.ts @@ -4,4 +4,4 @@ export const globals = (typeof window !== 'undefined' ? window : typeof globalThis !== 'undefined' ? globalThis - : global) as unknown as typeof globalThis; + : global) as unknown as typeof globalThis; \ No newline at end of file diff --git a/src/runtime/internal/index.ts b/src/runtime/internal/index.ts index e1dd2a1fcf36..6643d1b7ef5e 100644 --- a/src/runtime/internal/index.ts +++ b/src/runtime/internal/index.ts @@ -1,4 +1,3 @@ -export * from './animations'; export * from './await_block'; export * from './dom'; export * from './environment'; @@ -12,4 +11,5 @@ export * from './ssr'; export * from './transitions'; export * from './utils'; export * from './Component'; +export * from './style_manager'; export * from './dev'; diff --git a/src/runtime/internal/keyed_each.ts b/src/runtime/internal/keyed_each.ts index b397335c8766..88db365ebe8a 100644 --- a/src/runtime/internal/keyed_each.ts +++ b/src/runtime/internal/keyed_each.ts @@ -1,27 +1,18 @@ -import { transition_in, transition_out } from './transitions'; - -export function destroy_block(block, lookup) { - block.d(1); - lookup.delete(block.key); -} - -export function outro_and_destroy_block(block, lookup) { - transition_out(block, 1, 1, () => { - lookup.delete(block.key); - }); -} - -export function fix_and_destroy_block(block, lookup) { - block.f(); - destroy_block(block, lookup); -} - -export function fix_and_outro_and_destroy_block(block, lookup) { - block.f(); - outro_and_destroy_block(block, lookup); -} - -export function update_keyed_each(old_blocks, dirty, get_key, dynamic, ctx, list, lookup, node, destroy, create_each_block, next, get_context) { +import { transition_in } from './transitions'; +export const update_keyed_each = ( + old_blocks, + dirty, + ctx, + state, + get_key, + list, + lookup, + node, + create_each_block, + next, + get_context, + transition_out? +) => { let o = old_blocks.length; let n = list.length; @@ -42,11 +33,11 @@ export function update_keyed_each(old_blocks, dirty, get_key, dynamic, ctx, list if (!block) { block = create_each_block(key, child_ctx); block.c(); - } else if (dynamic) { + } else if (state & 1) { block.p(child_ctx, dirty); } - new_lookup.set(key, new_blocks[i] = block); + new_lookup.set(key, (new_blocks[i] = block)); if (key in old_indexes) deltas.set(key, Math.abs(i - old_indexes[key])); } @@ -54,13 +45,18 @@ export function update_keyed_each(old_blocks, dirty, get_key, dynamic, ctx, list const will_move = new Set(); const did_move = new Set(); - function insert(block) { + const insert = (block) => { transition_in(block, 1); block.m(node, next); lookup.set(block.key, block); next = block.first; n--; - } + }; + const destroy = (block) => { + if (state & 2) block.f(); + if (state & 4) transition_out(block, lookup.delete.bind(lookup, block.key)); + else block.d(1), lookup.delete(block.key); + }; while (o && n) { const new_block = new_blocks[n - 1]; @@ -73,25 +69,17 @@ export function update_keyed_each(old_blocks, dirty, get_key, dynamic, ctx, list next = new_block.first; o--; n--; - } - - else if (!new_lookup.has(old_key)) { + } else if (!new_lookup.has(old_key)) { // remove old block - destroy(old_block, lookup); + destroy(old_block); o--; - } - - else if (!lookup.has(new_key) || will_move.has(new_key)) { + } else if (!lookup.has(new_key) || will_move.has(new_key)) { insert(new_block); - } - - else if (did_move.has(old_key)) { + } else if (did_move.has(old_key)) { o--; - } else if (deltas.get(new_key) > deltas.get(old_key)) { did_move.add(new_key); insert(new_block); - } else { will_move.add(old_key); o--; @@ -100,14 +88,13 @@ export function update_keyed_each(old_blocks, dirty, get_key, dynamic, ctx, list while (o--) { const old_block = old_blocks[o]; - if (!new_lookup.has(old_block.key)) destroy(old_block, lookup); + if (!new_lookup.has(old_block.key)) destroy(old_block); } while (n) insert(new_blocks[n - 1]); return new_blocks; -} - +}; export function validate_each_keys(ctx, list, get_context, get_key) { const keys = new Set(); for (let i = 0; i < list.length; i++) { @@ -117,4 +104,4 @@ export function validate_each_keys(ctx, list, get_context, get_key) { } keys.add(key); } -} +} \ No newline at end of file diff --git a/src/runtime/internal/lifecycle.ts b/src/runtime/internal/lifecycle.ts index a8e37e9632a3..c9172b378384 100644 --- a/src/runtime/internal/lifecycle.ts +++ b/src/runtime/internal/lifecycle.ts @@ -2,9 +2,7 @@ import { custom_event } from './dom'; export let current_component; -export function set_current_component(component) { - current_component = component; -} +export const set_current_component = (component) => (current_component = component); export function get_current_component() { if (!current_component) throw new Error(`Function called outside component initialization`); diff --git a/src/runtime/internal/loop.ts b/src/runtime/internal/loop.ts index 33e519732fba..405f3d578a9f 100644 --- a/src/runtime/internal/loop.ts +++ b/src/runtime/internal/loop.ts @@ -1,11 +1,130 @@ -import { raf } from './environment'; +import { now, raf } from './environment'; +import { noop } from './utils'; -export interface Task { abort(): void; promise: Promise } +export const frame = { + rate: 1000 / 60, + time: 0.0, + sync() { + return n ? this.time : (this.time = now()); + }, +}; +type TaskCallback = (t: number) => boolean; +type TaskCanceller = () => void; +type TimeoutTask = { t: number; c: (now: number) => void }; -type TaskCallback = (now: number) => boolean | void; -type TaskEntry = { c: TaskCallback; f: () => void }; +const pending_sort: TimeoutTask[] = []; +const timed_tasks: TimeoutTask[] = []; -const tasks = new Set(); +let i = 0; +let j = 0; +let t = 0; +let c: TaskCallback; + +let running_frame: TaskCallback[] = []; +let next_frame: TaskCallback[] = []; + +let this_task: TimeoutTask; +let that_task: TimeoutTask; + +let l = -1; +let n = 0; +let p = 0; + +const run = (time: number) => { + time = (frame.time = now()); + if (0 !== n) { + [running_frame, next_frame] = [next_frame, running_frame]; + j = n; + for (i = n = 0; i < j; i++) { + c = running_frame[i]; + if (c(time)) { + next_frame[n++] = c; + } + } + running_frame.length = 0; + } + if (-1 !== l) { + while (-1 !== l && time >= timed_tasks[l].t) { + timed_tasks[l--].c(time); + } + if (0 !== p) { + for (i = j = 0; i < p; i++) { + this_task = pending_sort[i]; + t = this_task.t; + if (time >= t) { + this_task.c(time); + continue; + } + for (j = l++; -1 !== j; j--) { + that_task = timed_tasks[j]; + if (t <= that_task.t) break; + timed_tasks[j + 1] = that_task; + } + timed_tasks[j + 1] = this_task; + } + pending_sort.length = p = 0; + } + timed_tasks.length = l + 1; + } + if (0 !== n || -1 !== l) { + raf(run); + } +}; + +const loop = (fn) => { + let running = true; + if (0 === n) raf(run); + next_frame[n++] = (t) => running && fn(t); + return () => { + running = false; + }; +}; + +export const setFrameTimeout = (c: (t: number) => void, t: number): TaskCanceller => { + const task: TimeoutTask = { c, t }; + if (-1 !== l) { + pending_sort[p++] = task; + } else { + if (0 === n) raf(run); + timed_tasks[(l = 0)] = task; + } + return () => { + task.c = noop; + }; +}; +export const setTweenTimeout = ( + stop: (now: number) => void, + end_time: number, + run: (now: number) => void, + duration = end_time - frame.sync() +): TaskCanceller => { + let t = 0.0; + return loop((now) => { + t = 1.0 - (end_time - now) / duration; + if (t >= 1.0) return run(1), stop(now), false; + if (t >= 0.0) run(t); + return true; + }); +}; + +/** tests only */ +export const clear_loops = () => { + running_frame.length = 0; + pending_sort.length = 0; + timed_tasks.length = 0; + next_frame.length = 0; + i = j = t = n = p = 0; + l = -1; + tasks.clear(); +}; + +/** legacy loop for svelte/motion */ + +export interface MotionTask { abort(): void; promise: Promise } +type MotionTaskCallback = (now: number) => boolean | void; +type MotionTaskEntry = { c: MotionTaskCallback; f: () => void }; + +const tasks = new Set(); function run_tasks(now: number) { tasks.forEach(task => { @@ -17,20 +136,8 @@ function run_tasks(now: number) { if (tasks.size !== 0) raf(run_tasks); } - -/** - * For testing purposes only! - */ -export function clear_loops() { - tasks.clear(); -} - -/** - * Creates a new task that runs on each raf frame - * until it returns a falsy value or is aborted - */ -export function loop(callback: TaskCallback): Task { - let task: TaskEntry; +export function motion_loop(callback: MotionTaskCallback): MotionTask { + let task: MotionTaskEntry; if (tasks.size === 0) raf(run_tasks); @@ -42,4 +149,4 @@ export function loop(callback: TaskCallback): Task { tasks.delete(task); } }; -} +} \ No newline at end of file diff --git a/src/runtime/internal/scheduler.ts b/src/runtime/internal/scheduler.ts index b0db71035a98..b7433cb31199 100644 --- a/src/runtime/internal/scheduler.ts +++ b/src/runtime/internal/scheduler.ts @@ -1,89 +1,117 @@ -import { run_all } from './utils'; import { set_current_component } from './lifecycle'; +import { T$$ } from './Component'; +import { frame } from './loop'; -export const dirty_components = []; -export const intros = { enabled: false }; +const resolved_promise = Promise.resolve(); + +let update_scheduled = false; +let is_flushing = false; +const dirty_components = []; + +// todo : remove binding_callbacks export export const binding_callbacks = []; const render_callbacks = []; +const measure_callbacks = []; const flush_callbacks = []; -const resolved_promise = Promise.resolve(); -let update_scheduled = false; - -export function schedule_update() { +// todo : remove add_flush_callback +export const add_flush_callback = /*#__PURE__*/ Array.prototype.push.bind(flush_callbacks); +type MeasureCallback = () => FlushCallback +type FlushCallback = (current_frame_time: number) => void +export const add_measure_callback: (...args: MeasureCallback[]) => number = /*#__PURE__*/ Array.prototype.push.bind(measure_callbacks); + +const seen_render_callbacks = new Set(); +export const add_render_callback = (fn) => { + if (!seen_render_callbacks.has(fn)) { + seen_render_callbacks.add(fn); + render_callbacks.push(fn); + } +}; +export const schedule_update = (component) => { + dirty_components.push(component); if (!update_scheduled) { update_scheduled = true; - resolved_promise.then(flush); + if (!is_flushing) { + resolved_promise.then(flush); + } + } +}; +export const tick = () => { + if (!update_scheduled) { + update_scheduled = true; + if (!is_flushing) { + resolved_promise.then(flush); + } } -} - -export function tick() { - schedule_update(); return resolved_promise; -} - -export function add_render_callback(fn) { - render_callbacks.push(fn); -} +}; +export const flush = () => { + if (is_flushing) return; + else is_flushing = true; -export function add_flush_callback(fn) { - flush_callbacks.push(fn); -} + frame.sync(); -let flushing = false; -const seen_callbacks = new Set(); -export function flush() { - if (flushing) return; - flushing = true; + let i = 0; + let j = 0; + let $$: T$$; + let dirty; + let before_update; + let after_update; do { - // first, call beforeUpdate functions - // and update components - for (let i = 0; i < dirty_components.length; i += 1) { - const component = dirty_components[i]; - set_current_component(component); - update(component.$$); - } + for (;i < dirty_components.length;i++) { + ({ $$ } = set_current_component(dirty_components[i])); - dirty_components.length = 0; + // todo : is this check still necessary ? + if (null === $$.fragment) continue; - while (binding_callbacks.length) binding_callbacks.pop()(); + /* run reactive statements */ + $$.update(); - // then, once components are updated, call - // afterUpdate functions. This may cause - // subsequent updates... - for (let i = 0; i < render_callbacks.length; i += 1) { - const callback = render_callbacks[i]; + /* run beforeUpdate */ + for (j = 0, { before_update } = $$; j < before_update.length; j++) { + before_update[j](); + } - if (!seen_callbacks.has(callback)) { - // ...so guard against infinite loops - seen_callbacks.add(callback); + /* update blocks */ + ({ dirty } = $$).dirty = [-1]; + if (false !== $$.fragment) $$.fragment.p($$.ctx, dirty); - callback(); + /* schedule afterUpdate */ + for (j = 0, { after_update } = $$; j < after_update.length; j++) { + add_render_callback(after_update[j]); } } + dirty_components.length = 0; + + // update bindings [ ...in reverse order (#3145) ] + i = binding_callbacks.length; + while (i--) binding_callbacks[i](); + binding_callbacks.length = i = 0; - render_callbacks.length = 0; + // run afterUpdates + // todo : remove every non afterUpdate callback from render_callbacks + for (; i < render_callbacks.length; i++) render_callbacks[i](); + render_callbacks.length = i = 0; } while (dirty_components.length); + seen_render_callbacks.clear(); + update_scheduled = false; - while (flush_callbacks.length) { - flush_callbacks.pop()(); + // measurement callbacks for animations + for (i = 0, j = flush_callbacks.length; i < measure_callbacks.length; i++) { + flush_callbacks[j++] = measure_callbacks[i](); } + measure_callbacks.length = i = 0; - update_scheduled = false; - flushing = false; - seen_callbacks.clear(); -} - -function update($$) { - if ($$.fragment !== null) { - $$.update(); - run_all($$.before_update); - const dirty = $$.dirty; - $$.dirty = [-1]; - $$.fragment && $$.fragment.p($$.ctx, dirty); - - $$.after_update.forEach(add_render_callback); + // apply styles + // todo : remove every non style callback from flush_callbacks + for (const t = frame.time; i < j; i++) flush_callbacks[i](t); + flush_callbacks.length = i = j = 0; + + is_flushing = false; + if (update_scheduled) { + // reflush if applying animations triggered an update + flush(); } -} +}; diff --git a/src/runtime/internal/style_manager.ts b/src/runtime/internal/style_manager.ts index 31d7573a769e..571bf4c587c6 100644 --- a/src/runtime/internal/style_manager.ts +++ b/src/runtime/internal/style_manager.ts @@ -1,74 +1,80 @@ -import { element } from './dom'; -import { raf } from './environment'; +import { frame } from './loop'; +import { methodify, noop } from './utils'; -interface ExtendedDoc extends Document { - __svelte_stylesheet: CSSStyleSheet; - __svelte_rules: Record; -} +let documents_uid = 0; +let running_animations = 0; -const active_docs = new Set(); -let active = 0; +const document_uid = new Map(); +const document_stylesheets = new Map(); -// https://github.com/darkskyapp/string-hash/blob/master/index.js -function hash(str: string) { - let hash = 5381; - let i = str.length; +const current_rules = new Set(); +export const animate_css = /*#__PURE__*/ methodify( + function (this: HTMLElement, css: (t: number) => string, duration: number, delay = 0) { + if (!document_uid.has(this.ownerDocument)) { + document_uid.set(this.ownerDocument, documents_uid++); + document_stylesheets.set( + this.ownerDocument, + this.ownerDocument.head.appendChild(this.ownerDocument.createElement('style')).sheet + ); + } + let rule = '{\n'; + for (let t = 0, step = frame.rate / Math.max(frame.rate, duration); t < 1; t += step) rule += `${100 * t}%{${css(t)}}\n`; + rule += `100% {${css(1)}}\n}`; - while (i--) hash = ((hash << 5) - hash) ^ str.charCodeAt(i); - return hash >>> 0; -} + // darkskyapp/string-hash + let i = rule.length; + let hash = 5381; + while (i--) hash = ((hash << 5) - hash) ^ rule.charCodeAt(i); + const name = `__svelte_${hash >>> 0}${document_uid.get(this.ownerDocument)}`; -export function create_rule(node: Element & ElementCSSInlineStyle, a: number, b: number, duration: number, delay: number, ease: (t: number) => number, fn: (t: number, u: number) => string, uid: number = 0) { - const step = 16.666 / duration; - let keyframes = '{\n'; + if (!current_rules.has(name)) { + current_rules.add(name); + const stylesheet = document_stylesheets.get(this.ownerDocument); + stylesheet.insertRule(`@keyframes ${name} ${rule}`, stylesheet.cssRules.length); + } - for (let p = 0; p <= 1; p += step) { - const t = a + (b - a) * ease(p); - keyframes += p * 100 + `%{${fn(t, 1 - t)}}\n`; - } + const previous = this.style.animation; + this.style.animation = `${ + previous ? `${previous}, ` : '' + }${duration}ms linear ${delay}ms 1 normal both running ${name}`; - const rule = keyframes + `100% {${fn(b, 1 - b)}}\n}`; - const name = `__svelte_${hash(rule)}_${uid}`; - const doc = node.ownerDocument as ExtendedDoc; - active_docs.add(doc); - const stylesheet = doc.__svelte_stylesheet || (doc.__svelte_stylesheet = doc.head.appendChild(element('style') as HTMLStyleElement).sheet as CSSStyleSheet); - const current_rules = doc.__svelte_rules || (doc.__svelte_rules = {}); + running_animations++; - if (!current_rules[name]) { - current_rules[name] = true; - stylesheet.insertRule(`@keyframes ${name} ${rule}`, stylesheet.cssRules.length); + return () => { + const prev = (this.style.animation || '').split(', '); + const next = prev.filter((anim) => !anim.includes(name)); + if (prev.length !== next.length) this.style.animation = next.join(', '); + if (--running_animations === 0) { + document_stylesheets.forEach((stylesheet) => { + let i = stylesheet.cssRules.length; + while (i--) stylesheet.deleteRule(i); + }); + current_rules.clear(); + if (1 !== documents_uid) { + document_stylesheets.clear(); + document_uid.clear(); + documents_uid = 0; + } + } + }; } - - const animation = node.style.animation || ''; - node.style.animation = `${animation ? `${animation}, ` : ``}${name} ${duration}ms linear ${delay}ms 1 both`; - - active += 1; - return name; -} - -export function delete_rule(node: Element & ElementCSSInlineStyle, name?: string) { - const previous = (node.style.animation || '').split(', '); - const next = previous.filter(name - ? anim => anim.indexOf(name) < 0 // remove specific animation - : anim => anim.indexOf('__svelte') === -1 // remove all Svelte animations - ); - const deleted = previous.length - next.length; - if (deleted) { - node.style.animation = next.join(', '); - active -= deleted; - if (!active) clear_rules(); +); +export const fix_position = /*#__PURE__*/ methodify( + function (this: HTMLElement, { left, top }: DOMRect | ClientRect) { + const { position, width, height, transform } = getComputedStyle(this); + if (position === 'absolute' || position === 'fixed') return noop; + const { position: og_position, width: og_width, height: og_height, transform: og_transform } = this.style; + this.style.position = 'absolute'; + this.style.width = width; + this.style.height = height; + const b = this.getBoundingClientRect(); + this.style.transform = `${transform === 'none' ? '' : transform} translate(${left - b.left}px, ${top - b.top}px)`; + return () => { + // unsafe + this.style.position = og_position; + this.style.width = og_width; + this.style.height = og_height; + this.style.transform = og_transform; + }; } -} - -export function clear_rules() { - raf(() => { - if (active) return; - active_docs.forEach(doc => { - const stylesheet = doc.__svelte_stylesheet; - let i = stylesheet.cssRules.length; - while (i--) stylesheet.deleteRule(i); - doc.__svelte_rules = {}; - }); - active_docs.clear(); - }); -} +); \ No newline at end of file diff --git a/src/runtime/internal/transitions.ts b/src/runtime/internal/transitions.ts index ed23d3c1dd1e..794184a8086e 100644 --- a/src/runtime/internal/transitions.ts +++ b/src/runtime/internal/transitions.ts @@ -1,353 +1,343 @@ -import { identity as linear, is_function, noop, run_all } from './utils'; -import { now } from "./environment"; -import { loop } from './loop'; -import { create_rule, delete_rule } from './style_manager'; -import { custom_event } from './dom'; -import { add_render_callback } from './scheduler'; -import { TransitionConfig } from '../transition'; - -let promise: Promise|null; - -function wait() { - if (!promise) { - promise = Promise.resolve(); - promise.then(() => { - promise = null; - }); - } - - return promise; +import { Fragment } from "./Component"; +import { custom_event } from "./dom"; +import { setFrameTimeout, setTweenTimeout, frame } from "./loop"; +import { add_measure_callback, tick } from "./scheduler"; +import { animate_css } from "./style_manager"; +import { methodify, noop } from "./utils"; + + +export interface CssAnimationConfig { + delay?: number; + duration?: number; + easing?: (t: number) => number; } - -function dispatch(node: Element, direction: boolean, kind: 'start' | 'end') { - node.dispatchEvent(custom_event(`${direction ? 'intro' : 'outro'}${kind}`)); -} - -const outroing = new Set(); -let outros; - -export function group_outros() { - outros = { - r: 0, // remaining outros - c: [], // callbacks - p: outros // parent group - }; +export interface CssTransitionConfig extends CssAnimationConfig { + css?: (t: number, u?: number) => string; + tick?: (t: number, u?: number) => void; + strategy?: EasingStrategy; } - -export function check_outros() { - if (!outros.r) { - run_all(outros.c); - } - outros = outros.p; +export type TimeableConfig = Omit & { duration?: number | ((len: number) => number) }; +const enum EasingStrategy { + balanced = "balanced", + reversed = "reversed", + mirrored = "mirrored", } - -export function transition_in(block, local?: 0 | 1) { - if (block && block.i) { - outroing.delete(block); - block.i(local); - } +const enum TransitionEvent { + introstart = "introstart", + introend = "introend", + outrostart = "outrostart", + outroend = "outroend" } - -export function transition_out(block, local: 0 | 1, detach: 0 | 1, callback) { - if (block && block.o) { - if (outroing.has(block)) return; +export const transition_in = (block: Fragment, local?) => { + // todo : is `!block` necessary ? + if (!block || !block.i) return; + outroing.delete(block); + block.i(local); +}; + +export const transition_out = (block: Fragment, local?) => { + // todo : are `!block` and `outroing.has` checks necessary ? + if (!block || !block.o || outroing.has(block)) return; + outroing.add(block); + block.o(local); +}; +type TransitionGroup = { + /* parent group */ p: TransitionGroup; + /* callbacks */ c: Array<(cancelled: boolean) => void>; + /* running outros */ r: number; + /* stop callbacks */ s: Array<(t: number) => void>; + /* outro timeout */ t: number; +}; +let transition_group: TransitionGroup; +const outroing = new Set(); +export const group_transition_out = (fn) => { + const c = []; + const current_group = (transition_group = { + p: transition_group, + c, + r: 0, + s: [], + t: 0, + }); + fn((block, callback, detach = true) => { + if (!block || !block.o || outroing.has(block)) return; outroing.add(block); - - outros.c.push(() => { - outroing.delete(block); - if (callback) { + c.push((cancelled = false) => { + if (cancelled) { + // block destroyed before outro ended + outroing.delete(block); + } else if (outroing.has(block)) { + outroing.delete(block); if (detach) block.d(1); callback(); } }); - - block.o(local); - } -} - -const null_transition: TransitionConfig = { duration: 0 }; - -type TransitionFn = (node: Element, params: any) => TransitionConfig; - -export function create_in_transition(node: Element & ElementCSSInlineStyle, fn: TransitionFn, params: any) { - let config = fn(node, params); - let running = false; - let animation_name; - let task; - let uid = 0; - - function cleanup() { - if (animation_name) delete_rule(node, animation_name); - } - - 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; - - delete_rule(node); - - if (is_function(config)) { - config = config(); - wait().then(go); - } else { - go(); - } - }, - - invalidate() { - started = false; - }, - - end() { - if (running) { - cleanup(); - running = false; - } - } + block.o(1); + }); + if (!current_group.r) for (let i = 0; i < c.length; i++) c[i](); + transition_group = transition_group.p; +}; +type Rect = DOMRect | ClientRect; +type MeasureCallback = (is_intro?: boolean) => CssTransitionConfig; +type CustomTransitionFunction = (node: HTMLElement, params: any) => MeasureCallback | CssTransitionConfig; +type AnimationFn = (node: Element, { from, to }: { from: Rect; to: Rect }, params: any) => CssTransitionConfig; +type StopResetReverseFn = (t?: number | 1 | -1) => StopResetReverseFn | void; + +const swap = (fn, is_intro) => + fn.length === 1 + ? is_intro + ? fn + : (t) => fn(1 - t) + : is_intro + ? (t) => fn(t, 1 - t) + : (t) => fn(1 - t, t); + +const mirrored = (fn, is_intro, easing, _start?, _end?) => { + const run = swap(fn, is_intro); + return easing + ? is_intro + ? (t) => run(easing(t)) + : (t) => run(1 - easing(1 - t)) + : run; +}; + +const reversed = (fn, is_intro, easing, start = 0, end = 1) => { + const run = swap(fn, is_intro); + const difference = end - start; + return easing + ? (t) => run(start + difference * easing(t)) + : (t) => run(start + difference * t); +}; + +const balanced = (fn, is_intro, easing, start = 0, end = 1) => { + const run = swap(fn, is_intro); + const difference = end - start; + return easing + ? (t) => run(start + difference * easing(t)) + : (t) => run(start + difference * t); +}; + +export const run_animation = /*#__PURE__*/ methodify(function (this: HTMLElement, from: Rect, fn: AnimationFn, params: CssTransitionConfig = {}) { + let running = true; + let cancel_css; + let cancel_raf; + add_measure_callback(() => { + const to = this.getBoundingClientRect(); + if (from.top === to.top && from.left === to.left && from.right === to.right && from.bottom === to.bottom) return noop; + const config = fn(this, { from, to }, params); + return (current_frame_time) => { + if (false === running) return; + const { delay = 0, duration = 300, easing, tick, css }: CssTransitionConfig = config; + const end_time = current_frame_time + delay + duration; + const runner = (fn) => reversed(fn, true, easing); + if (css) cancel_css = animate_css(this, runner(css), duration, delay); + if (tick) tick(0, 1); + cancel_raf = tick ? setTweenTimeout(stop, end_time, runner(tick), duration) : setFrameTimeout(stop, end_time); + }; + }); + const stop = () => { + if (false === running) return; + else running = false; + if (cancel_css) cancel_css(); + if (cancel_raf) cancel_raf(); }; -} - -export function create_out_transition(node: Element & ElementCSSInlineStyle, fn: TransitionFn, params: any) { - let config = fn(node, params); + return stop; +}); +export const run_in = /*#__PURE__*/ methodify(function (this: HTMLElement, fn: CustomTransitionFunction, params: CssTransitionConfig = {}) { + let config; let running = true; - let animation_name; - - const group = outros; - - group.r += 1; - - 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); + let cancel_css; + let cancel_raf; + let end_time; + add_measure_callback(() => { + config = fn(this, params); + return (current_frame_time) => { + const { delay = 0, duration = 300, easing, tick, css }: CssTransitionConfig = + "function" === typeof config ? (config = config()) : config; + const runner = (fn) => balanced(fn, true, easing); + end_time = current_frame_time + delay + duration; + this.dispatchEvent(custom_event(TransitionEvent.introstart)); + if (css) cancel_css = animate_css(this, runner(css), duration, delay); + cancel_raf = tick ? setTweenTimeout(stop, end_time, runner(tick), duration ) : setFrameTimeout(stop, end_time); + }; + }); + const stop = (t?: number) => { + if (false === running) return; + else running = false; + if (cancel_css) cancel_css(); + if (cancel_raf) cancel_raf(); + if (t && t >= end_time) this.dispatchEvent(custom_event(TransitionEvent.introend)); + }; + return stop; +}); +export const run_out = /*#__PURE__*/ methodify(function (this: HTMLElement, fn: CustomTransitionFunction, params: CssTransitionConfig = {}) { + let config; + let running = true; + let cancel_css; + let cancel_raf; + let end_time; + const current_group = transition_group; + current_group.r++; + add_measure_callback(() => { + config = fn(this, params); + return (current_frame_time) => { + const { delay = 0, duration = 300, easing, tick, css }: CssTransitionConfig = + "function" === typeof config ? (config = config()) : config; + const runner = (fn) => balanced(fn, false, easing); + end_time = current_frame_time + delay + duration; + current_group.t = Math.max(end_time, current_group.t); + if (current_group.s.push(stop) === current_group.r) { + setFrameTimeout((t) => { + for (let i = 0; i < current_group.s.length; i++) { + current_group.s[i](t); } - - 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(); - 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; + }, current_group.t); } + this.dispatchEvent(custom_event(TransitionEvent.outrostart)); + if (css) cancel_css = animate_css(this, runner(css), duration, delay); + if (tick) cancel_raf = setTweenTimeout(noop, end_time, runner(tick), duration); + }; + }); + const stop = (t?: number) => { + if (1 === t && "tick" in config) config.tick(1, 0); + if (false === running) return; + else running = false; + if (cancel_css) cancel_css(); + if (cancel_raf) cancel_raf(); + if (t && t >= end_time) { + if ("tick" in config) config.tick(0, 1); + this.dispatchEvent(custom_event(TransitionEvent.outroend)); } + if (!--current_group.r) for (let i = 0, { c } = current_group, r = t === void 0;i < c.length;i++) c[i](r); }; -} - -export function create_bidirectional_transition(node: Element & ElementCSSInlineStyle, fn: TransitionFn, params: any, intro: boolean) { - let config = fn(node, params); - - let t = intro ? 0 : 1; - - let running_program = null; - let pending_program = null; - let animation_name = null; - - function clear_animation() { - if (animation_name) delete_rule(node, animation_name); - } - - 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 - }; - } - - function go(b) { - const { - delay = 0, - duration = 300, - easing = linear, - tick = noop, - css - } = config || null_transition; - - const program = { - start: now() + delay, - b + return stop; +}); +export const create_bidirectional_transition = /*#__PURE__*/ methodify(function(this: HTMLElement, fn: CustomTransitionFunction, params?: CssTransitionConfig) { + let transition_delay = 0.0; + let pending = 0; + let prev; + + const u = (new_fn = fn, new_params = params) => { + let test_config; + if (typeof (test_config = (fn = new_fn)(this,(params = new_params))) === "function") test_config = test_config(); + transition_delay = test_config.delay || 0.0; + }; + u(); + + const run_transition = (is_intro: boolean, cancel_previous?) => { + const delayed_start = transition_delay && cancel_previous && pending; + + let config; + + let running = true; + let cancelled = false; + + let cancel_css; + let cancel_raf; + + let start_time = 0.0; + let end_time = 0.0; + let ratio_left = 0.0; + + const current_group = transition_group; + if (!is_intro) current_group.r++; + + const run = (flush_frame_time) => { + pending++; + const [prev_duration_left, prev_ratio_left] = ((cancel_previous && cancel_previous(flush_frame_time)) || [0.0, 0.0] ); + ratio_left = prev_ratio_left; + return () => { + config = fn(this, params); + return (current_frame_time) => { + let { tick, css, duration = 300.0, delay = 0.0, easing, strategy = EasingStrategy.balanced }: CssTransitionConfig = + "function" === typeof config ? (config = config(is_intro)) : config; + const solver = EasingStrategy.balanced === strategy ? balanced : EasingStrategy.reversed === strategy ? reversed : mirrored; + const runner = (fn) => solver(fn, is_intro, easing, ratio_left, 1); + if (delayed_start) delay = 0; + if (solver === reversed) duration -= prev_duration_left; + else if (solver === balanced) duration *= 1 - ratio_left; + else if (solver === mirrored) delay -= prev_duration_left; + start_time = current_frame_time + delay; + end_time = start_time + duration; + if (cancelled) return; + this.dispatchEvent(custom_event(is_intro ? TransitionEvent.introstart : TransitionEvent.outrostart)); + if (css) cancel_css = animate_css(this, runner(css), duration, delay); + if (tick) cancel_raf = setTweenTimeout(is_intro ? stop : noop, end_time, runner(tick), duration); + else if (is_intro) cancel_raf = setFrameTimeout(stop, end_time); + if (!is_intro) { + current_group.t = Math.max(end_time, current_group.t); + if (current_group.s.push(stop) === current_group.r) { + setFrameTimeout((t) => { + for (let i = 0; i < current_group.s.length; i++) { + current_group.s[i](t); + } + }, current_group.t); + } + } + setFrameTimeout(() => { + if (!cancelled || !pending) { + if (!is_intro && tick) tick(0, 1); + this.dispatchEvent(custom_event(is_intro ? TransitionEvent.introend : TransitionEvent.outroend)); + } + }, end_time); + }; + }; }; - if (!b) { - // @ts-ignore todo: improve typings - program.group = outros; - outros.r += 1; - } - - if (running_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); + const cancel = (t) => { + if (!cancelled) { + pending--; + cancelled = true; + if (cancel_css) cancel_css(); + if (cancel_raf) cancel_raf(); } + if (!config || 1 === t) return; + const duration_left = end_time - t; + const next_ratio_left = 1 - duration_left / (end_time - start_time); + return duration_left > 0 && next_ratio_left > 0 && [duration_left, (1 - ratio_left) * (1 - (config.easing || ((v) => v))(next_ratio_left))]; + }; - 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); + const stop: StopResetReverseFn = (t?: number | -1 | 1) => { + if (running) { + running = false; + if (config) { + if (t && t >= end_time) { + if (pending === 1) cancel(1); } - } - - 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); - } + if (!is_intro) { + if (!--current_group.r) { + for (let i = 0, { c } = current_group, r = Math.abs(t) === 1; i < c.length; i++) c[i](r); } - - 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); } } + } + if (t === -1) return run_transition(!is_intro, cancel); + }; - return !!(running_program || pending_program); - }); + if (delayed_start) { + setFrameTimeout((t) => { + add_measure_callback(run(t)); + tick(); + }, frame.time + transition_delay); + } else { + add_measure_callback(run(frame.time)); } - } + + return stop; + }; return { - run(b) { - if (is_function(config)) { - wait().then(() => { - // @ts-ignore - config = config(); - go(b); - }); - } else { - go(b); - } + u, + o() { + prev = prev ? prev(-1) : run_transition(false); }, - - end() { - clear_animation(); - running_program = pending_program = null; + i() { + prev = prev ? prev(-1) : run_transition(true); + }, + d() { + if (prev) prev(1); } }; -} +}); +export const run_duration = (duration, value1, value2?): number => + typeof duration === "function" ? duration(value1, value2) : duration; diff --git a/src/runtime/internal/utils.ts b/src/runtime/internal/utils.ts index d752c9de9d83..0cffedfbf571 100644 --- a/src/runtime/internal/utils.ts +++ b/src/runtime/internal/utils.ts @@ -1,5 +1,4 @@ export function noop() {} - export const identity = x => x; export function assign(tar: T, src: S): T & S { @@ -146,4 +145,9 @@ export const has_prop = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, export function action_destroyer(action_result) { return action_result && is_function(action_result.destroy) ? action_result.destroy : noop; -} \ No newline at end of file +} +type Methodify any> = (thisType: ThisParameterType, ...parameters: Parameters) => T extends (...args: Parameters) => infer R ? R : any +export const methodify: any>(fn: T) => Methodify = /*#__PURE__*/ (function () { + const call = Function.prototype.call; + return call.bind.bind(call); +})(); \ No newline at end of file diff --git a/src/runtime/motion/spring.ts b/src/runtime/motion/spring.ts index 5845a13a577f..d78926aadb1a 100644 --- a/src/runtime/motion/spring.ts +++ b/src/runtime/motion/spring.ts @@ -1,5 +1,5 @@ import { Readable, writable } from 'svelte/store'; -import { loop, now, Task } from 'svelte/internal'; +import { motion_loop as loop, now, MotionTask as Task } from 'svelte/internal'; import { is_date } from './utils'; interface TickContext { diff --git a/src/runtime/motion/tweened.ts b/src/runtime/motion/tweened.ts index c802604c0e27..0c4cf55d8178 100644 --- a/src/runtime/motion/tweened.ts +++ b/src/runtime/motion/tweened.ts @@ -1,5 +1,5 @@ import { Readable, writable } from 'svelte/store'; -import { assign, loop, now, Task } from 'svelte/internal'; +import { assign, motion_loop as loop, now, MotionTask as Task } from 'svelte/internal'; import { linear } from 'svelte/easing'; import { is_date } from './utils'; diff --git a/src/runtime/transition/index.ts b/src/runtime/transition/index.ts index 0a20c81b1f83..39a1a2af86a1 100644 --- a/src/runtime/transition/index.ts +++ b/src/runtime/transition/index.ts @@ -1,110 +1,47 @@ -import { cubicOut, cubicInOut, linear } from 'svelte/easing'; -import { assign, is_function } from 'svelte/internal'; - -type EasingFunction = (t: number) => number; - -export interface TransitionConfig { - delay?: number; - duration?: number; - easing?: EasingFunction; - css?: (t: number, u: number) => string; - tick?: (t: number, u: number) => void; -} - -interface BlurParams { - delay: number; - duration: number; - easing?: EasingFunction; - amount: number; - opacity: number; -} - -export function blur(node: Element, { - delay = 0, - duration = 400, - easing = cubicInOut, - amount = 5, - opacity = 0 -}: BlurParams): TransitionConfig { +import { cubicOut, cubicInOut } from 'svelte/easing'; +import { run_duration, CssAnimationConfig, CssTransitionConfig, TimeableConfig } from 'svelte/internal'; + +type FlyParams = FadingConfig & { x: number; y: number; rotate: number }; +type BlurParams = FadingConfig & { amount: number }; +type ScaleParams = FadingConfig & { start: number }; +type DrawParams = CssAnimationConfig & { speed: number }; +type FadingConfig = CssAnimationConfig & { opacity: number }; +type MarkedCrossFadeConfig = TimeableConfig & { key: any }; +type CrossFadeConfig = TimeableConfig & { fallback(node: Element, params: TimeableConfig, intro: boolean): CssTransitionConfig }; +type ElementMap = Map; + +export function blur(node: Element, { delay = 0, duration = 400, easing = cubicInOut, amount = 5, opacity = 0 }: BlurParams): CssTransitionConfig { const style = getComputedStyle(node); const target_opacity = +style.opacity; const f = style.filter === 'none' ? '' : style.filter; - const od = target_opacity * (1 - opacity); - return { delay, duration, easing, - css: (_t, u) => `opacity: ${target_opacity - (od * u)}; filter: ${f} blur(${u * amount}px);` + css: (_t, u) => `opacity: ${target_opacity - od * u}; filter:${f} blur(${u * amount}px);`, }; } -interface FadeParams { - delay: number; - duration: number; - easing: EasingFunction; -} - -export function fade(node: Element, { - delay = 0, - duration = 400, - easing = linear -}: FadeParams): TransitionConfig { +export function fade(node: Element, { delay = 0, duration = 400, easing }: CssAnimationConfig): CssTransitionConfig { const o = +getComputedStyle(node).opacity; - - return { - delay, - duration, - easing, - css: t => `opacity: ${t * o}` - }; + return { delay, duration, easing, css: (t) => `opacity: ${t * o};` }; } -interface FlyParams { - delay: number; - duration: number; - easing: EasingFunction; - x: number; - y: number; - opacity: number; -} - -export function fly(node: Element, { - delay = 0, - duration = 400, - easing = cubicOut, - x = 0, - y = 0, - opacity = 0 -}: FlyParams): TransitionConfig { +export function fly(node: Element, { delay = 0, duration = 400, easing = cubicOut, x = 0, y = 0, opacity = 0, rotate = 0 }: FlyParams ): CssTransitionConfig { const style = getComputedStyle(node); const target_opacity = +style.opacity; - const transform = style.transform === 'none' ? '' : style.transform; - + const prev = style.transform === 'none' ? '' : style.transform; const od = target_opacity * (1 - opacity); - return { delay, duration, easing, - css: (t, u) => ` - transform: ${transform} translate(${(1 - t) * x}px, ${(1 - t) * y}px); - opacity: ${target_opacity - (od * u)}` + css: (_t, u) => `transform: ${prev} translate(${u * x}px, ${u * y}px) rotate(${u * rotate}deg); opacity: ${target_opacity - od * u};`, }; } -interface SlideParams { - delay: number; - duration: number; - easing: EasingFunction; -} - -export function slide(node: Element, { - delay = 0, - duration = 400, - easing = cubicOut -}: SlideParams): TransitionConfig { +export function slide(node: Element, { delay = 0, duration = 400, easing = cubicOut }: CssAnimationConfig): CssTransitionConfig { const style = getComputedStyle(node); const opacity = +style.opacity; const height = parseFloat(style.height); @@ -114,159 +51,91 @@ export function slide(node: Element, { const margin_bottom = parseFloat(style.marginBottom); const border_top_width = parseFloat(style.borderTopWidth); const border_bottom_width = parseFloat(style.borderBottomWidth); - return { delay, duration, easing, - css: t => - `overflow: hidden;` + - `opacity: ${Math.min(t * 20, 1) * opacity};` + - `height: ${t * height}px;` + - `padding-top: ${t * padding_top}px;` + - `padding-bottom: ${t * padding_bottom}px;` + - `margin-top: ${t * margin_top}px;` + - `margin-bottom: ${t * margin_bottom}px;` + - `border-top-width: ${t * border_top_width}px;` + - `border-bottom-width: ${t * border_bottom_width}px;` + css: (t) => ` + overflow: hidden; + opacity: ${Math.min(t * 20, 1) * opacity}; + height: ${t * height}px; + padding-top: ${t * padding_top}px; + padding-bottom: ${t * padding_bottom}px; + margin-top: ${t * margin_top}px; + margin-bottom: ${t * margin_bottom}px; + border-top-width: ${t * border_top_width}px; + border-bottom-width: ${t * border_bottom_width}px;`, }; } -interface ScaleParams { - delay: number; - duration: number; - easing: EasingFunction; - start: number; - opacity: number; -} - -export function scale(node: Element, { - delay = 0, - duration = 400, - easing = cubicOut, - start = 0, - opacity = 0 -}: ScaleParams): TransitionConfig { +export function scale(node: Element, { delay = 0, duration = 400, easing = cubicOut, start = 0, opacity = 0 }: ScaleParams): CssTransitionConfig { const style = getComputedStyle(node); const target_opacity = +style.opacity; const transform = style.transform === 'none' ? '' : style.transform; - const sd = 1 - start; const od = target_opacity * (1 - opacity); - return { delay, duration, easing, - css: (_t, u) => ` - transform: ${transform} scale(${1 - (sd * u)}); - opacity: ${target_opacity - (od * u)} - ` + css: (_t, u) => `transform: ${transform} scale(${1 - sd * u}); opacity: ${target_opacity - od * u};`, }; } -interface DrawParams { - delay: number; - speed: number; - duration: number | ((len: number) => number); - easing: EasingFunction; -} -export function draw(node: SVGElement & { getTotalLength(): number }, { - delay = 0, - speed, - duration, - easing = cubicInOut -}: DrawParams): TransitionConfig { +export function draw(node: SVGPathElement | SVGGeometryElement, { delay = 0, speed, duration, easing = cubicInOut }: DrawParams): CssTransitionConfig { const len = node.getTotalLength(); - - if (duration === undefined) { - if (speed === undefined) { - duration = 800; - } else { - duration = len / speed; - } - } else if (typeof duration === 'function') { - duration = duration(len); - } - - return { - delay, - duration, - easing, - css: (t, u) => `stroke-dasharray: ${t * len} ${u * len}` - }; -} - -interface CrossfadeParams { - delay: number; - duration: number | ((len: number) => number); - easing: EasingFunction; + if (duration === undefined) duration = speed ? len / speed : 800; + else duration = run_duration(duration, len); + return { delay, duration, easing, css: (t, u) => `stroke-dasharray: ${t * len} ${u * len};` }; } -type ClientRectMap = Map; - -export function crossfade({ fallback, ...defaults }: CrossfadeParams & { - fallback: (node: Element, params: CrossfadeParams, intro: boolean) => TransitionConfig; -}) { - const to_receive: ClientRectMap = new Map(); - const to_send: ClientRectMap = new Map(); +export function crossfade({ delay: default_delay = 0, duration: default_duration = (d) => Math.sqrt(d) * 30, easing: default_easing = cubicOut, fallback }: CrossFadeConfig) { + const a: ElementMap = new Map(); + const b: ElementMap = new Map(); - function crossfade(from: ClientRect, node: Element, params: CrossfadeParams): TransitionConfig { - const { - delay = 0, - duration = d => Math.sqrt(d) * 30, - easing = cubicOut - } = assign(assign({}, defaults), params); - - const to = node.getBoundingClientRect(); + const crossfade = (from_node: Element, to_node: Element, { delay = default_delay, easing = default_easing, duration = default_duration }: TimeableConfig ) => { + const from = from_node.getBoundingClientRect(); + const to = to_node.getBoundingClientRect(); const dx = from.left - to.left; const dy = from.top - to.top; const dw = from.width / to.width; const dh = from.height / to.height; - const d = Math.sqrt(dx * dx + dy * dy); - - const style = getComputedStyle(node); - const transform = style.transform === 'none' ? '' : style.transform; - const opacity = +style.opacity; - + const { transform, opacity } = getComputedStyle(to_node); + const op = +opacity; + const prev = transform === 'none' ? '' : transform; return { delay, - duration: is_function(duration) ? duration(d) : duration, easing, + duration: run_duration(duration, Math.sqrt(dx * dx + dy * dy)), css: (t, u) => ` - opacity: ${t * opacity}; + opacity: ${t * op}; transform-origin: top left; - transform: ${transform} translate(${u * dx}px,${u * dy}px) scale(${t + (1-t) * dw}, ${t + (1-t) * dh}); - ` - }; - } - - function transition(items: ClientRectMap, counterparts: ClientRectMap, intro: boolean) { - return (node: Element, params: CrossfadeParams & { key: any }) => { - items.set(params.key, { - rect: node.getBoundingClientRect() - }); - + transform: ${prev} translate(${u * dx}px,${u * dy}px) scale(${t + (1 - t) * dw}, ${t + (1 - t) * dh}); + `, + } as CssTransitionConfig; + }; + + const transition = (a: ElementMap, b: ElementMap, is_intro: boolean) => ( to_node: Element, params: MarkedCrossFadeConfig ) => { + const { key } = params; + a.set(key, to_node); + if (b.has(key)) { + const from_node = b.get(key); + b.delete(key); + return crossfade(from_node, to_node, params); + } else { return () => { - if (counterparts.has(params.key)) { - const { rect } = counterparts.get(params.key); - counterparts.delete(params.key); - - return crossfade(rect, node, params); + if (b.has(key)) { + const from_node = b.get(key); + b.delete(key); + return crossfade(from_node, to_node, params); + } else { + a.delete(key); + return fallback && fallback(to_node, params, is_intro); } - - // if the node is disappearing altogether - // (i.e. wasn't claimed by the other list) - // then we need to supply an outro - items.delete(params.key); - return fallback && fallback(node, params, intro); }; - }; - } + } + }; - return [ - transition(to_send, to_receive, false), - transition(to_receive, to_send, true) - ]; + return [transition(b, a, false), transition(a, b, true)]; } diff --git a/test/js/samples/each-block-keyed-animated/expected.js b/test/js/samples/each-block-keyed-animated/expected.js index 7fb81c27a226..f25b4cd57efc 100644 --- a/test/js/samples/each-block-keyed-animated/expected.js +++ b/test/js/samples/each-block-keyed-animated/expected.js @@ -2,15 +2,14 @@ import { SvelteComponent, append, - create_animation, detach, element, empty, - fix_and_destroy_block, fix_position, init, insert, noop, + run_animation, safe_not_equal, set_data, text, @@ -28,6 +27,7 @@ function create_each_block(key_1, ctx) { let div; let t_value = /*thing*/ ctx[1].name + ""; let t; + let unfreeze; let rect; let stop_animation = noop; @@ -50,15 +50,18 @@ function create_each_block(key_1, ctx) { rect = div.getBoundingClientRect(); }, f() { - fix_position(div); stop_animation(); + unfreeze = fix_position(div, rect); }, a() { - stop_animation(); - stop_animation = create_animation(div, rect, foo, {}); + if (unfreeze || !rect) return; else { + stop_animation(); + stop_animation = run_animation(div, rect, foo); + } }, d(detaching) { if (detaching) detach(div); + unfreeze = void 0; } }; } @@ -95,7 +98,7 @@ function create_fragment(ctx) { if (dirty & /*things*/ 1) { const each_value = /*things*/ ctx[0]; for (let i = 0; i < each_blocks.length; i += 1) each_blocks[i].r(); - each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx, each_value, each_1_lookup, each_1_anchor.parentNode, fix_and_destroy_block, create_each_block, each_1_anchor, get_each_context); + each_blocks = update_keyed_each(each_blocks, dirty, ctx, 3, get_key, each_value, each_1_lookup, each_1_anchor.parentNode, create_each_block, each_1_anchor, get_each_context); for (let i = 0; i < each_blocks.length; i += 1) each_blocks[i].a(); } }, diff --git a/test/js/samples/each-block-keyed/expected.js b/test/js/samples/each-block-keyed/expected.js index ad8c074e99f8..0707de49e502 100644 --- a/test/js/samples/each-block-keyed/expected.js +++ b/test/js/samples/each-block-keyed/expected.js @@ -2,7 +2,6 @@ import { SvelteComponent, append, - destroy_block, detach, element, empty, @@ -79,7 +78,7 @@ function create_fragment(ctx) { p(ctx, [dirty]) { if (dirty & /*things*/ 1) { const each_value = /*things*/ ctx[0]; - each_blocks = update_keyed_each(each_blocks, dirty, get_key, 1, ctx, each_value, each_1_lookup, each_1_anchor.parentNode, destroy_block, create_each_block, each_1_anchor, get_each_context); + each_blocks = update_keyed_each(each_blocks, dirty, ctx, 1, get_key, each_value, each_1_lookup, each_1_anchor.parentNode, create_each_block, each_1_anchor, get_each_context); } }, i: noop, diff --git a/test/js/samples/transition-local/expected.js b/test/js/samples/transition-local/expected.js index 25a03f026f75..5d4bde5ae252 100644 --- a/test/js/samples/transition-local/expected.js +++ b/test/js/samples/transition-local/expected.js @@ -1,14 +1,13 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent, - add_render_callback, - create_in_transition, detach, element, empty, init, insert, noop, + run_in, safe_not_equal, transition_in } from "svelte/internal"; @@ -53,7 +52,8 @@ function create_if_block(ctx) { // (9:1) {#if y} function create_if_block_1(ctx) { let div; - let div_intro; + let div_intro = noop; + let current; return { c() { @@ -62,18 +62,21 @@ function create_if_block_1(ctx) { }, m(target, anchor) { insert(target, div, anchor); + current = true; }, i(local) { + if (current) return; + if (local) { - if (!div_intro) { - add_render_callback(() => { - div_intro = create_in_transition(div, foo, {}); - div_intro.start(); - }); - } + div_intro(); + div_intro = run_in(div, foo); } + + current = true; + }, + o(local) { + current = false; }, - o: noop, d(detaching) { if (detaching) detach(div); } diff --git a/test/js/samples/transition-repeated-outro/expected.js b/test/js/samples/transition-repeated-outro/expected.js index 1f76a93666be..93b5dab54d67 100644 --- a/test/js/samples/transition-repeated-outro/expected.js +++ b/test/js/samples/transition-repeated-outro/expected.js @@ -1,14 +1,14 @@ /* generated by Svelte vX.Y.Z */ import { SvelteComponent, - check_outros, - create_out_transition, detach, element, empty, - group_outros, + group_transition_out, init, insert, + noop, + run_out, safe_not_equal, transition_in, transition_out @@ -18,7 +18,7 @@ import { fade } from "svelte/transition"; function create_if_block(ctx) { let div; - let div_outro; + let div_outro = noop; let current; return { @@ -32,16 +32,16 @@ function create_if_block(ctx) { }, i(local) { if (current) return; - if (div_outro) div_outro.end(1); + div_outro(1); current = true; }, o(local) { - div_outro = create_out_transition(div, fade, {}); + div_outro = run_out(div, fade); current = false; }, d(detaching) { if (detaching) detach(div); - if (detaching && div_outro) div_outro.end(); + if (detaching) div_outro(); } }; } @@ -74,13 +74,11 @@ function create_fragment(ctx) { if_block.m(if_block_anchor.parentNode, if_block_anchor); } } else if (if_block) { - group_outros(); - - transition_out(if_block, 1, 1, () => { - if_block = null; + group_transition_out(transition_out => { + transition_out(if_block, () => { + if_block = null; + }); }); - - check_outros(); } }, i(local) { diff --git a/test/runtime/samples/transition-css-in-out-in/_config.js b/test/runtime/samples/transition-css-in-out-in/_config.js index 10719280e92d..eaf761b6a71d 100644 --- a/test/runtime/samples/transition-css-in-out-in/_config.js +++ b/test/runtime/samples/transition-css-in-out-in/_config.js @@ -2,19 +2,24 @@ export default { test({ assert, component, target, window, raf }) { component.visible = true; const div = target.querySelector('div'); + const startsWith = (v, t = div.style.animation) => { + if (Array.isArray(v)) { + t.split(", ").forEach((r,i) => startsWith(v[i],r)); + } else { + assert.equal(t.slice(0, -1), v); + } + }; - assert.equal(div.style.animation, `__svelte_3809512021_0 100ms linear 0ms 1 both`); + startsWith(`100ms linear 0ms 1 normal both running __svelte_3261048502`); raf.tick(50); component.visible = false; - // both in and out styles - assert.equal(div.style.animation, `__svelte_3809512021_0 100ms linear 0ms 1 both, __svelte_3750847757_0 100ms linear 0ms 1 both`); + startsWith([`100ms linear 0ms 1 normal both running __svelte_3261048502`, `100ms linear 0ms 1 normal both running __svelte_890840093`]); raf.tick(75); component.visible = true; - // reset original styles - assert.equal(div.style.animation, `__svelte_3809512021_1 100ms linear 0ms 1 both`); + startsWith(`100ms linear 0ms 1 normal both running __svelte_3261048502`); }, }; diff --git a/test/runtime/samples/transition-js-args/_config.js b/test/runtime/samples/transition-js-args/_config.js index 00682894cd06..1999f0d54f92 100644 --- a/test/runtime/samples/transition-js-args/_config.js +++ b/test/runtime/samples/transition-js-args/_config.js @@ -4,9 +4,6 @@ export default { const div = target.querySelector('div'); - assert.equal(div.foo, 0); - assert.equal(div.oof, 1); - raf.tick(50); assert.equal(div.foo, 0.5); assert.equal(div.oof, 0.5); diff --git a/test/runtime/samples/transition-js-await-block/_config.js b/test/runtime/samples/transition-js-await-block/_config.js index 80546ae6b8f4..5da109748e34 100644 --- a/test/runtime/samples/transition-js-await-block/_config.js +++ b/test/runtime/samples/transition-js-await-block/_config.js @@ -17,8 +17,7 @@ export default { const p = target.querySelector('p'); assert.equal(p.className, 'pending'); - assert.equal(p.foo, 0); - + raf.tick(50); assert.equal(p.foo, 0.5); @@ -29,8 +28,8 @@ export default { const ps = document.querySelectorAll('p'); assert.equal(ps[1].className, 'pending'); assert.equal(ps[0].className, 'then'); - assert.equal(ps[1].foo, 0.2); - assert.equal(ps[0].foo, 0.3); + assert.equal(Math.round(ps[1].foo * 10) / 10, 0.2); + assert.equal(Math.round(ps[0].foo * 10) / 10, 0.3); }); } }; diff --git a/test/runtime/samples/transition-js-context/_config.js b/test/runtime/samples/transition-js-context/_config.js index e2e7135180be..823762ef379f 100644 --- a/test/runtime/samples/transition-js-context/_config.js +++ b/test/runtime/samples/transition-js-context/_config.js @@ -3,8 +3,7 @@ export default { component.visible = true; const div = target.querySelector('div'); - assert.equal(div.foo, 42); - + raf.tick(50); assert.equal(div.foo, 42); } diff --git a/test/runtime/samples/transition-js-deferred-b/_config.js b/test/runtime/samples/transition-js-deferred-b/_config.js index 05929964cd4e..3b435d91f9fa 100644 --- a/test/runtime/samples/transition-js-deferred-b/_config.js +++ b/test/runtime/samples/transition-js-deferred-b/_config.js @@ -4,8 +4,7 @@ export default { return Promise.resolve().then(() => { const div = target.querySelector('.foo'); - assert.equal(div.foo, 0); - + raf.tick(50); assert.equal(div.foo, 0.5); }); diff --git a/test/runtime/samples/transition-js-deferred/_config.js b/test/runtime/samples/transition-js-deferred/_config.js index 9d6833d2f491..b534aa4cbbca 100644 --- a/test/runtime/samples/transition-js-deferred/_config.js +++ b/test/runtime/samples/transition-js-deferred/_config.js @@ -3,8 +3,7 @@ export default { component.visible = true; return Promise.resolve().then(() => { - const div = target.querySelector('div'); - assert.equal(div.foo, 0); + const div = target.querySelector('.foo'); raf.tick(50); assert.equal(div.foo, 0.5); diff --git a/test/runtime/samples/transition-js-deferred/main.svelte b/test/runtime/samples/transition-js-deferred/main.svelte index 063773df396d..0297ced7beb2 100644 --- a/test/runtime/samples/transition-js-deferred/main.svelte +++ b/test/runtime/samples/transition-js-deferred/main.svelte @@ -7,9 +7,9 @@ function foo(node, params) { foo_text = node.textContent; - return () => { + return (is_intro) => { if (bar_text !== `b`) { - throw new Error(`foo ran prematurely`); + if (is_intro != null) throw new Error(`foo ran prematurely`); } return { @@ -24,9 +24,9 @@ function bar(node, params) { bar_text = node.textContent; - return () => { + return (is_intro) => { if (foo_text !== `a`) { - throw new Error(`bar ran prematurely`); + if (is_intro != null) throw new Error(`bar ran prematurely`); } return { @@ -40,7 +40,7 @@ {#if visible} -
a
+
a
{:else}
b
{/if} \ No newline at end of file diff --git a/test/runtime/samples/transition-js-delay-in-out/_config.js b/test/runtime/samples/transition-js-delay-in-out/_config.js index fb77fca01554..ba3df07de215 100644 --- a/test/runtime/samples/transition-js-delay-in-out/_config.js +++ b/test/runtime/samples/transition-js-delay-in-out/_config.js @@ -2,7 +2,6 @@ export default { test({ assert, component, target, window, raf }) { component.visible = true; const div = target.querySelector('div'); - assert.equal(div.foo, 0); raf.tick(50); assert.equal(div.foo, 0); diff --git a/test/runtime/samples/transition-js-delay/_config.js b/test/runtime/samples/transition-js-delay/_config.js index a0613737b25d..710a616dc64f 100644 --- a/test/runtime/samples/transition-js-delay/_config.js +++ b/test/runtime/samples/transition-js-delay/_config.js @@ -2,7 +2,6 @@ export default { test({ assert, component, target, window, raf }) { component.visible = true; const div = target.querySelector('div'); - assert.equal(div.foo, 0); raf.tick(50); assert.equal(div.foo, 0); diff --git a/test/runtime/samples/transition-js-dynamic-if-block-bidi/_config.js b/test/runtime/samples/transition-js-dynamic-if-block-bidi/_config.js index aa0bd9e1aaef..7cffe51d6d76 100644 --- a/test/runtime/samples/transition-js-dynamic-if-block-bidi/_config.js +++ b/test/runtime/samples/transition-js-dynamic-if-block-bidi/_config.js @@ -7,9 +7,8 @@ export default { global.count = 0; component.visible = true; - assert.equal(global.count, 1); + assert.equal(global.count, 2); const div = target.querySelector('div'); - assert.equal(div.foo, 0); raf.tick(75); component.name = 'everybody'; diff --git a/test/runtime/samples/transition-js-each-block-intro-outro/_config.js b/test/runtime/samples/transition-js-each-block-intro-outro/_config.js index 3a764c561241..2fd6b3075c24 100644 --- a/test/runtime/samples/transition-js-each-block-intro-outro/_config.js +++ b/test/runtime/samples/transition-js-each-block-intro-outro/_config.js @@ -7,9 +7,6 @@ export default { test({ assert, component, target, window, raf }) { component.visible = true; const divs = target.querySelectorAll('div'); - assert.equal(divs[0].foo, 0); - assert.equal(divs[1].foo, 0); - assert.equal(divs[2].foo, 0); raf.tick(50); assert.equal(divs[0].foo, 0.5); @@ -30,9 +27,9 @@ export default { component.visible = true; raf.tick(100); - assert.equal(divs[0].foo, 0.3); - assert.equal(divs[1].foo, 0.3); - assert.equal(divs[2].foo, 0.3); + assert.equal(Math.round(divs[0].foo * 10) / 10, 0.3); + assert.equal(Math.round(divs[1].foo * 10) / 10, 0.3); + assert.equal(Math.round(divs[2].foo * 10) / 10, 0.3); assert.equal(divs[0].bar, 1); assert.equal(divs[1].bar, 1); diff --git a/test/runtime/samples/transition-js-each-block-intro/_config.js b/test/runtime/samples/transition-js-each-block-intro/_config.js index 3d143ea5149a..d7b5abf8cf37 100644 --- a/test/runtime/samples/transition-js-each-block-intro/_config.js +++ b/test/runtime/samples/transition-js-each-block-intro/_config.js @@ -7,9 +7,6 @@ export default { test({ assert, component, target, window, raf }) { let divs = target.querySelectorAll('div'); - assert.equal(divs[0].foo, 0); - assert.equal(divs[1].foo, 0); - assert.equal(divs[2].foo, 0); raf.tick(50); assert.equal(divs[0].foo, 0.5); @@ -21,7 +18,6 @@ export default { assert.equal(divs[0].foo, 0.5); assert.equal(divs[1].foo, 0.5); assert.equal(divs[2].foo, 0.5); - assert.equal(divs[3].foo, 0); raf.tick(75); assert.equal(divs[0].foo, 0.75); diff --git a/test/runtime/samples/transition-js-each-block-keyed-intro-outro/_config.js b/test/runtime/samples/transition-js-each-block-keyed-intro-outro/_config.js index 083752cbd9ab..95c3b59bd295 100644 --- a/test/runtime/samples/transition-js-each-block-keyed-intro-outro/_config.js +++ b/test/runtime/samples/transition-js-each-block-keyed-intro-outro/_config.js @@ -15,9 +15,6 @@ export default { divs[1].i = 1; divs[2].i = 2; - assert.equal(divs[0].foo, 0); - assert.equal(divs[1].foo, 0); - assert.equal(divs[2].foo, 0); raf.tick(100); assert.equal(divs[0].foo, 1); diff --git a/test/runtime/samples/transition-js-each-block-keyed-intro/_config.js b/test/runtime/samples/transition-js-each-block-keyed-intro/_config.js index 5886cc5c6fff..d65b2343c419 100644 --- a/test/runtime/samples/transition-js-each-block-keyed-intro/_config.js +++ b/test/runtime/samples/transition-js-each-block-keyed-intro/_config.js @@ -11,9 +11,6 @@ export default { test({ assert, component, target, window, raf }) { let divs = target.querySelectorAll('div'); - assert.equal(divs[0].foo, 0); - assert.equal(divs[1].foo, 0); - assert.equal(divs[2].foo, 0); raf.tick(50); assert.equal(divs[0].foo, 0.5); @@ -34,7 +31,6 @@ export default { `); divs = target.querySelectorAll('div'); assert.equal(divs[0].foo, 0.5); - assert.equal(divs[1].foo, 0); assert.equal(divs[2].foo, 0.5); assert.equal(divs[3].foo, 0.5); diff --git a/test/runtime/samples/transition-js-events-in-out/_config.js b/test/runtime/samples/transition-js-events-in-out/_config.js index 854e7e2f4b24..d6e1019f9128 100644 --- a/test/runtime/samples/transition-js-events-in-out/_config.js +++ b/test/runtime/samples/transition-js-events-in-out/_config.js @@ -10,7 +10,7 @@ export default {

waiting...

`, - async test({ assert, component, target, raf }) { + test({ assert, component, target, raf }) { component.visible = true; assert.htmlEqual(target.innerHTML, ` @@ -21,12 +21,12 @@ export default {

d

`); - await raf.tick(50); + raf.tick(50); assert.deepEqual(component.intros.sort(), ['a', 'b', 'c', 'd']); assert.equal(component.intro_count, 4); - await raf.tick(100); + raf.tick(100); assert.equal(component.intro_count, 0); assert.htmlEqual(target.innerHTML, ` @@ -47,16 +47,16 @@ export default {

d

`); - await raf.tick(150); + raf.tick(150); assert.deepEqual(component.outros.sort(), ['a', 'b', 'c', 'd']); assert.equal(component.outro_count, 4); - await raf.tick(200); + raf.tick(200); assert.equal(component.outro_count, 0); component.visible = true; - await raf.tick(250); + raf.tick(250); assert.deepEqual(component.intros.sort(), ['a', 'a', 'b', 'b', 'c', 'c', 'd', 'd']); assert.equal(component.intro_count, 4); diff --git a/test/runtime/samples/transition-js-events/_config.js b/test/runtime/samples/transition-js-events/_config.js index 00d83cb2755f..891f8f03821a 100644 --- a/test/runtime/samples/transition-js-events/_config.js +++ b/test/runtime/samples/transition-js-events/_config.js @@ -10,7 +10,7 @@ export default {

waiting...

`, - async test({ assert, component, target, raf }) { + test({ assert, component, target, raf }) { component.visible = true; assert.htmlEqual(target.innerHTML, ` @@ -25,7 +25,7 @@ export default { assert.deepEqual(component.intros.sort(), ['a', 'b', 'c', 'd']); assert.equal(component.intro_count, 4); - await raf.tick(100); + raf.tick(100); assert.equal(component.intro_count, 0); assert.htmlEqual(target.innerHTML, ` @@ -55,7 +55,7 @@ export default { component.visible = true; - await raf.tick(250); + raf.tick(250); assert.deepEqual(component.intros.sort(), ['a', 'a', 'b', 'b', 'c', 'c', 'd', 'd']); assert.equal(component.intro_count, 4); diff --git a/test/runtime/samples/transition-js-if-block-bidi/_config.js b/test/runtime/samples/transition-js-if-block-bidi/_config.js index a070f978e2c8..3201c933b9e5 100644 --- a/test/runtime/samples/transition-js-if-block-bidi/_config.js +++ b/test/runtime/samples/transition-js-if-block-bidi/_config.js @@ -3,15 +3,14 @@ export default { global.count = 0; component.visible = true; - assert.equal(global.count, 1); + assert.equal(global.count, 2); const div = target.querySelector('div'); - assert.equal(div.foo, 0); raf.tick(300); assert.equal(div.foo, 0.75); component.visible = false; - assert.equal(global.count, 1); + assert.equal(global.count, 3); raf.tick(500); assert.equal(div.foo, 0.25); diff --git a/test/runtime/samples/transition-js-if-block-in-each-block-bidi/_config.js b/test/runtime/samples/transition-js-if-block-in-each-block-bidi/_config.js index 4c75e3ef06b2..f5469b045b54 100644 --- a/test/runtime/samples/transition-js-if-block-in-each-block-bidi/_config.js +++ b/test/runtime/samples/transition-js-if-block-in-each-block-bidi/_config.js @@ -16,7 +16,6 @@ export default { test({ assert, component, target, window, raf }) { const divs = target.querySelectorAll('div'); - assert.equal(divs[0].foo, 0); raf.tick(100); assert.equal(divs[0].foo, 1); diff --git a/test/runtime/samples/transition-js-if-block-intro-outro/_config.js b/test/runtime/samples/transition-js-if-block-intro-outro/_config.js index e6512c93ed23..e59cb015362a 100644 --- a/test/runtime/samples/transition-js-if-block-intro-outro/_config.js +++ b/test/runtime/samples/transition-js-if-block-intro-outro/_config.js @@ -2,7 +2,6 @@ export default { test({ assert, component, target, window, raf }) { component.visible = true; let div = target.querySelector('div'); - assert.equal(div.foo, 0); raf.tick(200); assert.equal(div.foo, 0.5); diff --git a/test/runtime/samples/transition-js-if-else-block-intro/_config.js b/test/runtime/samples/transition-js-if-else-block-intro/_config.js index c5eccf50e562..45d4b5934f5f 100644 --- a/test/runtime/samples/transition-js-if-else-block-intro/_config.js +++ b/test/runtime/samples/transition-js-if-else-block-intro/_config.js @@ -3,7 +3,6 @@ export default { test({ assert, component, target, raf }) { assert.equal(target.querySelector('div'), component.no); - assert.equal(component.no.foo, 0); raf.tick(200); assert.equal(component.no.foo, 0.5); @@ -11,7 +10,6 @@ export default { raf.tick(500); component.x = true; assert.equal(component.no, undefined); - assert.equal(component.yes.foo, 0); raf.tick(700); assert.equal(component.yes.foo, 0.5); diff --git a/test/runtime/samples/transition-js-initial/_config.js b/test/runtime/samples/transition-js-initial/_config.js index 3760a1cbfb53..9ed39aead3f0 100644 --- a/test/runtime/samples/transition-js-initial/_config.js +++ b/test/runtime/samples/transition-js-initial/_config.js @@ -3,7 +3,6 @@ export default { component.visible = true; const div = target.querySelector('div'); - assert.equal(div.foo, 0); raf.tick(50); assert.equal(div.foo, 0.5); diff --git a/test/runtime/samples/transition-js-intro-enabled-by-option/_config.js b/test/runtime/samples/transition-js-intro-enabled-by-option/_config.js index 4b5f1cf98755..820a26800be4 100644 --- a/test/runtime/samples/transition-js-intro-enabled-by-option/_config.js +++ b/test/runtime/samples/transition-js-intro-enabled-by-option/_config.js @@ -5,7 +5,6 @@ export default { test({ assert, component, target, window, raf }) { const div = target.querySelector('div'); - assert.equal(div.foo, 0); raf.tick(50); assert.equal(div.foo, 0.5); diff --git a/test/runtime/samples/transition-js-local-and-global/_config.js b/test/runtime/samples/transition-js-local-and-global/_config.js index b7f8baa7ee95..c10dc89d027f 100644 --- a/test/runtime/samples/transition-js-local-and-global/_config.js +++ b/test/runtime/samples/transition-js-local-and-global/_config.js @@ -11,7 +11,6 @@ export default { let divs = target.querySelectorAll('div'); assert.equal(divs[0].foo, undefined); - assert.equal(divs[1].foo, 0); raf.tick(50); assert.equal(divs[0].foo, undefined); diff --git a/test/runtime/samples/transition-js-local-nested-await/_config.js b/test/runtime/samples/transition-js-local-nested-await/_config.js index b07d88741fa1..8add2a208055 100644 --- a/test/runtime/samples/transition-js-local-nested-await/_config.js +++ b/test/runtime/samples/transition-js-local-nested-await/_config.js @@ -16,7 +16,6 @@ export default { return promise.then(() => { const div = target.querySelector('div'); - assert.equal(div.foo, 0); raf.tick(100); assert.equal(div.foo, 1); diff --git a/test/runtime/samples/transition-js-local-nested-component/_config.js b/test/runtime/samples/transition-js-local-nested-component/_config.js index 87d7aaa233f3..606b9f35ef8e 100644 --- a/test/runtime/samples/transition-js-local-nested-component/_config.js +++ b/test/runtime/samples/transition-js-local-nested-component/_config.js @@ -7,7 +7,6 @@ export default { component.x = true; const div = target.querySelector('div'); - assert.equal(div.foo, 0); raf.tick(100); assert.equal(div.foo, 1); diff --git a/test/runtime/samples/transition-js-local-nested-each-keyed/_config.js b/test/runtime/samples/transition-js-local-nested-each-keyed/_config.js index 0595e6701309..85b94422610c 100644 --- a/test/runtime/samples/transition-js-local-nested-each-keyed/_config.js +++ b/test/runtime/samples/transition-js-local-nested-each-keyed/_config.js @@ -18,7 +18,6 @@ export default { const div2 = target.querySelector('div:last-child'); assert.equal(div1.foo, undefined); - assert.equal(div2.foo, 0); raf.tick(200); assert.equal(div1.foo, undefined); diff --git a/test/runtime/samples/transition-js-local-nested-each/_config.js b/test/runtime/samples/transition-js-local-nested-each/_config.js index 0595e6701309..85b94422610c 100644 --- a/test/runtime/samples/transition-js-local-nested-each/_config.js +++ b/test/runtime/samples/transition-js-local-nested-each/_config.js @@ -18,7 +18,6 @@ export default { const div2 = target.querySelector('div:last-child'); assert.equal(div1.foo, undefined); - assert.equal(div2.foo, 0); raf.tick(200); assert.equal(div1.foo, undefined); diff --git a/test/runtime/samples/transition-js-nested-await/_config.js b/test/runtime/samples/transition-js-nested-await/_config.js index 78dbe5ae41d4..637f37ca2b9d 100644 --- a/test/runtime/samples/transition-js-nested-await/_config.js +++ b/test/runtime/samples/transition-js-nested-await/_config.js @@ -16,7 +16,6 @@ export default { return promise.then(() => { const div = target.querySelector('div'); - assert.equal(div.foo, 0); raf.tick(100); assert.equal(div.foo, 1); diff --git a/test/runtime/samples/transition-js-nested-component/_config.js b/test/runtime/samples/transition-js-nested-component/_config.js index f1ca81c52e87..7bdc49e2dc1b 100644 --- a/test/runtime/samples/transition-js-nested-component/_config.js +++ b/test/runtime/samples/transition-js-nested-component/_config.js @@ -7,7 +7,6 @@ export default { component.x = true; const div = target.querySelector('div'); - assert.equal(div.foo, 0); raf.tick(100); assert.equal(div.foo, 1); diff --git a/test/runtime/samples/transition-js-nested-each-keyed/_config.js b/test/runtime/samples/transition-js-nested-each-keyed/_config.js index 3d2fe3c32e57..9226e47eee69 100644 --- a/test/runtime/samples/transition-js-nested-each-keyed/_config.js +++ b/test/runtime/samples/transition-js-nested-each-keyed/_config.js @@ -8,7 +8,6 @@ export default { component.x = true; const div = target.querySelector('div'); - assert.equal(div.foo, 0); raf.tick(100); assert.equal(div.foo, 1); diff --git a/test/runtime/samples/transition-js-nested-each/_config.js b/test/runtime/samples/transition-js-nested-each/_config.js index c21024dd6995..0d3a393e9b40 100644 --- a/test/runtime/samples/transition-js-nested-each/_config.js +++ b/test/runtime/samples/transition-js-nested-each/_config.js @@ -8,7 +8,6 @@ export default { component.x = true; const div = target.querySelector('div'); - assert.equal(div.foo, 0); raf.tick(100); assert.equal(div.foo, 1); diff --git a/test/runtime/samples/transition-js-nested-if/_config.js b/test/runtime/samples/transition-js-nested-if/_config.js index 2fdd17da936e..93b624dfa85b 100644 --- a/test/runtime/samples/transition-js-nested-if/_config.js +++ b/test/runtime/samples/transition-js-nested-if/_config.js @@ -8,7 +8,6 @@ export default { component.x = true; const div = target.querySelector('div'); - assert.equal(div.foo, 0); raf.tick(100); assert.equal(div.foo, 1); diff --git a/test/runtime/samples/transition-js-nested-intro/_config.js b/test/runtime/samples/transition-js-nested-intro/_config.js index 9072e885fcbe..95d2775d50d0 100644 --- a/test/runtime/samples/transition-js-nested-intro/_config.js +++ b/test/runtime/samples/transition-js-nested-intro/_config.js @@ -2,7 +2,6 @@ export default { test({ assert, component, target, window, raf }) { component.visible = true; const div = target.querySelector('div'); - assert.equal(div.foo, 0); raf.tick(50); assert.equal(div.foo, 0); diff --git a/test/runtime/samples/transition-js-parameterised-with-state/_config.js b/test/runtime/samples/transition-js-parameterised-with-state/_config.js index 64e40dc63dbd..c7988fce9f8a 100644 --- a/test/runtime/samples/transition-js-parameterised-with-state/_config.js +++ b/test/runtime/samples/transition-js-parameterised-with-state/_config.js @@ -6,7 +6,6 @@ export default { test({ assert, component, target, window, raf }) { component.visible = true; const div = target.querySelector('div'); - assert.equal(div.foo, 0); raf.tick(50); assert.equal(div.foo, 100); diff --git a/test/runtime/samples/transition-js-parameterised/_config.js b/test/runtime/samples/transition-js-parameterised/_config.js index 1370905547a2..851c714a7192 100644 --- a/test/runtime/samples/transition-js-parameterised/_config.js +++ b/test/runtime/samples/transition-js-parameterised/_config.js @@ -2,7 +2,6 @@ export default { test({ assert, component, target, window, raf }) { component.visible = true; const div = target.querySelector('div'); - assert.equal(div.foo, 0); raf.tick(50); assert.equal(div.foo, 100); diff --git a/test/runtime/samples/transition-js-slot/_config.js b/test/runtime/samples/transition-js-slot/_config.js index a32e4bda7202..1af9e25d33af 100644 --- a/test/runtime/samples/transition-js-slot/_config.js +++ b/test/runtime/samples/transition-js-slot/_config.js @@ -10,7 +10,6 @@ export default { test({ assert, component, target, window, raf }) { component.visible = true; const p = target.querySelector('p'); - assert.equal(p.foo, 0); raf.tick(50); assert.equal(p.foo, 0.5);