|
@@ -0,0 +1,320 @@
|
|
|
|
+/*!
|
|
|
|
+ * An jQuery | zepto plugin for lazy loading images.
|
|
|
|
+ * author -> jieyou
|
|
|
|
+ * see https://github.com/jieyou/lazyload
|
|
|
|
+ * use some tuupola's code https://github.com/tuupola/jquery_lazyload (BSD)
|
|
|
|
+ * use component's throttle https://github.com/component/throttle (MIT)
|
|
|
|
+ */
|
|
|
|
+;(function(factory){
|
|
|
|
+ if(typeof define === 'function' && define.amd){ // AMD
|
|
|
|
+ // you may need to change `define([------>'jquery'<------], factory)`
|
|
|
|
+ // if you use zepto, change it rely name, such as `define(['zepto'], factory)`
|
|
|
|
+ define(['jquery'], factory)
|
|
|
|
+ // define(['zepto'], factory)
|
|
|
|
+ }else{ // Global
|
|
|
|
+ factory(window.jQuery || window.Zepto)
|
|
|
|
+ }
|
|
|
|
+})(function($,undefined){
|
|
|
|
+ var w = window,
|
|
|
|
+ $window = $(w),
|
|
|
|
+ defaultOptions = {
|
|
|
|
+ threshold : 0,
|
|
|
|
+ failure_limit : 0,
|
|
|
|
+ event : 'scroll',
|
|
|
|
+ effect : 'show',
|
|
|
|
+ effect_params : null,
|
|
|
|
+ container : w,
|
|
|
|
+ data_attribute : 'original',
|
|
|
|
+ data_srcset_attribute : 'original-srcset',
|
|
|
|
+ skip_invisible : true,
|
|
|
|
+ appear : emptyFn,
|
|
|
|
+ load : emptyFn,
|
|
|
|
+ vertical_only : false,
|
|
|
|
+ check_appear_throttle_time : 300,
|
|
|
|
+ url_rewriter_fn : emptyFn,
|
|
|
|
+ no_fake_img_loader : false,
|
|
|
|
+ placeholder_data_img : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC',
|
|
|
|
+ // for IE6\7 that does not support data image
|
|
|
|
+ placeholder_real_img : 'http://ditu.baidu.cn/yyfm/lazyload/0.0.1/img/placeholder.png'
|
|
|
|
+ // todo : 将某些属性用global来配置,而不是每次在$(selector).lazyload({})内配置
|
|
|
|
+ },
|
|
|
|
+ type // function
|
|
|
|
+
|
|
|
|
+ function emptyFn(){}
|
|
|
|
+
|
|
|
|
+ type = (function(){
|
|
|
|
+ var object_prototype_toString = Object.prototype.toString
|
|
|
|
+ return function(obj){
|
|
|
|
+ // todo: compare the speeds of replace string twice or replace a regExp
|
|
|
|
+ return object_prototype_toString.call(obj).replace('[object ','').replace(']','')
|
|
|
|
+ }
|
|
|
|
+ })()
|
|
|
|
+
|
|
|
|
+ function belowthefold($element, options){
|
|
|
|
+ var fold
|
|
|
|
+ if(options._$container == $window){
|
|
|
|
+ fold = ('innerHeight' in w ? w.innerHeight : $window.height()) + $window.scrollTop()
|
|
|
|
+ }else{
|
|
|
|
+ fold = options._$container.offset().top + options._$container.height()
|
|
|
|
+ }
|
|
|
|
+ return fold <= $element.offset().top - options.threshold
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function rightoffold($element, options){
|
|
|
|
+ var fold
|
|
|
|
+ if(options._$container == $window){
|
|
|
|
+ // Zepto do not support `$window.scrollLeft()` yet.
|
|
|
|
+ fold = $window.width() + ($.fn.scrollLeft?$window.scrollLeft():w.pageXOffset)
|
|
|
|
+ }else{
|
|
|
|
+ fold = options._$container.offset().left + options._$container.width()
|
|
|
|
+ }
|
|
|
|
+ return fold <= $element.offset().left - options.threshold
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function abovethetop($element, options){
|
|
|
|
+ var fold
|
|
|
|
+ if(options._$container == $window){
|
|
|
|
+ fold = $window.scrollTop()
|
|
|
|
+ }else{
|
|
|
|
+ fold = options._$container.offset().top
|
|
|
|
+ }
|
|
|
|
+ // console.log('abovethetop fold '+ fold)
|
|
|
|
+ // console.log('abovethetop $element.height() '+ $element.height())
|
|
|
|
+ return fold >= $element.offset().top + options.threshold + $element.height()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function leftofbegin($element, options){
|
|
|
|
+ var fold
|
|
|
|
+ if(options._$container == $window){
|
|
|
|
+ // Zepto do not support `$window.scrollLeft()` yet.
|
|
|
|
+ fold = $.fn.scrollLeft?$window.scrollLeft():w.pageXOffset
|
|
|
|
+ }else{
|
|
|
|
+ fold = options._$container.offset().left
|
|
|
|
+ }
|
|
|
|
+ return fold >= $element.offset().left + options.threshold + $element.width()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function checkAppear($elements, options){
|
|
|
|
+ var counter = 0
|
|
|
|
+ $elements.each(function(i,e){
|
|
|
|
+ var $element = $elements.eq(i)
|
|
|
|
+ if(($element.width() <= 0 && $element.height() <= 0) || $element.css('display') === 'none'){
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ function appear(){
|
|
|
|
+ $element.trigger('_lazyload_appear')
|
|
|
|
+ // if we found an image we'll load, reset the counter
|
|
|
|
+ counter = 0
|
|
|
|
+ }
|
|
|
|
+ // If vertical_only is set to true, only check the vertical to decide appear or not
|
|
|
|
+ // In most situations, page can only scroll vertically, set vertical_only to true will improve performance
|
|
|
|
+ if(options.vertical_only){
|
|
|
|
+ if(abovethetop($element, options)){
|
|
|
|
+ // Nothing.
|
|
|
|
+ }else if(!belowthefold($element, options)){
|
|
|
|
+ appear()
|
|
|
|
+ }else{
|
|
|
|
+ if(++counter > options.failure_limit){
|
|
|
|
+ return false
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }else{
|
|
|
|
+ if(abovethetop($element, options) || leftofbegin($element, options)){
|
|
|
|
+ // Nothing.
|
|
|
|
+ }else if(!belowthefold($element, options) && !rightoffold($element, options)){
|
|
|
|
+ appear()
|
|
|
|
+ }else{
|
|
|
|
+ if(++counter > options.failure_limit){
|
|
|
|
+ return false
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Remove image from array so it is not looped next time.
|
|
|
|
+ function getUnloadElements($elements){
|
|
|
|
+ return $elements.filter(function(i,e){
|
|
|
|
+ return !$elements.eq(i)._lazyload_loadStarted
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // throttle : https://github.com/component/throttle , MIT License
|
|
|
|
+ function throttle (func, wait) {
|
|
|
|
+ var ctx, args, rtn, timeoutID // caching
|
|
|
|
+ var last = 0
|
|
|
|
+
|
|
|
|
+ return function throttled () {
|
|
|
|
+ ctx = this
|
|
|
|
+ args = arguments
|
|
|
|
+ var delta = new Date() - last
|
|
|
|
+ if (!timeoutID)
|
|
|
|
+ if (delta >= wait) call()
|
|
|
|
+ else timeoutID = setTimeout(call, wait - delta)
|
|
|
|
+ return rtn
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function call () {
|
|
|
|
+ timeoutID = 0
|
|
|
|
+ last = +new Date()
|
|
|
|
+ rtn = func.apply(ctx, args)
|
|
|
|
+ ctx = null
|
|
|
|
+ args = null
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if(!$.fn.hasOwnProperty('lazyload')){
|
|
|
|
+
|
|
|
|
+ $.fn.lazyload = function(options){
|
|
|
|
+ var $elements = this,
|
|
|
|
+ isScrollEvent,
|
|
|
|
+ isScrollTypeEvent,
|
|
|
|
+ throttleCheckAppear
|
|
|
|
+
|
|
|
|
+ if(!$.isPlainObject(options)){
|
|
|
|
+ options = {}
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ $.each(defaultOptions,function(k,v){
|
|
|
|
+ if($.inArray(k,['threshold','failure_limit','check_appear_throttle_time']) != -1){ // these params can be a string
|
|
|
|
+ if(type(options[k]) == 'String'){
|
|
|
|
+ options[k] = parseInt(options[k],10)
|
|
|
|
+ }else{
|
|
|
|
+ options[k] = v
|
|
|
|
+ }
|
|
|
|
+ }else if(k == 'container'){ // options.container can be a seletor string \ dom \ jQuery object
|
|
|
|
+ if(options.hasOwnProperty(k)){
|
|
|
|
+ if(options[k] == w || options[k] == document){
|
|
|
|
+ options._$container = $window
|
|
|
|
+ }else{
|
|
|
|
+ options._$container = $(options[k])
|
|
|
|
+ }
|
|
|
|
+ }else{
|
|
|
|
+ options._$container = $window
|
|
|
|
+ }
|
|
|
|
+ delete options.container
|
|
|
|
+ }else if(defaultOptions.hasOwnProperty(k) && (!options.hasOwnProperty(k) || (type(options[k]) != type(defaultOptions[k])))){
|
|
|
|
+ options[k] = v
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ isScrollEvent = options.event == 'scroll'
|
|
|
|
+ throttleCheckAppear = options.check_appear_throttle_time == 0?
|
|
|
|
+ checkAppear
|
|
|
|
+ :throttle(checkAppear,options.check_appear_throttle_time)
|
|
|
|
+
|
|
|
|
+ // isScrollTypeEvent cantains custom scrollEvent . Such as 'scrollstart' & 'scrollstop'
|
|
|
|
+ // https://github.com/search?utf8=%E2%9C%93&q=scrollstart
|
|
|
|
+ isScrollTypeEvent = isScrollEvent || options.event == 'scrollstart' || options.event == 'scrollstop'
|
|
|
|
+
|
|
|
|
+ $elements.each(function(i,e){
|
|
|
|
+ var element = this,
|
|
|
|
+ $element = $elements.eq(i),
|
|
|
|
+ placeholderSrc = $element.attr('src'),
|
|
|
|
+ originalSrcInAttr = $element.attr('data-'+options.data_attribute), // `data-original` attribute value
|
|
|
|
+ originalSrc = options.url_rewriter_fn == emptyFn?
|
|
|
|
+ originalSrcInAttr:
|
|
|
|
+ options.url_rewriter_fn.call(element,$element,originalSrcInAttr),
|
|
|
|
+ originalSrcset = $element.attr('data-'+options.data_srcset_attribute),
|
|
|
|
+ isImg = $element.is('img')
|
|
|
|
+
|
|
|
|
+ if($element._lazyload_loadStarted == true || placeholderSrc == originalSrc){
|
|
|
|
+ $element._lazyload_loadStarted = true
|
|
|
|
+ $elements = getUnloadElements($elements)
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ $element._lazyload_loadStarted = false
|
|
|
|
+
|
|
|
|
+ // If element is an img and no src attribute given, use placeholder.
|
|
|
|
+ if(isImg && !placeholderSrc){
|
|
|
|
+ // For browsers that do not support data image.
|
|
|
|
+ $element.one('error',function(){ // `on` -> `one` : IE6 triggered twice error event sometimes
|
|
|
|
+ $element.attr('src',options.placeholder_real_img)
|
|
|
|
+ }).attr('src',options.placeholder_data_img)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // When appear is triggered load original image.
|
|
|
|
+ $element.one('_lazyload_appear',function(){
|
|
|
|
+ var effectParamsIsArray = $.isArray(options.effect_params),
|
|
|
|
+ effectIsNotImmediacyShow
|
|
|
|
+ function loadFunc(){
|
|
|
|
+ // In most situations, the effect is immediacy show, at this time there is no need to hide element first
|
|
|
|
+ // Hide this element may cause css reflow, call it as less as possible
|
|
|
|
+ if(effectIsNotImmediacyShow){
|
|
|
|
+ // todo: opacity:0 for fadeIn effect
|
|
|
|
+ $element.hide()
|
|
|
|
+ }
|
|
|
|
+ if(isImg){
|
|
|
|
+ // attr srcset first
|
|
|
|
+ if(originalSrcset){
|
|
|
|
+ $element.attr('srcset', originalSrcset)
|
|
|
|
+ }
|
|
|
|
+ if(originalSrc){
|
|
|
|
+ $element.attr('src', originalSrc)
|
|
|
|
+ }
|
|
|
|
+ }else{
|
|
|
|
+ $element.css('background-image','url("' + originalSrc + '")')
|
|
|
|
+ }
|
|
|
|
+ if(effectIsNotImmediacyShow){
|
|
|
|
+ $element[options.effect].apply($element,effectParamsIsArray?options.effect_params:[])
|
|
|
|
+ }
|
|
|
|
+ $elements = getUnloadElements($elements)
|
|
|
|
+ }
|
|
|
|
+ if(!$element._lazyload_loadStarted){
|
|
|
|
+ effectIsNotImmediacyShow = (options.effect != 'show' && $.fn[options.effect] && (!options.effect_params || (effectParamsIsArray && options.effect_params.length == 0)))
|
|
|
|
+ if(options.appear != emptyFn){
|
|
|
|
+ options.appear.call(element, $element, $elements.length, options)
|
|
|
|
+ }
|
|
|
|
+ $element._lazyload_loadStarted = true
|
|
|
|
+ if(options.no_fake_img_loader || originalSrcset){
|
|
|
|
+ if(options.load != emptyFn){
|
|
|
|
+ $element.one('load',function(){
|
|
|
|
+ options.load.call(element, $element, $elements.length, options)
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+ loadFunc()
|
|
|
|
+ }else{
|
|
|
|
+ $('<img />').one('load', function(){ // `on` -> `one` : IE6 triggered twice load event sometimes
|
|
|
|
+ loadFunc()
|
|
|
|
+ if(options.load != emptyFn){
|
|
|
|
+ options.load.call(element, $element, $elements.length, options)
|
|
|
|
+ }
|
|
|
|
+ }).attr('src',originalSrc)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ // When wanted event is triggered load original image
|
|
|
|
+ // by triggering appear.
|
|
|
|
+ if (!isScrollTypeEvent){
|
|
|
|
+ $element.on(options.event, function(){
|
|
|
|
+ if (!$element._lazyload_loadStarted){
|
|
|
|
+ $element.trigger('_lazyload_appear')
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ // Fire one scroll event per scroll. Not one scroll event per image.
|
|
|
|
+ if(isScrollTypeEvent){
|
|
|
|
+ options._$container.on(options.event, function(){
|
|
|
|
+ throttleCheckAppear($elements, options)
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Check if something appears when window is resized.
|
|
|
|
+ // Force initial check if images should appear when window is onload.
|
|
|
|
+ $window.on('resize load', function(){
|
|
|
|
+ throttleCheckAppear($elements, options)
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ // Force initial check if images should appear.
|
|
|
|
+ $(function(){
|
|
|
|
+ throttleCheckAppear($elements, options)
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ return this
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+})
|