251 lines
6.7 KiB
JavaScript
251 lines
6.7 KiB
JavaScript
/**
|
|
* animOnScroll.js v1.0.0
|
|
* http://www.codrops.com
|
|
*
|
|
* Licensed under the MIT license.
|
|
* http://www.opensource.org/licenses/mit-license.php
|
|
*
|
|
* Copyright 2013, Codrops
|
|
* http://www.codrops.com
|
|
*/
|
|
;( function( window ) {
|
|
|
|
'use strict';
|
|
|
|
var docElem = window.document.documentElement;
|
|
|
|
function classReg( className ) {
|
|
return new RegExp("(^|\\s+)" + className + "(\\s+|$)");
|
|
}
|
|
|
|
// classList support for class management
|
|
// altho to be fair, the api sucks because it won't accept multiple classes at once
|
|
var hasClass, addClass, removeClass;
|
|
|
|
if ( 'classList' in document.documentElement ) {
|
|
hasClass = function( elem, c ) {
|
|
return elem.classList.contains( c );
|
|
};
|
|
addClass = function( elem, c ) {
|
|
elem.classList.add( c );
|
|
};
|
|
removeClass = function( elem, c ) {
|
|
elem.classList.remove( c );
|
|
};
|
|
}
|
|
else {
|
|
hasClass = function( elem, c ) {
|
|
return classReg( c ).test( elem.className );
|
|
};
|
|
addClass = function( elem, c ) {
|
|
if ( !hasClass( elem, c ) ) {
|
|
elem.className = elem.className + ' ' + c;
|
|
}
|
|
};
|
|
removeClass = function( elem, c ) {
|
|
elem.className = elem.className.replace( classReg( c ), ' ' );
|
|
};
|
|
}
|
|
|
|
function toggleClass( elem, c ) {
|
|
var fn = hasClass( elem, c ) ? removeClass : addClass;
|
|
fn( elem, c );
|
|
}
|
|
|
|
var classie = {
|
|
// full names
|
|
hasClass: hasClass,
|
|
addClass: addClass,
|
|
removeClass: removeClass,
|
|
toggleClass: toggleClass,
|
|
// short names
|
|
has: hasClass,
|
|
add: addClass,
|
|
remove: removeClass,
|
|
toggle: toggleClass
|
|
};
|
|
|
|
// transport
|
|
if ( typeof define === 'function' && define.amd ) {
|
|
// AMD
|
|
define( classie );
|
|
} else {
|
|
// browser global
|
|
window.classie = classie;
|
|
}
|
|
|
|
function getViewportH() {
|
|
var client = docElem['clientHeight'],
|
|
inner = window['innerHeight'];
|
|
|
|
if( client < inner )
|
|
return inner;
|
|
else
|
|
return client;
|
|
}
|
|
|
|
function scrollY() {
|
|
return window.pageYOffset || docElem.scrollTop;
|
|
}
|
|
|
|
// http://stackoverflow.com/a/5598797/989439
|
|
function getOffset( el ) {
|
|
var offsetTop = 0, offsetLeft = 0;
|
|
do {
|
|
if ( !isNaN( el.offsetTop ) ) {
|
|
offsetTop += el.offsetTop;
|
|
}
|
|
if ( !isNaN( el.offsetLeft ) ) {
|
|
offsetLeft += el.offsetLeft;
|
|
}
|
|
} while( el = el.offsetParent )
|
|
|
|
return {
|
|
top : offsetTop,
|
|
left : offsetLeft
|
|
}
|
|
}
|
|
|
|
function inViewport( el, h ) {
|
|
var elH = el.offsetHeight,
|
|
scrolled = scrollY(),
|
|
viewed = scrolled + getViewportH(),
|
|
elTop = getOffset(el).top,
|
|
elBottom = elTop + elH,
|
|
// if 0, the element is considered in the viewport as soon as it enters.
|
|
// if 1, the element is considered in the viewport only when it's fully inside
|
|
// value in percentage (1 >= h >= 0)
|
|
h = h || 0;
|
|
|
|
return (elTop + elH * h) <= viewed && (elBottom - elH * h) >= scrolled;
|
|
}
|
|
|
|
function extend( a, b ) {
|
|
for( var key in b ) {
|
|
if( b.hasOwnProperty( key ) ) {
|
|
a[key] = b[key];
|
|
}
|
|
}
|
|
return a;
|
|
}
|
|
|
|
function AnimOnScroll( el, options, masonryOptions ) {
|
|
this.el = el;
|
|
this.options = extend( this.defaults, options );
|
|
this.masonryOptions = masonryOptions;
|
|
this._init();
|
|
}
|
|
|
|
AnimOnScroll.prototype = {
|
|
defaults : {
|
|
// Minimum and a maximum duration of the animation (random value is chosen)
|
|
minDuration : 0,
|
|
maxDuration : 0,
|
|
// The viewportFactor defines how much of the appearing item has to be visible in order to trigger the animation
|
|
// if we'd use a value of 0, this would mean that it would add the animation class as soon as the item is in the viewport.
|
|
// If we were to use the value of 1, the animation would only be triggered when we see all of the item in the viewport (100% of it)
|
|
viewportFactor : 0,
|
|
animateVisible : false
|
|
},
|
|
_init : function() {
|
|
this.items = Array.prototype.slice.call( jQuery(this.el).find('.masonry-item') );
|
|
this.itemsCount = this.items.length;
|
|
this.itemsRenderedCount = 0;
|
|
this.didScroll = false;
|
|
|
|
var self = this;
|
|
|
|
imagesLoaded( this.el, function() {
|
|
|
|
// initialize masonry
|
|
if (self.masonryOptions){
|
|
new Masonry( self.el, self.masonryOptions );
|
|
}
|
|
|
|
if( Modernizr.cssanimations ) {
|
|
// the items already shown...
|
|
self.items.forEach( function( el, i ) {
|
|
if( inViewport( el ) ) {
|
|
self._checkTotalRendered();
|
|
|
|
//fade item after init
|
|
if (self.options.animateVisible){
|
|
setTimeout( function() {
|
|
self._scrollPage();
|
|
}, 500 );
|
|
} else {
|
|
//show item on init
|
|
classie.add( el, 'shown' );
|
|
}
|
|
}
|
|
} );
|
|
|
|
// animate on scroll the items inside the viewport
|
|
window.addEventListener( 'scroll', function() {
|
|
self._onScrollFn();
|
|
}, false );
|
|
window.addEventListener( 'resize', function() {
|
|
self._resizeHandler();
|
|
}, false );
|
|
}
|
|
|
|
});
|
|
},
|
|
_onScrollFn : function() {
|
|
var self = this;
|
|
if( !this.didScroll ) {
|
|
this.didScroll = true;
|
|
setTimeout( function() { self._scrollPage(); }, 60 );
|
|
}
|
|
},
|
|
_scrollPage : function() {
|
|
var self = this;
|
|
this.items.forEach( function( el, i ) {
|
|
if( !classie.has( el, 'shown' ) && !classie.has( el, 'animate' ) && inViewport( el, self.options.viewportFactor ) ) {
|
|
setTimeout( function() {
|
|
var el_offset = $( self.el ).offset();
|
|
var el_scrollY = (parseFloat(scrollY() - el_offset.top) < 0) ? 0 : (scrollY() - el_offset.top);
|
|
var perspY = el_scrollY + getViewportH() / 2;
|
|
self.el.style.WebkitPerspectiveOrigin = '50% ' + perspY + 'px';
|
|
self.el.style.MozPerspectiveOrigin = '50% ' + perspY + 'px';
|
|
self.el.style.perspectiveOrigin = '50% ' + perspY + 'px';
|
|
|
|
self._checkTotalRendered();
|
|
|
|
if( self.options.minDuration && self.options.maxDuration ) {
|
|
var randDuration = ( Math.random() * ( self.options.maxDuration - self.options.minDuration ) + self.options.minDuration ) + 's';
|
|
el.style.WebkitAnimationDuration = randDuration;
|
|
el.style.MozAnimationDuration = randDuration;
|
|
el.style.animationDuration = randDuration;
|
|
}
|
|
|
|
classie.add( el, 'animate' );
|
|
}, 25 );
|
|
}
|
|
});
|
|
this.didScroll = false;
|
|
},
|
|
_resizeHandler : function() {
|
|
var self = this;
|
|
function delayed() {
|
|
self._scrollPage();
|
|
self.resizeTimeout = null;
|
|
}
|
|
if ( this.resizeTimeout ) {
|
|
clearTimeout( this.resizeTimeout );
|
|
}
|
|
this.resizeTimeout = setTimeout( delayed, 1000 );
|
|
},
|
|
_checkTotalRendered : function() {
|
|
++this.itemsRenderedCount;
|
|
if( this.itemsRenderedCount === this.itemsCount ) {
|
|
window.removeEventListener( 'scroll', this._onScrollFn );
|
|
}
|
|
}
|
|
}
|
|
|
|
// add to global namespace
|
|
window.AnimOnScroll = AnimOnScroll;
|
|
|
|
} )( window );
|