1 2/** 3 * animOnScroll.js v1.0.0 4 * http://www.codrops.com 5 * 6 * Licensed under the MIT license. 7 * http://www.opensource.org/licenses/mit-license.php 8 * 9 * Copyright 2013, Codrops 10 * http://www.codrops.com 11 */ 12 13 14 /*! 15 * imagesLoaded v3.0.2 16 * JavaScript is all like "You images are done yet or what?" 17 */ 18 19 ( function( window ) { 20 21 'use strict'; 22 23 var $ = window.jQuery; 24 var console = window.console; 25 var hasConsole = typeof console !== 'undefined'; 26 27 // -------------------------- helpers -------------------------- // 28 29 // extend objects 30 function extend( a, b ) { 31 for ( var prop in b ) { 32 a[ prop ] = b[ prop ]; 33 } 34 return a; 35 } 36 37 var objToString = Object.prototype.toString; 38 function isArray( obj ) { 39 return objToString.call( obj ) === '[object Array]'; 40 } 41 42 // turn element or nodeList into an array 43 function makeArray( obj ) { 44 var ary = []; 45 if ( isArray( obj ) ) { 46 // use object if already an array 47 ary = obj; 48 } else if ( typeof obj.length === 'number' ) { 49 // convert nodeList to array 50 for ( var i=0, len = obj.length; i < len; i++ ) { 51 ary.push( obj[i] ); 52 } 53 } else { 54 // array of single index 55 ary.push( obj ); 56 } 57 return ary; 58 } 59 60 // -------------------------- -------------------------- // 61 62 function defineImagesLoaded( EventEmitter, eventie ) { 63 64 /** 65 * @param {Array, Element, NodeList, String} elem 66 * @param {Object or Function} options - if function, use as callback 67 * @param {Function} onAlways - callback function 68 */ 69 function ImagesLoaded( elem, options, onAlways ) { 70 // coerce ImagesLoaded() without new, to be new ImagesLoaded() 71 if ( !( this instanceof ImagesLoaded ) ) { 72 return new ImagesLoaded( elem, options ); 73 } 74 // use elem as selector string 75 if ( typeof elem === 'string' ) { 76 elem = document.querySelectorAll( elem ); 77 } 78 79 this.elements = makeArray( elem ); 80 this.options = extend( {}, this.options ); 81 82 if ( typeof options === 'function' ) { 83 onAlways = options; 84 } else { 85 extend( this.options, options ); 86 } 87 88 if ( onAlways ) { 89 this.on( 'always', onAlways ); 90 } 91 92 this.getImages(); 93 94 if ( $ ) { 95 // add jQuery Deferred object 96 this.jqDeferred = new $.Deferred(); 97 } 98 99 // HACK check async to allow time to bind listeners 100 var _this = this; 101 setTimeout( function() { 102 _this.check(); 103 }); 104 } 105 106 ImagesLoaded.prototype = new EventEmitter(); 107 108 ImagesLoaded.prototype.options = {}; 109 110 ImagesLoaded.prototype.getImages = function() { 111 this.images = []; 112 113 // filter & find items if we have an item selector 114 for ( var i=0, len = this.elements.length; i < len; i++ ) { 115 var elem = this.elements[i]; 116 // filter siblings 117 if ( elem.nodeName === 'IMG' ) { 118 this.addImage( elem ); 119 } 120 // find children 121 var childElems = elem.querySelectorAll('img'); 122 // concat childElems to filterFound array 123 for ( var j=0, jLen = childElems.length; j < jLen; j++ ) { 124 var img = childElems[j]; 125 this.addImage( img ); 126 } 127 } 128 }; 129 130 /** 131 * @param {Image} img 132 */ 133 ImagesLoaded.prototype.addImage = function( img ) { 134 var loadingImage = new LoadingImage( img ); 135 this.images.push( loadingImage ); 136 }; 137 138 ImagesLoaded.prototype.check = function() { 139 var _this = this; 140 var checkedCount = 0; 141 var length = this.images.length; 142 this.hasAnyBroken = false; 143 // complete if no images 144 if ( !length ) { 145 this.complete(); 146 return; 147 } 148 149 function onConfirm( image, message ) { 150 if ( _this.options.debug && hasConsole ) { 151 console.log( 'confirm', image, message ); 152 } 153 154 _this.progress( image ); 155 checkedCount++; 156 if ( checkedCount === length ) { 157 _this.complete(); 158 } 159 return true; // bind once 160 } 161 162 for ( var i=0; i < length; i++ ) { 163 var loadingImage = this.images[i]; 164 loadingImage.on( 'confirm', onConfirm ); 165 loadingImage.check(); 166 } 167 }; 168 169 ImagesLoaded.prototype.progress = function( image ) { 170 this.hasAnyBroken = this.hasAnyBroken || !image.isLoaded;
171 this.emit( 'progress', this, image ); 172 if ( this.jqDeferred ) { 173 this.jqDeferred.notify( this, image ); 174 } 175 }; 176 177 ImagesLoaded.prototype.complete = function() { 178 var eventName = this.hasAnyBroken ? 'fail' : 'done'; 179 this.isComplete = true; 180 this.emit( eventName, this ); 181 this.emit( 'always', this ); 182 if ( this.jqDeferred ) { 183 var jqMethod = this.hasAnyBroken ? 'reject' : 'resolve'; 184 this.jqDeferred[ jqMethod ]( this ); 185 } 186 }; 187 188 // -------------------------- jquery -------------------------- // 189 190 if ( $ ) { 191 $.fn.imagesLoaded = function( options, callback ) { 192 var instance = new ImagesLoaded( this, options, callback ); 193 return instance.jqDeferred.promise( $(this) ); 194 }; 195 } 196 197 198 // -------------------------- -------------------------- // 199 200 var cache = {}; 201 202 function LoadingImage( img ) { 203 this.img = img; 204 } 205 206 LoadingImage.prototype = new EventEmitter(); 207 208 LoadingImage.prototype.check = function() { 209 // first check cached any previous images that have same src 210 var cached = cache[ this.img.src ]; 211 if ( cached ) { 212 this.useCached( cached ); 213 return; 214 } 215 // add this to cache 216 cache[ this.img.src ] = this; 217 218 // If complete is true and browser supports natural sizes, 219 // try to check for image status manually. 220 if ( this.img.complete && this.img.naturalWidth !== undefined ) { 221 // report based on naturalWidth 222 this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' ); 223 return; 224 } 225 226 // If none of the checks above matched, simulate loading on detached element. 227 var proxyImage = this.proxyImage = new Image(); 228 eventie.bind( proxyImage, 'load', this ); 229 eventie.bind( proxyImage, 'error', this ); 230 proxyImage.src = this.img.src; 231 }; 232 233 LoadingImage.prototype.useCached = function( cached ) { 234 if ( cached.isConfirmed ) { 235 this.confirm( cached.isLoaded, 'cached was confirmed' ); 236 } else { 237 var _this = this; 238 cached.on( 'confirm', function( image ) { 239 _this.confirm( image.isLoaded, 'cache emitted confirmed' ); 240 return true; // bind once 241 }); 242 } 243 }; 244 245 LoadingImage.prototype.confirm = function( isLoaded, message ) { 246 this.isConfirmed = true; 247 this.isLoaded = isLoaded; 248 this.emit( 'confirm', this, message ); 249 }; 250 251 // trigger specified handler for event type 252 LoadingImage.prototype.handleEvent = function( event ) { 253 var method = 'on' + event.type; 254 if ( this[ method ] ) { 255 this[ method ]( event ); 256 } 257 }; 258 259 LoadingImage.prototype.onload = function() { 260 this.confirm( true, 'onload' ); 261 this.unbindProxyEvents(); 262 }; 263 264 LoadingImage.prototype.onerror = function() { 265 this.confirm( false, 'onerror' ); 266 this.unbindProxyEvents(); 267 }; 268 269 LoadingImage.prototype.unbindProxyEvents = function() { 270 eventie.unbind( this.proxyImage, 'load', this ); 271 eventie.unbind( this.proxyImage, 'error', this ); 272 }; 273 274 // ----- ----- // 275 276 return ImagesLoaded; 277 } 278 279 // -------------------------- transport -------------------------- // 280 281 if ( typeof define === 'function' && define.amd ) { 282 // AMD 283 define( [ 284 'eventEmitter', 285 'eventie' 286 ], 287 defineImagesLoaded ); 288 } else { 289 // browser global 290 window.imagesLoaded = defineImagesLoaded( 291 window.EventEmitter, 292 window.eventie 293 ); 294 } 295 296 })( window ); 297 298 299;( function( window ) { 300 301 'use strict'; 302 303 var docElem = window.document.documentElement; 304 305 function getViewportH() { 306 var client = docElem['clientHeight'], 307 inner = window['innerHeight']; 308 309 if( client < inner ) 310 return inner; 311 else 312 return client; 313 } 314 315 function scrollY() { 316 return window.pageYOffset || docElem.scrollTop; 317 } 318 319 // http://stackoverflow.com/a/5598797/989439 320 function getOffset( el ) { 321 var offsetTop = 0, offsetLeft = 0; 322 do { 323 if ( !isNaN( el.offsetTop ) ) { 324 offsetTop += el.offsetTop; 325 } 326 if ( !isNaN( el.offsetLeft ) ) { 327 offsetLeft += el.offsetLeft; 328 } 329 } while( el = el.offsetParent ) 330 331 return { 332 top : offsetTop, 333 left : offsetLeft 334 } 335 } 336 337 function inViewport( el, h ) {
338 var elH = el.offsetHeight, 339 scrolled = scrollY(), 340 viewed = scrolled + getViewportH(), 341 elTop = getOffset(el).top, 342 elBottom = elTop + elH, 343 // if 0, the element is considered in the viewport as soon as it enters. 344 // if 1, the element is considered in the viewport only when it's fully inside 345 // value in percentage (1 >= h >= 0) 346 h = h || 0; 347 348 return (elTop + elH * h) <= viewed && (elBottom - elH * h) >= scrolled; 349 } 350 351 function extend( a, b ) { 352 for( var key in b ) { 353 if( b.hasOwnProperty( key ) ) { 354 a[key] = b[key]; 355 } 356 } 357 return a; 358 } 359 360 function AnimOnScroll( el, options ) { 361 this.el = el; 362 this.options = extend( this.defaults, options ); 363 this._init(); 364 } 365 366 AnimOnScroll.prototype = { 367 defaults : { 368 // Minimum and a maximum duration of the animation (random value is chosen) 369 minDuration : 0, 370 maxDuration : 0, 371 // The viewportFactor defines how much of the appearing item has to be visible in order to trigger the animation 372 // 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. 373 // 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) 374 viewportFactor : 0 375 }, 376 _init : function() { 377 this.items = Array.prototype.slice.call( document.querySelectorAll( '#' + this.el.id + ' > li' ) ); 378 this.itemsCount = this.items.length; 379 this.itemsRenderedCount = 0; 380 this.didScroll = false; 381 382 var self = this; 383 384 imagesLoaded( this.el, function() { 385 386 // initialize masonry 387 new Masonry( self.el, { 388 itemSelector: 'li', 389 transitionDuration : 0 390 } ); 391 392 if( Modernizr.cssanimations ) { 393 // the items already shown... 394 self.items.forEach( function( el, i ) { 395 if( inViewport( el ) ) { 396 self._checkTotalRendered(); 397 classie.add( el, 'shown' ); 398 } 399 } ); 400 401 // animate on scroll the items inside the viewport 402 window.addEventListener( 'scroll', function() { 403 self._onScrollFn(); 404 }, false ); 405 window.addEventListener( 'resize', function() { 406 self._resizeHandler(); 407 }, false ); 408 } 409 410 }); 411 }, 412 _onScrollFn : function() { 413 var self = this; 414 if( !this.didScroll ) { 415 this.didScroll = true; 416 setTimeout( function() { self._scrollPage(); }, 60 ); 417 } 418 }, 419 _scrollPage : function() { 420 var self = this; 421 this.items.forEach( function( el, i ) { 422 if( !classie.has( el, 'shown' ) && !classie.has( el, 'animate' ) && inViewport( el, self.options.viewportFactor ) ) { 423 setTimeout( function() { 424 var perspY = scrollY() + getViewportH() / 2; 425 self.el.style.WebkitPerspectiveOrigin = '50% ' + perspY + 'px'; 426 self.el.style.MozPerspectiveOrigin = '50% ' + perspY + 'px'; 427 self.el.style.perspectiveOrigin = '50% ' + perspY + 'px'; 428 429 self._checkTotalRendered(); 430 431 if( self.options.minDuration && self.options.maxDuration ) { 432 var randDuration = ( Math.random() * ( self.options.maxDuration - self.options.minDuration ) + self.options.minDuration ) + 's'; 433 el.style.WebkitAnimationDuration = randDuration; 434 el.style.MozAnimationDuration = randDuration; 435 el.style.animationDuration = randDuration; 436 } 437 438 classie.add( el, 'animate' ); 439 }, 25 ); 440 } 441 }); 442 this.didScroll = false; 443 }, 444 _resizeHandler : function() { 445 var self = this; 446 function delayed() { 447 self._scrollPage(); 448 self.resizeTimeout = null; 449 } 450 if ( this.resizeTimeout ) { 451 clearTimeout( this.resizeTimeout ); 452 } 453 this.resizeTimeout = setTimeout( delayed, 1000 ); 454 }, 455 _checkTotalRendered : function() { 456 ++this.itemsRenderedCount; 457 if( this.itemsRenderedCount === this.itemsCount ) { 458 window.removeEventListener( 'scroll', this._onScrollFn ); 459 } 460 } 461 } 462 463 // add to global namespace 464 window.AnimOnScroll = AnimOnScroll; 465 466} )( window );
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.