From b7ab024d123449627f5195ca9cd18a9694f91731 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Sun, 30 Apr 2017 13:45:10 -0400 Subject: [PATCH] handle bidirectional transitions differently --- package.json | 1 + src/generators/dom/index.js | 2 +- .../dom/visitors/Element/Element.js | 2 +- .../dom/visitors/Element/addTransitions.js | 85 ++++++++++++------- src/shared/transitions.js | 52 +++++++++--- 5 files changed, 98 insertions(+), 44 deletions(-) diff --git a/package.json b/package.json index 31c7eacb7a..1a80a18c78 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "build:shared": "rollup -c rollup/rollup.config.shared.js", "build:ssr": "rollup -c rollup/rollup.config.ssr.js", "dev": "rollup -c rollup/rollup.config.main.js -w", + "dev:shared": "rollup -c rollup/rollup.config.shared.js -w", "pretest": "npm run build", "prepublish": "npm run lint && npm run build" }, diff --git a/src/generators/dom/index.js b/src/generators/dom/index.js index 8cf7226a22..fcd36fcba2 100644 --- a/src/generators/dom/index.js +++ b/src/generators/dom/index.js @@ -159,7 +159,7 @@ export default function dom ( parsed, source, options ) { ` ); } - if ( generator.hasComponents ) { + if ( generator.hasComponents || generator.hasIntroTransitions ) { const statement = `this._flush();`; builders.init.addBlock( statement ); diff --git a/src/generators/dom/visitors/Element/Element.js b/src/generators/dom/visitors/Element/Element.js index 11b3db978f..c42e79c947 100644 --- a/src/generators/dom/visitors/Element/Element.js +++ b/src/generators/dom/visitors/Element/Element.js @@ -62,7 +62,7 @@ export default function visitElement ( generator, block, state, node ) { visitors[ attribute.type ]( generator, block, childState, node, attribute ); }); - addTransitions( generator, block, childState, node, intro, outro ); + if ( intro || outro ) addTransitions( generator, block, childState, node, intro, outro ); if ( !outro && !state.parentNode ) { // TODO this probably doesn't belong here. We eventually need to consider diff --git a/src/generators/dom/visitors/Element/addTransitions.js b/src/generators/dom/visitors/Element/addTransitions.js index 2483b4f12b..3671321275 100644 --- a/src/generators/dom/visitors/Element/addTransitions.js +++ b/src/generators/dom/visitors/Element/addTransitions.js @@ -1,52 +1,77 @@ import deindent from '../../../../utils/deindent.js'; export default function addTransitions ( generator, block, state, node, intro, outro ) { - const introName = intro && block.getUniqueName( `${state.name}_intro` ); - const outroName = outro && block.getUniqueName( `${state.name}_outro` ); - - const introSnippet = intro && intro.expression ? block.contextualise( intro.expression ).snippet : '{}'; - - const outroSnippet = outro === intro ? - introSnippet : - outro && outro.expression ? block.contextualise( outro.expression ).snippet : '{}'; - const wrapTransition = generator.helper( 'wrapTransition' ); - if ( intro ) { - block.addVariable( introName ); + if ( intro === outro ) { + const name = block.getUniqueName( `${state.name}_transition` ); + const snippet = intro.expression ? block.contextualise( intro.expression ).snippet : '{}'; - const fn = `${generator.alias( 'template' )}.transitions.${intro.name}`; // TODO add built-in transitions? + block.addVariable( name ); - if ( outro ) { - block.builders.intro.addBlock( `if ( ${outroName} ) ${outroName}.abort();` ); - } + const fn = `${generator.alias( 'template' )}.transitions.${intro.name}`; block.builders.intro.addBlock( deindent` ${block.component}._renderHooks.push( function () { - ${introName} = ${wrapTransition}( ${state.name}, ${fn}, ${introSnippet}, true, null, function () { + if ( !${name} ) ${name} = ${wrapTransition}( ${state.name}, ${fn}, ${snippet}, true, null ); + ${name}.run( ${name}.t, 1, function () { ${block.component}.fire( 'intro.end', { node: ${state.name} }); }); - ${generator.helper( 'transitionManager' )}.add( ${introName} ); }); ` ); - } - - if ( outro ) { - block.addVariable( outroName ); - - const fn = `${generator.alias( 'template' )}.transitions.${outro.name}`; - - if ( intro ) { - block.builders.outro.addBlock( `${introName}.abort();` ); - } block.builders.outro.addBlock( deindent` - ${outroName} = ${wrapTransition}( ${state.name}, ${fn}, ${outroSnippet}, false, null, function () { - detachNode( div ); + ${name}.run( ${name}.t, 0, function () { + detachNode( ${state.name} ); ${block.component}.fire( 'outro.end', { node: ${state.name} }); if ( --${block.alias( 'outros' )} === 0 ) ${block.alias( 'outrocallback' )}(); + ${name} = null; }); - transitionManager.add( ${outroName} ); ` ); } + + else { + const introName = intro && block.getUniqueName( `${state.name}_intro` ); + const outroName = outro && block.getUniqueName( `${state.name}_outro` ); + + if ( intro ) { + block.addVariable( introName ); + const snippet = intro.expression ? block.contextualise( intro.expression ).snippet : '{}'; + + const fn = `${generator.alias( 'template' )}.transitions.${intro.name}`; // TODO add built-in transitions? + + if ( outro ) { + block.builders.intro.addBlock( `if ( ${outroName} ) ${outroName}.abort();` ); + } + + block.builders.intro.addBlock( deindent` + ${block.component}._renderHooks.push( function () { + ${introName} = ${wrapTransition}( ${state.name}, ${fn}, ${snippet}, true, null ); + ${introName}.run( 0, 1, function () { + ${block.component}.fire( 'intro.end', { node: ${state.name} }); + }); + }); + ` ); + } + + if ( outro ) { + block.addVariable( outroName ); + const snippet = outro.expression ? block.contextualise( outro.expression ).snippet : '{}'; + + const fn = `${generator.alias( 'template' )}.transitions.${outro.name}`; + + if ( intro ) { + block.builders.outro.addBlock( `${introName}.abort();` ); + } + + block.builders.outro.addBlock( deindent` + ${outroName} = ${wrapTransition}( ${state.name}, ${fn}, ${snippet}, false, null ); + ${outroName}.run( 1, 0, function () { + detachNode( ${state.name} ); + ${block.component}.fire( 'outro.end', { node: ${state.name} }); + if ( --${block.alias( 'outros' )} === 0 ) ${block.alias( 'outrocallback' )}(); + }); + ` ); + } + } } \ No newline at end of file diff --git a/src/shared/transitions.js b/src/shared/transitions.js index 58406864f7..e106a6a837 100644 --- a/src/shared/transitions.js +++ b/src/shared/transitions.js @@ -4,12 +4,10 @@ export function linear ( t ) { return t; } -export function wrapTransition ( node, fn, params, intro, outgroup, callback ) { +export function wrapTransition ( node, fn, params, intro, outgroup ) { var obj = fn( node, params, intro ); - var start = window.performance.now() + ( obj.delay || 0 ); var duration = obj.duration || 300; - var end = start + duration; var ease = obj.easing || linear; if ( obj.tick ) { @@ -17,19 +15,40 @@ export function wrapTransition ( node, fn, params, intro, outgroup, callback ) { if ( intro ) obj.tick( 0 ); return { - start: start, - end: end, + start: null, + end: null, + a: null, + d: null, + running: false, + t: intro ? 0 : 1, + callback: null, update: function ( now ) { - const p = intro ? now - start : end - now; - obj.tick( ease( p / duration ) ); + const p = now - this.start; + this.t = this.a + this.d * ease( p / this.duration ); + obj.tick( this.t ); }, done: function () { obj.tick( intro ? 1 : 0 ); - callback(); + this.callback(); + this.running = false; }, abort: function () { if ( !intro ) obj.tick( 1 ); // reset styles for intro - this.aborted = true; + this.running = false; + }, + run: function ( a, b, callback ) { + this.a = a; + this.d = b - a; + this.start = window.performance.now() + ( obj.delay || 0 ); + this.duration = duration * Math.abs( b - a ); + this.end = this.start + this.duration; + + this.callback = callback; + + if ( !this.running ) { + this.running = true; + transitionManager.add( this ); + } } }; } else { @@ -75,6 +94,9 @@ export function wrapTransition ( node, fn, params, intro, outgroup, callback ) { abort: function () { node.style.cssText = getComputedStyle( node ).cssText; this.aborted = true; + }, + run: function ( a, b, callback ) { + // TODO... } }; } @@ -110,11 +132,17 @@ export var transitionManager = { while ( i-- ) { var transition = transitionManager.transitions[i]; - if ( now < transition.end && !transition.aborted ) { - if ( now > transition.start ) transition.update( now ); + if ( transition.running ) { + if ( now >= transition.end ) { + transition.done(); + } else if ( now > transition.start ) { + transition.update( now ); + } + } + + if ( transition.running ) { transitionManager.running = true; } else { - if ( !transition.aborted ) transition.done(); transitionManager.transitions.splice( i, 1 ); } }