From 1a92398101cbdfdda476748828e5ff53369aa01a Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Thu, 4 May 2017 17:47:22 -0400 Subject: [PATCH 1/2] apply delays to bidirectional transitions - fixes #562 --- .../dom/visitors/Element/addTransitions.js | 8 +- src/shared/transitions.js | 82 +++++++++++-------- .../transition-js-delay-in-out/_config.js | 24 ++++++ .../transition-js-delay-in-out/main.html | 29 +++++++ .../samples/transition-js-delay/_config.js | 29 +++++++ .../samples/transition-js-delay/main.html | 19 +++++ .../_config.js | 10 +-- .../main.html | 2 +- 8 files changed, 160 insertions(+), 43 deletions(-) create mode 100644 test/runtime/samples/transition-js-delay-in-out/_config.js create mode 100644 test/runtime/samples/transition-js-delay-in-out/main.html create mode 100644 test/runtime/samples/transition-js-delay/_config.js create mode 100644 test/runtime/samples/transition-js-delay/main.html diff --git a/src/generators/dom/visitors/Element/addTransitions.js b/src/generators/dom/visitors/Element/addTransitions.js index e7fda98331..6a5953d02c 100644 --- a/src/generators/dom/visitors/Element/addTransitions.js +++ b/src/generators/dom/visitors/Element/addTransitions.js @@ -14,14 +14,14 @@ export default function addTransitions ( generator, block, state, node, intro, o block.builders.intro.addBlock( deindent` ${block.component}._renderHooks.push( function () { if ( !${name} ) ${name} = ${wrapTransition}( ${state.name}, ${fn}, ${snippet}, true, null ); - ${name}.run( ${name}.t, 1, function () { + ${name}.run( true, function () { ${block.component}.fire( 'intro.end', { node: ${state.name} }); }); }); ` ); block.builders.outro.addBlock( deindent` - ${name}.run( ${name}.t, 0, function () { + ${name}.run( false, function () { ${block.component}.fire( 'outro.end', { node: ${state.name} }); if ( --${block.alias( 'outros' )} === 0 ) ${block.alias( 'outrocallback' )}(); ${name} = null; @@ -49,7 +49,7 @@ export default function addTransitions ( generator, block, state, node, intro, o block.builders.intro.addBlock( deindent` ${block.component}._renderHooks.push( function () { ${introName} = ${wrapTransition}( ${state.name}, ${fn}, ${snippet}, true, null ); - ${introName}.run( 0, 1, function () { + ${introName}.run( true, function () { ${block.component}.fire( 'intro.end', { node: ${state.name} }); }); }); @@ -66,7 +66,7 @@ export default function addTransitions ( generator, block, state, node, intro, o // group) prior to their removal from the DOM block.builders.outro.addBlock( deindent` ${outroName} = ${wrapTransition}( ${state.name}, ${fn}, ${snippet}, false, null ); - ${outroName}.run( 1, 0, function () { + ${outroName}.run( false, function () { ${block.component}.fire( 'outro.end', { node: ${state.name} }); if ( --${block.alias( 'outros' )} === 0 ) ${block.alias( 'outrocallback' )}(); }); diff --git a/src/shared/transitions.js b/src/shared/transitions.js index 289ded6182..f13f3a357c 100644 --- a/src/shared/transitions.js +++ b/src/shared/transitions.js @@ -28,7 +28,7 @@ export function generateKeyframes ( a, b, delta, duration, ease, fn, node, style } export function wrapTransition ( node, fn, params, intro, outgroup ) { - var obj = fn( node, params, intro ); + var obj = fn( node, params ); var duration = obj.duration || 300; var ease = obj.easing || linear; @@ -40,26 +40,20 @@ export function wrapTransition ( node, fn, params, intro, outgroup ) { if ( intro && obj.tick ) obj.tick( 0 ); return { - start: null, - end: null, - a: null, - b: null, - d: null, running: false, t: intro ? 0 : 1, - callback: null, - run: function ( a, b, callback ) { - this.a = a; - this.b = b; - this.delta = 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 ( obj.css ) { - generateKeyframes( this.a, this.b, this.delta, this.duration, ease, obj.css, node, style ); + pending: null, + run: function ( intro, callback ) { + var program = { + intro: intro, + start: window.performance.now() + ( obj.delay || 0 ), + callback: callback + }; + + if ( obj.delay ) { + this.pending = program; + } else { + this.start( program ); } if ( !this.running ) { @@ -67,21 +61,41 @@ export function wrapTransition ( node, fn, params, intro, outgroup ) { transitionManager.add( this ); } }, + start: function ( program ) { + program.a = this.t; + program.b = program.intro ? 1 : 0; + program.delta = program.b - program.a; + program.duration = duration * Math.abs( program.b - program.a ); + program.end = program.start + program.duration; + + if ( obj.css ) { + generateKeyframes( program.a, program.b, program.delta, program.duration, ease, obj.css, node, style ); + } + + this.program = program; + this.pending = null; + }, update: function ( now ) { - var p = now - this.start; - this.t = this.a + this.delta * ease( p / this.duration ); + var program = this.program; + if ( !program ) return; + + var p = now - program.start; + this.t = program.a + program.delta * ease( p / program.duration ); if ( obj.tick ) obj.tick( this.t ); }, done: function () { - if ( obj.tick ) obj.tick( intro ? 1 : 0 ); + this.t = this.program.b; + if ( obj.tick ) obj.tick( this.t ); if ( obj.css ) document.head.removeChild( style ); - this.callback(); - this.running = false; + this.program.callback(); + this.program = null; + this.running = !!this.pending; }, abort: function () { if ( obj.tick ) obj.tick( 1 ); if ( obj.css ) document.head.removeChild( style ); - this.running = false; + this.program = null; + this.running = !!this.pending; } }; } @@ -108,16 +122,18 @@ export var transitionManager = { while ( i-- ) { var transition = transitionManager.transitions[i]; - if ( transition.running ) { - if ( now >= transition.end ) { - transition.running = false; - transition.done(); - } else if ( now > transition.start ) { - transition.update( now ); - } + if ( transition.program && now >= transition.program.end ) { + transition.done(); + } + if ( transition.pending && now >= transition.pending.start ) { + transition.start( transition.pending ); + } + + if ( transition.running ) { + transition.update( now ); transitionManager.running = true; - } else { + } else if ( !transition.pending ) { transitionManager.transitions.splice( i, 1 ); } } diff --git a/test/runtime/samples/transition-js-delay-in-out/_config.js b/test/runtime/samples/transition-js-delay-in-out/_config.js new file mode 100644 index 0000000000..6e97f19c3b --- /dev/null +++ b/test/runtime/samples/transition-js-delay-in-out/_config.js @@ -0,0 +1,24 @@ +export default { + test ( assert, component, target, window, raf ) { + component.set({ visible: true }); + const div = target.querySelector( 'div' ); + assert.equal( div.foo, 0 ); + + raf.tick( 50 ); + assert.equal( div.foo, 0 ); + + raf.tick( 150 ); + assert.equal( div.foo, 1 ); + + component.set({ visible: false }); + assert.equal( div.bar, undefined ); + + raf.tick( 200 ); + assert.equal( div.bar, 1 ); + + raf.tick( 300 ); + assert.equal( div.bar, 0 ); + + component.destroy(); + } +}; \ No newline at end of file diff --git a/test/runtime/samples/transition-js-delay-in-out/main.html b/test/runtime/samples/transition-js-delay-in-out/main.html new file mode 100644 index 0000000000..6a0a3f88a7 --- /dev/null +++ b/test/runtime/samples/transition-js-delay-in-out/main.html @@ -0,0 +1,29 @@ +{{#if visible}} +
delayed
+{{/if}} + + \ No newline at end of file diff --git a/test/runtime/samples/transition-js-delay/_config.js b/test/runtime/samples/transition-js-delay/_config.js new file mode 100644 index 0000000000..eab832ca3f --- /dev/null +++ b/test/runtime/samples/transition-js-delay/_config.js @@ -0,0 +1,29 @@ +export default { + test ( assert, component, target, window, raf ) { + component.set({ visible: true }); + const div = target.querySelector( 'div' ); + assert.equal( div.foo, 0 ); + + raf.tick( 50 ); + assert.equal( div.foo, 0 ); + + raf.tick( 100 ); + assert.equal( div.foo, 0.5 ); + + component.set({ visible: false }); + + raf.tick( 125 ); + assert.equal( div.foo, 0.75 ); + + raf.tick( 150 ); + assert.equal( div.foo, 1 ); + + raf.tick( 175 ); + assert.equal( div.foo, 0.75 ); + + raf.tick( 250 ); + assert.equal( div.foo, 0 ); + + component.destroy(); + } +}; \ No newline at end of file diff --git a/test/runtime/samples/transition-js-delay/main.html b/test/runtime/samples/transition-js-delay/main.html new file mode 100644 index 0000000000..a4277f34ce --- /dev/null +++ b/test/runtime/samples/transition-js-delay/main.html @@ -0,0 +1,19 @@ +{{#if visible}} +
delayed
+{{/if}} + + \ No newline at end of file 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 fcf391ac60..65dece1a13 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 @@ -11,7 +11,7 @@ export default { const div = target.querySelector( 'div' ); assert.equal( div.foo, 0 ); - raf.tick( 300 ); + raf.tick( 75 ); component.set({ name: 'everybody' }); assert.equal( div.foo, 0.75 ); assert.htmlEqual( div.innerHTML, 'hello everybody!' ); @@ -19,18 +19,18 @@ export default { component.set({ visible: false, name: 'again' }); assert.htmlEqual( div.innerHTML, 'hello everybody!' ); - raf.tick( 500 ); + raf.tick( 125 ); assert.equal( div.foo, 0.25 ); component.set({ visible: true }); - raf.tick( 700 ); + raf.tick( 175 ); assert.equal( div.foo, 0.75 ); assert.htmlEqual( div.innerHTML, 'hello again!' ); - raf.tick( 800 ); + raf.tick( 200 ); assert.equal( div.foo, 1 ); - raf.tick( 900 ); + raf.tick( 225 ); component.destroy(); } diff --git a/test/runtime/samples/transition-js-dynamic-if-block-bidi/main.html b/test/runtime/samples/transition-js-dynamic-if-block-bidi/main.html index bc0dace68b..576b4efc2e 100644 --- a/test/runtime/samples/transition-js-dynamic-if-block-bidi/main.html +++ b/test/runtime/samples/transition-js-dynamic-if-block-bidi/main.html @@ -8,7 +8,7 @@ foo: function ( node, params ) { global.count += 1; return { - duration: 400, + duration: 100, tick: t => { node.foo = t; } From cfd5d3e3c78fc3ea5929d24edf65c748047e2803 Mon Sep 17 00:00:00 2001 From: Rich-Harris Date: Thu, 4 May 2017 17:57:04 -0400 Subject: [PATCH 2/2] minor tidy up --- src/shared/transitions.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/shared/transitions.js b/src/shared/transitions.js index f13f3a357c..916a69b3dd 100644 --- a/src/shared/transitions.js +++ b/src/shared/transitions.js @@ -40,13 +40,14 @@ export function wrapTransition ( node, fn, params, intro, outgroup ) { if ( intro && obj.tick ) obj.tick( 0 ); return { - running: false, t: intro ? 0 : 1, + running: false, + program: null, pending: null, run: function ( intro, callback ) { var program = { - intro: intro, start: window.performance.now() + ( obj.delay || 0 ), + intro: intro, callback: callback }; @@ -94,8 +95,8 @@ export function wrapTransition ( node, fn, params, intro, outgroup ) { abort: function () { if ( obj.tick ) obj.tick( 1 ); if ( obj.css ) document.head.removeChild( style ); - this.program = null; - this.running = !!this.pending; + this.program = this.pending = null; + this.running = false; } }; }