Browse Source

initial work for auto-animate

Hakim El Hattab 5 years ago
parent
commit
8d89db32f6
4 changed files with 247 additions and 1 deletions
  1. 11 0
      css/reveal.css
  2. 15 0
      css/reveal.scss
  3. 138 1
      js/reveal.js
  4. 83 0
      test/examples/auto-animate.html

+ 11 - 0
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
  *********************************************/

+ 15 - 0
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
  *********************************************/

+ 138 - 1
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.

+ 83 - 0
test/examples/auto-animate.html

@@ -0,0 +1,83 @@
+<!doctype html>
+<html lang="en">
+
+	<head>
+		<meta charset="utf-8">
+
+		<title>reveal.js - Auto Animate</title>
+
+		<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
+
+		<link rel="stylesheet" href="../../css/reveal.css">
+		<link rel="stylesheet" href="../../css/theme/black.css" id="theme">
+	</head>
+
+	<body>
+
+		<div class="reveal">
+
+			<div class="slides">
+
+				<section data-auto-animate>
+					<h3 data-id="opacity-test">Opacity 1.0</h2>
+				</section>
+				<section data-auto-animate>
+					<h3 data-id="opacity-test" style="opacity: 0.2;">Opacity 0.2</h2>
+				</section>
+
+				<section data-auto-animate style="height: 600px">
+					<h3 style="opacity: 0.3; font-size: 18px;">SLIDE 1</h2>
+					<h2 data-id="title" style="margin-top: 260px;">Animate Anything</h2>
+					<div data-id="1" style="background: white; position: absolute; top: 150px; left: 16%; width: 60px; height: 60px;"></div>
+					<div data-id="2" style="background: white; position: absolute; top: 150px; left: 36%; width: 60px; height: 60px;"></div>
+					<div data-id="3" style="background: white; position: absolute; top: 150px; left: 56%; width: 60px; height: 60px;"></div>
+					<div data-id="4" style="background: white; position: absolute; top: 150px; left: 76%; width: 60px; height: 60px;"></div>
+					<!-- <p data-id="2" style="margin-top: 200px; border: 2px solid #999;">Text</p> -->
+				</section>
+				<section data-auto-animate style="height: 600px">
+					<h3 style="opacity: 0.3; font-size: 18px;">SLIDE 2</h2>
+					<h2 data-id="title" style="margin-top: 500px">With Auto Animate</h2>
+					<div data-id="1" style="background: cyan; position: absolute; bottom: 190px; left: 16%; width: 60px; height: 60px;"></div>
+					<div data-id="2" style="background: magenta; position: absolute; bottom: 190px; left: 36%; width: 60px; height: 160px;"></div>
+					<div data-id="3" style="background: yellow; position: absolute; bottom: 190px; left: 56%; width: 60px; height: 260px;"></div>
+					<div data-id="4" style="background: red; position: absolute; bottom: 190px; left: 76%; width: 60px; height: 360px;"></div>
+					<!-- <p data-id="2" style="margin-top: 200px; border: 2px solid #999; width: 50%;">Text</p> -->
+				</section>
+				<section data-auto-animate style="height: 600px">
+					<h3 style="opacity: 0.3; font-size: 18px;">SLIDE 3</h2>
+					<h2 data-id="title" style="margin-top: 500px; opacity: 0;">With Auto Animate</h2>
+					<div data-id="1" style="background: cyan; position: absolute; top: 50%; left: 50%; width: 400px; height: 400px; margin: -200px 0 0 -200px; border-radius: 400px;"></div>
+					<div data-id="2" style="background: magenta; position: absolute; top: 50%; left: 50%; width: 300px; height: 300px; margin: -150px 0 0 -150px; border-radius: 400px;"></div>
+					<div data-id="3" style="background: yellow; position: absolute; top: 50%; left: 50%; width: 200px; height: 200px; margin: -100px 0 0 -100px; border-radius: 400px;"></div>
+					<div data-id="4" style="background: red; position: absolute; top: 50%; left: 50%; width: 100px; height: 100px; margin: -50px 0 0 -50px; border-radius: 400px;"></div>
+					<!-- <p data-id="2" style="margin-top: 200px; border: 2px solid #999;">Text</p> -->
+				</section>
+				<section data-auto-animate style="height: 600px">
+					<h3 style="opacity: 0.3; font-size: 18px;">SLIDE 3</h2>
+					<h2 data-id="title" style="margin-top: 500px; opacity: 0;">With Auto Animate</h2>
+					<div data-id="1" style="background: red; position: absolute; top: 250px; left: 16%; width: 60px; height: 60px;"></div>
+					<div data-id="2" style="background: yellow; position: absolute; top: 250px; left: 36%; width: 60px; height: 60px;"></div>
+					<div data-id="3" style="background: magenta; position: absolute; top: 250px; left: 56%; width: 60px; height: 60px;"></div>
+					<div data-id="4" style="background: cyan; position: absolute; top: 250px; left: 76%; width: 60px; height: 60px;"></div>
+					<!-- <p data-id="2" style="margin-top: 200px; border: 2px solid #999;">Text</p> -->
+				</section>
+
+			</div>
+
+		</div>
+
+		<script src="../../js/reveal.js"></script>
+
+		<script>
+
+			// Full list of configuration options available here:
+			// https://github.com/hakimel/reveal.js#configuration
+			Reveal.initialize({
+				center: true,
+				hash: true
+			});
+
+		</script>
+
+	</body>
+</html>