diff --git a/src/shared/transitions.js b/src/shared/transitions.js index e106a6a837..8f92cef272 100644 --- a/src/shared/transitions.js +++ b/src/shared/transitions.js @@ -1,4 +1,4 @@ -import { noop } from './utils.js'; +import { assign, noop } from './utils.js'; export function linear ( t ) { return t; @@ -10,18 +10,39 @@ export function wrapTransition ( node, fn, params, intro, outgroup ) { var duration = obj.duration || 300; var ease = obj.easing || linear; + var transition = { + 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.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 ( !obj.tick ) this.generateKeyframes(); + + if ( !this.running ) { + this.running = true; + transitionManager.add( this ); + } + } + } + if ( obj.tick ) { // JS transition if ( intro ) obj.tick( 0 ); - return { - start: null, - end: null, - a: null, - d: null, - running: false, - t: intro ? 0 : 1, - callback: null, + return assign( transition, { update: function ( now ) { const p = now - this.start; this.t = this.a + this.d * ease( p / this.duration ); @@ -30,76 +51,58 @@ export function wrapTransition ( node, fn, params, intro, outgroup ) { done: function () { obj.tick( intro ? 1 : 0 ); this.callback(); - this.running = false; }, abort: function () { if ( !intro ) obj.tick( 1 ); // reset styles for intro 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 { - // CSS transition - var started = false; - var inlineStyles = {}; - var computedStyles = getComputedStyle( node ); - - return { - start: start, - end: end, - init: function () { - for ( var key in obj.styles ) { - inlineStyles[ key ] = node.style[ key ]; - node.style[ key ] = intro ? obj.styles[ key ] : computedStyles[ key ]; - } - }, - update: function ( now ) { - if ( !started ) { - var keys = Object.keys( obj.styles ); - div.style.transition = keys.map( function ( key ) { - return key + ' ' + d; - }).join( ', ' ); - - // TODO use a keyframe animation for custom easing functions - - for ( var key in obj.styles ) { - node.style[ key ] = intro ? computedStyles[ key ] : obj.styles[ key ]; - } + }); + } - started = true; - } - }, - done: function () { - // TODO what if one of these styles was dynamic? - if ( intro ) { - for ( var key in obj.styles ) { - node.style[ key ] = inlineStyles[ key ]; - } - } - callback(); - }, - abort: function () { - node.style.cssText = getComputedStyle( node ).cssText; - this.aborted = true; - }, - run: function ( a, b, callback ) { - // TODO... + // CSS transition + var started = false; + var id = null; + var style = document.createElement( 'style' ); + + var cleanup = function () { + document.head.removeChild( style ); + transition.running = started = false; + }; + + return assign( transition, { + generateKeyframes: function () { + id = 'svelte_' + ~~( Math.random() * 1e9 ); // TODO make this more robust + var keyframes = '@keyframes ' + id + '{\n'; + + for ( var p = 0; p <= 1; p += 166.666 / this.duration ) { + var t = this.a + this.d * ease( p ); + var styles = obj.styles( ease( t ) ); + keyframes += ( p * 100 ) + '%{' + styles + '}\n'; } - }; - } + + keyframes += '100% {' + obj.styles( this.b ) + '}\n}'; + + style.textContent = keyframes; + document.head.appendChild( style ); + + node.style.animationName = id; + node.style.animationDuration = ( this.duration / 1e3 ) + 's'; + node.style.animationTimingFunction = 'linear'; + node.style.animationIterationCount = 1; + node.style.animationFillMode = 'forwards'; + }, + update: function ( now ) { + const p = now - this.start; + this.t = this.a + this.d * ease( p / this.duration ); + }, + done: function () { + this.callback(); + cleanup(); + }, + abort: function () { + cleanup(); + } + }); } export var transitionManager = { @@ -134,13 +137,12 @@ export var transitionManager = { if ( transition.running ) { if ( now >= transition.end ) { + transition.running = false; transition.done(); } else if ( now > transition.start ) { transition.update( now ); } - } - if ( transition.running ) { transitionManager.running = true; } else { transitionManager.transitions.splice( i, 1 );