diff --git a/css/reveal.css b/css/reveal.css index 0ca77e77..2688acc0 100644 --- a/css/reveal.css +++ b/css/reveal.css @@ -931,6 +931,17 @@ body { transform: none; transition: none; } +/********************************************* + * AUTO ANIMATE + *********************************************/ +.reveal section[data-auto-animate] { + transition: none; } + +.reveal section[data-auto-animate] .auto-animate-target { + transition: all 0.7s ease; + -webkit-transform-origin: top left; + transform-origin: top left; } + /********************************************* * PAUSED MODE *********************************************/ diff --git a/css/reveal.scss b/css/reveal.scss index ab11f32e..81cc8a37 100644 --- a/css/reveal.scss +++ b/css/reveal.scss @@ -990,6 +990,21 @@ $controlsArrowAngleActive: 36deg; } +/********************************************* + * AUTO ANIMATE + *********************************************/ + +.reveal section[data-auto-animate] { + transition: none; +} + +.reveal section[data-auto-animate] .auto-animate-target { + transition: all 0.7s ease; + transform-origin: top left; +} + + + /********************************************* * PAUSED MODE *********************************************/ diff --git a/js/reveal.js b/js/reveal.js index 99745269..28fb35a2 100644 --- a/js/reveal.js +++ b/js/reveal.js @@ -187,6 +187,20 @@ // - false: All iframes with data-src will be loaded only when visible preloadIframes: null, + // Can be used to globally disable auto-animation + autoAnimate: true, + + // CSS styles that auto-animations will animate between + autoAnimateStyles: [ + 'opacity', + 'color', + 'backgroundColor', + 'border-top-left-radius', + 'border-top-right-radius', + 'border-bottom-left-radius', + 'border-bottom-right-radius' + ], + // Controls automatic progression to the next slide // - 0: Auto-sliding only happens if the data-autoslide HTML attribute // is present on the current slide or fragment @@ -361,6 +375,10 @@ // Flags if the interaction event listeners are bound eventsAreBound = false, + // A list of all elements that we have animated through + // auto-animations + autoAnimatedRollbacks = [], + // The current auto-slide duration autoSlide = 0, @@ -1390,6 +1408,12 @@ enablePreviewLinks( '[data-preview-link]:not([data-preview-link=false])' ); } + // Reset all auto animated elements + autoAnimatedRollbacks.forEach( function( rollback ) { + rollback(); + } ); + autoAnimatedRollbacks = []; + // Remove existing auto-slide controls if( autoSlidePlayer ) { autoSlidePlayer.destroy(); @@ -2300,7 +2324,7 @@ continue; } - if( config.center || slide.classList.contains( 'center' ) ) { + if( ( config.center || slide.classList.contains( 'center' ) ) ) { // Vertical stacks are not centred since their section // children will be if( slide.classList.contains( 'stack' ) ) { @@ -2994,6 +3018,10 @@ cueAutoSlide(); + if( slideChanged && previousSlide && currentSlide ) { + autoAnimate( previousSlide, currentSlide ); + } + } /** @@ -3751,6 +3779,115 @@ } + /** + * Runs an auto-animation between the given slides. + * + * @param {HTMLElement} fromSlide + * @param {HTMLElement} toSlide + */ + function autoAnimate( fromSlide, toSlide ) { + + if( config.autoAnimate ) { + + var prevTargets = {}; + + toArray( fromSlide.querySelectorAll( '[data-id]' ) ).forEach( function( element ) { + prevTargets[ element.getAttribute( 'data-id' ) ] = element; + } ); + + toArray( toSlide.querySelectorAll( '[data-id]' ) ).forEach( function( element ) { + var previousElement = prevTargets[ element.getAttribute( 'data-id' ) ]; + if( previousElement ) { + autoAnimateElement( previousElement, element ); + } + } ); + + } + + } + + /** + * Auto-animates the properties of an element from their original + * values to their new state. + * + * @param {HTMLElement} from + * @param {HTMLElement} to + */ + function autoAnimateElement( from, to ) { + + var fromProps = getAutoAnimatableProperties( from ), + toProps = getAutoAnimatableProperties( to ); + + var delta = { + x: fromProps.x - toProps.x, + y: fromProps.y - toProps.y, + scaleX: fromProps.width / toProps.width, + scaleY: fromProps.height / toProps.height + }; + + to.style.transition = 'none'; + to.style.transform = 'translate('+delta.x+'px, '+delta.y+'px) scale('+delta.scaleX+','+delta.scaleY+')'; + to.classList.add( 'auto-animate-target' ); + + config.autoAnimateStyles.forEach( function( propertyName ) { + to.style[propertyName] = fromProps[propertyName]; + } ); + + setTimeout( function() { + + // Run the FLIP animation + to.style.transition = ''; + to.style.transform = ''; + + config.autoAnimateStyles.forEach( function( propertyName ) { + to.style[propertyName] = toProps[propertyName]; + } ); + + }, 1 ); + + } + + /** + * Returns an object containing all of the properties + * that can be auto-animated for the given element + * and their respective values. + */ + function getAutoAnimatableProperties( element ) { + + var properties = element._animatableProperties; + + if( !properties ) { + + properties = {}; + + // Position and size + properties.x = element.offsetLeft; + properties.y = element.offsetTop; + properties.width = element.offsetWidth; + properties.height = element.offsetHeight; + + // Styles + config.autoAnimateStyles.forEach( function( propertyName ) { + properties[propertyName] = element.style[propertyName]; + } ); + + // Cache the list of properties + element._animatableProperties = properties; + + // Provide a method for rolling back this element to its + // pre auto-animated state. + autoAnimatedRollbacks.push( function( originalStyleAttribute ) { + element.setAttribute( 'style', originalStyleAttribute ); + element.classList.remove( 'auto-animate-target' ); + delete element._animatableProperties; + }.bind( null, element.getAttribute( 'style' ) ) ); + + } + + return properties; + + } + /** * Should the given element be preloaded? * Decides based on local element attributes and global config. diff --git a/test/examples/auto-animate.html b/test/examples/auto-animate.html new file mode 100644 index 00000000..87293d3a --- /dev/null +++ b/test/examples/auto-animate.html @@ -0,0 +1,83 @@ + + + + + + + reveal.js - Auto Animate + + + + + + + + + +
+ +
+ +
+

Opacity 1.0

+
+
+

Opacity 0.2

+
+ +
+

SLIDE 1

+

Animate Anything

+
+
+
+
+ +
+
+

SLIDE 2

+

With Auto Animate

+
+
+
+
+ +
+
+

SLIDE 3

+

With Auto Animate

+
+
+
+
+ +
+
+

SLIDE 3

+

With Auto Animate

+
+
+
+
+ +
+ +
+ +
+ + + + + + +