From 15e6994569835d1e00083c57aa4decb9521e8857 Mon Sep 17 00:00:00 2001 From: Hakim El Hattab Date: Wed, 12 Feb 2020 14:05:23 +0100 Subject: [PATCH] support for fading in unmatched auto-animate elements --- css/reveal.css | 9 ++++ css/reveal.scss | 12 +++++ js/reveal.js | 81 +++++++++++++++++++++++++++++---- test/examples/auto-animate.html | 2 +- 4 files changed, 94 insertions(+), 10 deletions(-) diff --git a/css/reveal.css b/css/reveal.css index a98a752..2d19d61 100644 --- a/css/reveal.css +++ b/css/reveal.css @@ -1226,6 +1226,15 @@ body { .reveal[data-transition-speed="slow"] > .backgrounds .slide-background { transition-duration: 1200ms; } +/********************************************* + * AUTO ANIMATE + *********************************************/ +.reveal section[data-auto-animate] [data-auto-animate-unmatched="fade-in"] { + opacity: 0; } + +.reveal section[data-auto-animate="running"] [data-auto-animate-unmatched="fade-in"] { + opacity: 1; } + /********************************************* * OVERVIEW *********************************************/ diff --git a/css/reveal.scss b/css/reveal.scss index 335b96b..1d14a02 100644 --- a/css/reveal.scss +++ b/css/reveal.scss @@ -1310,6 +1310,18 @@ $controlsArrowAngleActive: 36deg; } +/********************************************* + * AUTO ANIMATE + *********************************************/ + +.reveal section[data-auto-animate] [data-auto-animate-unmatched="fade-in"] { + opacity: 0; +} +.reveal section[data-auto-animate="running"] [data-auto-animate-unmatched="fade-in"] { + opacity: 1; +} + + /********************************************* * OVERVIEW *********************************************/ diff --git a/js/reveal.js b/js/reveal.js index 1bb9842..765ce3d 100644 --- a/js/reveal.js +++ b/js/reveal.js @@ -1431,9 +1431,8 @@ toArray( dom.slides.querySelectorAll( '[data-auto-animate]:not([data-auto-animate=""])' ) ).forEach( function( element ) { element.dataset.autoAnimate = ''; } ); - toArray( dom.wrapper.querySelectorAll( '[data-auto-animate-target]' ) ).forEach( function( element ) { - delete element.dataset.autoAnimateTarget; - } ); + + removeEphemeralAutoAnimateAttributes(); if( autoAnimateStyleSheet && autoAnimateStyleSheet.parentNode ) { autoAnimateStyleSheet.parentNode.removeChild( autoAnimateStyleSheet ); @@ -3849,10 +3848,8 @@ autoAnimateStyleSheet.innerHTML = ''; } - // Clean up from previous animations - toArray( document.querySelectorAll( '[data-auto-animate-target]' ) ).forEach( function( element ) { - delete element.dataset.autoAnimateTarget; - } ); + // Clean up after prior animations + removeEphemeralAutoAnimateAttributes(); var slideOptions = getAutoAnimateOptions( toSlide, { @@ -3868,9 +3865,21 @@ toSlide.dataset.autoAnimate = 'pending'; // Inject our auto-animate styles for this transition - autoAnimateStyleSheet.innerHTML = getAutoAnimatableElements( fromSlide, toSlide ).map( function( elements ) { + var css = getAutoAnimatableElements( fromSlide, toSlide ).map( function( elements ) { return getAutoAnimateCSS( elements.from, elements.to, elements.options || {}, slideOptions, autoAnimateCounter++ ); - } ).join( '' ); + } ); + + // If the slide is configured to animate unmatched elements we + // need to flag them + if( toSlide.dataset.autoAnimateUnmatched ) { + getUnmatchedAutoAnimateElements( toSlide ).forEach( function( unmatchedElement ) { + unmatchedElement.dataset.autoAnimateUnmatched = 'fade-in'; + } ); + + css.push( '.reveal [data-auto-animate="running"] [data-auto-animate-unmatched] { transition: all '+ (slideOptions.duration*0.8) +'s ease '+ (slideOptions.duration*0.2) +'s; }' ); + } + + autoAnimateStyleSheet.innerHTML = css.join( '' ); // Start the animation next cycle requestAnimationFrame( function() { @@ -3879,6 +3888,22 @@ } + /** + * Removes all attributes that we temporarily add to slide + * elements in order to carry out auto-animation. + */ + function removeEphemeralAutoAnimateAttributes() { + + toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ':not(.stack) [data-auto-animate-target]' ) ).forEach( function( element ) { + delete element.dataset.autoAnimateTarget; + } ); + + toArray( dom.wrapper.querySelectorAll( SLIDES_SELECTOR + ':not(.stack) [data-auto-animate-unmatched]' ) ).forEach( function( element ) { + delete element.dataset.autoAnimateUnmatched; + } ); + + } + /** * Auto-animates the properties of an element from their original * values to their new state. @@ -4139,6 +4164,44 @@ } + /** + * Returns a all elements within the given scope that should + * be considered unmatched in an auto-animate transition. If + * fading of unmatched elements is turnded on, these elements + * will fade when going between auto-aniamted slides. + * + * Note that parents of auto-animate targets are NOT considerd + * unmatched since fading them would break the auto-animation. + * + * @param {HTMLElement} rootElement + * @return {Array} + */ + function getUnmatchedAutoAnimateElements( rootElement ) { + + return [].slice.call( rootElement.children ).reduce( function( result, element ) { + + // If the element is auto-animated we can stop looking at this tree + if( !element.hasAttribute( 'data-auto-animate-target' ) ) { + + // If this element contains an auto-animated element it's considered + // a match since we can't fade it without affecting the inner + // auto-animate target + if( !element.querySelector( '[data-auto-animate-target]' ) ) { + result.push( element ); + } + else { + // Keep looking down this tree + result = result.concat( getUnmatchedAutoAnimateElements( element ) ); + } + + } + + return result; + + }, [] ); + + } + /** * 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 index bb982f5..a70e2d2 100644 --- a/test/examples/auto-animate.html +++ b/test/examples/auto-animate.html @@ -19,7 +19,7 @@
-
+

Auto-Animate Example

This will fade out