bolo-fantastic/js/lazyload.js

320 lines
14 KiB
JavaScript
Raw Normal View History

2020-10-06 14:05:43 +00:00
/*!
* 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
}
}
})