apply delays to bidirectional transitions - fixes #562

pull/567/head
Rich-Harris 8 years ago
parent 90d2e7f883
commit 1a92398101

@ -14,14 +14,14 @@ export default function addTransitions ( generator, block, state, node, intro, o
block.builders.intro.addBlock( deindent` block.builders.intro.addBlock( deindent`
${block.component}._renderHooks.push( function () { ${block.component}._renderHooks.push( function () {
if ( !${name} ) ${name} = ${wrapTransition}( ${state.name}, ${fn}, ${snippet}, true, null ); 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.component}.fire( 'intro.end', { node: ${state.name} });
}); });
}); });
` ); ` );
block.builders.outro.addBlock( deindent` block.builders.outro.addBlock( deindent`
${name}.run( ${name}.t, 0, function () { ${name}.run( false, function () {
${block.component}.fire( 'outro.end', { node: ${state.name} }); ${block.component}.fire( 'outro.end', { node: ${state.name} });
if ( --${block.alias( 'outros' )} === 0 ) ${block.alias( 'outrocallback' )}(); if ( --${block.alias( 'outros' )} === 0 ) ${block.alias( 'outrocallback' )}();
${name} = null; ${name} = null;
@ -49,7 +49,7 @@ export default function addTransitions ( generator, block, state, node, intro, o
block.builders.intro.addBlock( deindent` block.builders.intro.addBlock( deindent`
${block.component}._renderHooks.push( function () { ${block.component}._renderHooks.push( function () {
${introName} = ${wrapTransition}( ${state.name}, ${fn}, ${snippet}, true, null ); ${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} }); ${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 // group) prior to their removal from the DOM
block.builders.outro.addBlock( deindent` block.builders.outro.addBlock( deindent`
${outroName} = ${wrapTransition}( ${state.name}, ${fn}, ${snippet}, false, null ); ${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} }); ${block.component}.fire( 'outro.end', { node: ${state.name} });
if ( --${block.alias( 'outros' )} === 0 ) ${block.alias( 'outrocallback' )}(); if ( --${block.alias( 'outros' )} === 0 ) ${block.alias( 'outrocallback' )}();
}); });

@ -28,7 +28,7 @@ export function generateKeyframes ( a, b, delta, duration, ease, fn, node, style
} }
export function wrapTransition ( node, fn, params, intro, outgroup ) { 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 duration = obj.duration || 300;
var ease = obj.easing || linear; var ease = obj.easing || linear;
@ -40,26 +40,20 @@ export function wrapTransition ( node, fn, params, intro, outgroup ) {
if ( intro && obj.tick ) obj.tick( 0 ); if ( intro && obj.tick ) obj.tick( 0 );
return { return {
start: null,
end: null,
a: null,
b: null,
d: null,
running: false, running: false,
t: intro ? 0 : 1, t: intro ? 0 : 1,
callback: null, pending: null,
run: function ( a, b, callback ) { run: function ( intro, callback ) {
this.a = a; var program = {
this.b = b; intro: intro,
this.delta = b - a; start: window.performance.now() + ( obj.delay || 0 ),
this.start = window.performance.now() + ( obj.delay || 0 ); callback: callback
this.duration = duration * Math.abs( b - a ); };
this.end = this.start + this.duration;
this.callback = callback;
if ( obj.css ) { if ( obj.delay ) {
generateKeyframes( this.a, this.b, this.delta, this.duration, ease, obj.css, node, style ); this.pending = program;
} else {
this.start( program );
} }
if ( !this.running ) { if ( !this.running ) {
@ -67,21 +61,41 @@ export function wrapTransition ( node, fn, params, intro, outgroup ) {
transitionManager.add( this ); 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 ) { update: function ( now ) {
var p = now - this.start; var program = this.program;
this.t = this.a + this.delta * ease( p / this.duration ); 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 ); if ( obj.tick ) obj.tick( this.t );
}, },
done: function () { 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 ); if ( obj.css ) document.head.removeChild( style );
this.callback(); this.program.callback();
this.running = false; this.program = null;
this.running = !!this.pending;
}, },
abort: function () { abort: function () {
if ( obj.tick ) obj.tick( 1 ); if ( obj.tick ) obj.tick( 1 );
if ( obj.css ) document.head.removeChild( style ); 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-- ) { while ( i-- ) {
var transition = transitionManager.transitions[i]; var transition = transitionManager.transitions[i];
if ( transition.running ) { if ( transition.program && now >= transition.program.end ) {
if ( now >= transition.end ) {
transition.running = false;
transition.done(); transition.done();
} else if ( now > transition.start ) {
transition.update( now );
} }
if ( transition.pending && now >= transition.pending.start ) {
transition.start( transition.pending );
}
if ( transition.running ) {
transition.update( now );
transitionManager.running = true; transitionManager.running = true;
} else { } else if ( !transition.pending ) {
transitionManager.transitions.splice( i, 1 ); transitionManager.transitions.splice( i, 1 );
} }
} }

@ -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();
}
};

@ -0,0 +1,29 @@
{{#if visible}}
<div in:foo out:bar>delayed</div>
{{/if}}
<script>
export default {
transitions: {
foo: function ( node, params ) {
return {
delay: 50,
duration: 100,
tick: t => {
node.foo = t;
}
};
},
bar: function ( node, params ) {
return {
delay: 50,
duration: 100,
tick: t => {
node.bar = t;
}
};
}
}
};
</script>

@ -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();
}
};

@ -0,0 +1,19 @@
{{#if visible}}
<div transition:foo>delayed</div>
{{/if}}
<script>
export default {
transitions: {
foo: function ( node, params ) {
return {
delay: 50,
duration: 100,
tick: t => {
node.foo = t;
}
};
}
}
};
</script>

@ -11,7 +11,7 @@ export default {
const div = target.querySelector( 'div' ); const div = target.querySelector( 'div' );
assert.equal( div.foo, 0 ); assert.equal( div.foo, 0 );
raf.tick( 300 ); raf.tick( 75 );
component.set({ name: 'everybody' }); component.set({ name: 'everybody' });
assert.equal( div.foo, 0.75 ); assert.equal( div.foo, 0.75 );
assert.htmlEqual( div.innerHTML, 'hello everybody!' ); assert.htmlEqual( div.innerHTML, 'hello everybody!' );
@ -19,18 +19,18 @@ export default {
component.set({ visible: false, name: 'again' }); component.set({ visible: false, name: 'again' });
assert.htmlEqual( div.innerHTML, 'hello everybody!' ); assert.htmlEqual( div.innerHTML, 'hello everybody!' );
raf.tick( 500 ); raf.tick( 125 );
assert.equal( div.foo, 0.25 ); assert.equal( div.foo, 0.25 );
component.set({ visible: true }); component.set({ visible: true });
raf.tick( 700 ); raf.tick( 175 );
assert.equal( div.foo, 0.75 ); assert.equal( div.foo, 0.75 );
assert.htmlEqual( div.innerHTML, 'hello again!' ); assert.htmlEqual( div.innerHTML, 'hello again!' );
raf.tick( 800 ); raf.tick( 200 );
assert.equal( div.foo, 1 ); assert.equal( div.foo, 1 );
raf.tick( 900 ); raf.tick( 225 );
component.destroy(); component.destroy();
} }

@ -8,7 +8,7 @@
foo: function ( node, params ) { foo: function ( node, params ) {
global.count += 1; global.count += 1;
return { return {
duration: 400, duration: 100,
tick: t => { tick: t => {
node.foo = t; node.foo = t;
} }

Loading…
Cancel
Save