in js/controllers/autoanimate.js [158:296]
autoAnimateElements( from, to, elementOptions, animationOptions, id ) {
// 'from' elements are given a data-auto-animate-target with no value,
// 'to' elements are are given a data-auto-animate-target with an ID
from.dataset.autoAnimateTarget = '';
to.dataset.autoAnimateTarget = id;
// Each element may override any of the auto-animate options
// like transition easing, duration and delay via data-attributes
let options = this.getAutoAnimateOptions( to, animationOptions );
// If we're using a custom element matcher the element options
// may contain additional transition overrides
if( typeof elementOptions.delay !== 'undefined' ) options.delay = elementOptions.delay;
if( typeof elementOptions.duration !== 'undefined' ) options.duration = elementOptions.duration;
if( typeof elementOptions.easing !== 'undefined' ) options.easing = elementOptions.easing;
let fromProps = this.getAutoAnimatableProperties( 'from', from, elementOptions ),
toProps = this.getAutoAnimatableProperties( 'to', to, elementOptions );
// Maintain fragment visibility for matching elements when
// we're navigating forwards, this way the viewer won't need
// to step through the same fragments twice
if( to.classList.contains( 'fragment' ) ) {
// Don't auto-animate the opacity of fragments to avoid
// conflicts with fragment animations
delete toProps.styles['opacity'];
if( from.classList.contains( 'fragment' ) ) {
let fromFragmentStyle = ( from.className.match( FRAGMENT_STYLE_REGEX ) || [''] )[0];
let toFragmentStyle = ( to.className.match( FRAGMENT_STYLE_REGEX ) || [''] )[0];
// Only skip the fragment if the fragment animation style
// remains unchanged
if( fromFragmentStyle === toFragmentStyle && animationOptions.slideDirection === 'forward' ) {
to.classList.add( 'visible', 'disabled' );
}
}
}
// If translation and/or scaling are enabled, css transform
// the 'to' element so that it matches the position and size
// of the 'from' element
if( elementOptions.translate !== false || elementOptions.scale !== false ) {
let presentationScale = this.Reveal.getScale();
let delta = {
x: ( fromProps.x - toProps.x ) / presentationScale,
y: ( fromProps.y - toProps.y ) / presentationScale,
scaleX: fromProps.width / toProps.width,
scaleY: fromProps.height / toProps.height
};
// Limit decimal points to avoid 0.0001px blur and stutter
delta.x = Math.round( delta.x * 1000 ) / 1000;
delta.y = Math.round( delta.y * 1000 ) / 1000;
delta.scaleX = Math.round( delta.scaleX * 1000 ) / 1000;
delta.scaleX = Math.round( delta.scaleX * 1000 ) / 1000;
let translate = elementOptions.translate !== false && ( delta.x !== 0 || delta.y !== 0 ),
scale = elementOptions.scale !== false && ( delta.scaleX !== 0 || delta.scaleY !== 0 );
// No need to transform if nothing's changed
if( translate || scale ) {
let transform = [];
if( translate ) transform.push( `translate(${delta.x}px, ${delta.y}px)` );
if( scale ) transform.push( `scale(${delta.scaleX}, ${delta.scaleY})` );
fromProps.styles['transform'] = transform.join( ' ' );
fromProps.styles['transform-origin'] = 'top left';
toProps.styles['transform'] = 'none';
}
}
// Delete all unchanged 'to' styles
for( let propertyName in toProps.styles ) {
const toValue = toProps.styles[propertyName];
const fromValue = fromProps.styles[propertyName];
if( toValue === fromValue ) {
delete toProps.styles[propertyName];
}
else {
// If these property values were set via a custom matcher providing
// an explicit 'from' and/or 'to' value, we always inject those values.
if( toValue.explicitValue === true ) {
toProps.styles[propertyName] = toValue.value;
}
if( fromValue.explicitValue === true ) {
fromProps.styles[propertyName] = fromValue.value;
}
}
}
let css = '';
let toStyleProperties = Object.keys( toProps.styles );
// Only create animate this element IF at least one style
// property has changed
if( toStyleProperties.length > 0 ) {
// Instantly move to the 'from' state
fromProps.styles['transition'] = 'none';
// Animate towards the 'to' state
toProps.styles['transition'] = `all ${options.duration}s ${options.easing} ${options.delay}s`;
toProps.styles['transition-property'] = toStyleProperties.join( ', ' );
toProps.styles['will-change'] = toStyleProperties.join( ', ' );
// Build up our custom CSS. We need to override inline styles
// so we need to make our styles vErY IMPORTANT!1!!
let fromCSS = Object.keys( fromProps.styles ).map( propertyName => {
return propertyName + ': ' + fromProps.styles[propertyName] + ' !important;';
} ).join( '' );
let toCSS = Object.keys( toProps.styles ).map( propertyName => {
return propertyName + ': ' + toProps.styles[propertyName] + ' !important;';
} ).join( '' );
css = '[data-auto-animate-target="'+ id +'"] {'+ fromCSS +'}' +
'[data-auto-animate="running"] [data-auto-animate-target="'+ id +'"] {'+ toCSS +'}';
}
return css;
}