zoukankan      html  css  js  c++  java
  • jQuery.lazyload使用及源码分析

    前言:

    貌似以前自己也写过图片懒加载插件,但是新公司使用的是jQuery.lazyload插件,为了更好的运用,自己还是把源码看了遍,分别记录了如何使用,

    插件原理,各个配置属性的完整解释,demo实例,源码分析(较简短),源码分析可以配合使用,配置属性,原理进行阅读,如需转载,请注明出处

    博客园 华子yjh

    一、如何使用

    // 最简单的使用,不带参数
    $('img').lazyload();
    
    
    // 带参数(配置对象),下面配置对象中的各个属性值都是默认的
    $('img').lazyload({
        threshold       : 0,
        failure_limit   : 0,
        event           : "scroll",
        effect          : "show",
        container       : window,
        data_attribute  : "original",
        skip_invisible  : true,
        appear          : null,
        load            : null,
        placeholder     : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC"
    });

    二、内部原理

    首先选中的img元素都绑定了一个appear事件(处理img显示真实的图片地址),方便以后满足条件时触发该事件;
    在配置对象中有一个container属性配置,默认为window,如果img元素在该container容器视口中,则触发appear事件;
    为了判断img元素是否在container容器视口范围中,造了如下四个轮子:

    $.belowthefold = function(element, settings) {};    // 在视口下方
    $.rightoffold = function(element, settings) {};        // 在视口右方
    $.abovethetop = function(element, settings) {};        // 在视口上方
    $.leftofbegin = function(element, settings) {};        // 在视口左方

    看看源码中是如何利用这四个轮子:

    if ($.abovethetop(this, settings) || $.leftofbegin(this, settings)) {
        /* Nothing. */
    }
    // 不满足在上方,左方;也不满足在下方,右方; 则触发appear事件
    else if (!$.belowthefold(this, settings) && !$.rightoffold(this, settings)) {
        $this.trigger("appear");
    }


    三、配置对象中的其他属性

    临界值,这个值是针对container容器的,即距离container容器视口的临界值

    {
        threshold: 0
    }

    事件,container容器默认绑定这个事件,在这个事件被触发时,会不断的判断img元素是否满足触发appear的条件,
    因此当浏览器不停的滚动下来时,如果满足条件,则显示图片;

    另外还有一点,如果这个事件不是scroll事件,则选中的img元素都会绑定这个事件,绑定的这个事件中同样会触发内部appear事件;

    {
        event: 'scroll'
    }

    显示方法,默认为show,也可以设置为fadeIn,API中隐藏了一个配置属性effectspeed,动画运行的时间

    {
        effect: "show"
    }

    img元素的一个data属性,用于存放图片的真实地址

    {
        data_attribute: "original",
    }

    忽略隐藏的img元素

    {
        skip_invisible: true
    }

    图片占位符,img元素默认src属性为1*1像素的透明图片

    {
        placeholder: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC"
    }

    在img触发appear事件时执行的回调

    {
        appear: null
    }

    在img触发load事件时执行的回调

    {
        load: null
    }

    最后一个配置属性failure_limit

    {
        failure_limit: 0
    }

    为了便于理解,我们先来看一段与其有关的源码:

    var counter = 0;
    
    elements.each(function() {
        if ($.abovethetop(this, settings) || $.leftofbegin(this, settings)) {
            // ...
        } else if (!$.belowthefold(this, settings) && !$.rightoffold(this, settings)) {
            // ...
        } else {
            if (++counter > settings.failure_limit) {
                return false;
            }
        }
    });

    什么意思呢,如果找到的是第 failure_limit 个img元素,且不在container视口上方,左方及视口内(可以允许在视口下方,右方),则中断循环

    三、demo

    看完原理和配置属性,是否觉得很简单呢,来看看几个demo吧

    demo1 下拉滚动: http://jsfiddle.net/ddEPL/
    demo2 Tab切换: http://jsfiddle.net/ddEPL/1/  http://jsfiddle.net/ddEPL/8/

    四、源码分析

    /*
     * Lazy Load - jQuery plugin for lazy loading images
     *
     * Copyright (c) 2007-2013 Mika Tuupola
     *
     * Licensed under the MIT license:
     *   http://www.opensource.org/licenses/mit-license.php
     *
     * Project home:
     *   http://www.appelsiini.net/projects/lazyload
     *
     * Version:  1.9.3
     *
     */
    
    (function($, window, document, undefined) {
        var $window = $(window);
    
        $.fn.lazyload = function(options) {
            var elements = this;
            var $container;
            var settings = {
                threshold       : 0,
                failure_limit   : 0,
                event           : "scroll",
                effect          : "show",
                container       : window,
                data_attribute  : "original",
                skip_invisible  : true,
                appear          : null,
                load            : null,
                placeholder     : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAANSURBVBhXYzh8+PB/AAffA0nNPuCLAAAAAElFTkSuQmCC"
            };
    
            function update() {
                var counter = 0;
    
                elements.each(function() {
                    var $this = $(this);
                    // 如果图片隐藏,且忽略隐藏,则中断循环
                    if (settings.skip_invisible && !$this.is(":visible")) {
                        return;
                    }
                    if ($.abovethetop(this, settings) || $.leftofbegin(this, settings)) {
                            /* Nothing. */
                    } 
                    // img满足在container视口中,则显示
                    else if (!$.belowthefold(this, settings) && !$.rightoffold(this, settings)) {
                            $this.trigger("appear");
                            /* if we found an image we'll load, reset the counter */
                            counter = 0;
                    }
                    // 如果找到的是第(failure_limit + 1)个img元素,且不在container视口上方,左方及视口内(可以允许在视口下方,右方),
                    // 则中断循环
                    else {
                        if (++counter > settings.failure_limit) {
                            return false;
                        }
                    }
                });
    
            }
    
            if(options) {
                /* Maintain BC for a couple of versions. */
                if (undefined !== options.failurelimit) {
                    options.failure_limit = options.failurelimit;
                    delete options.failurelimit;
                }
                if (undefined !== options.effectspeed) {
                    options.effect_speed = options.effectspeed;
                    delete options.effectspeed;
                }
    
                $.extend(settings, options);
            }
    
            /* Cache container as jQuery as object. */
            $container = (settings.container === undefined ||
                          settings.container === window) ? $window : $(settings.container);
    
            /* Fire one scroll event per scroll. Not one scroll event per image. */
            // 为container绑定scroll事件
            if (0 === settings.event.indexOf("scroll")) {
                $container.bind(settings.event, function() {
                    return update();
                });
            }
    
            this.each(function() {
                var self = this;
                var $self = $(self);
    
                self.loaded = false;
    
                /* If no src attribute given use data:uri. */
                // 设置占位符
                if ($self.attr("src") === undefined || $self.attr("src") === false) {
                    if ($self.is("img")) {
                        $self.attr("src", settings.placeholder);
                    }
                }
    
                /* When appear is triggered load original image. */
                // one绑定appear,触发后则移除该事件
                $self.one("appear", function() {
                    if (!this.loaded) {
                        // 存在回调则触发
                        if (settings.appear) {
                            var elements_left = elements.length;
                            settings.appear.call(self, elements_left, settings);
                        }
                        $("<img />")
                            .bind("load", function() {
    
                                var original = $self.attr("data-" + settings.data_attribute);
                                $self.hide();
                                if ($self.is("img")) {
                                    $self.attr("src", original);
                                } else {
                                    $self.css("background-image", "url('" + original + "')");
                                }
                                $self[settings.effect](settings.effect_speed);
    
                                self.loaded = true;
    
                                /* Remove image from array so it is not looped next time. */
                                // 更新elements,过滤掉已经加载的img元素,避免下次在update中轮循
                                var temp = $.grep(elements, function(element) {
                                    return !element.loaded;
                                });
                                elements = $(temp);
                                
                                // 存在回调则触发
                                if (settings.load) {
                                    var elements_left = elements.length;
                                    settings.load.call(self, elements_left, settings);
                                }
                            })
                            .attr("src", $self.attr("data-" + settings.data_attribute));
                    }
                });
    
                /* When wanted event is triggered load original image */
                /* by triggering appear.                              */
                // 绑定不是scroll的事件,用于触发appear事件
                if (0 !== settings.event.indexOf("scroll")) {
                    $self.bind(settings.event, function() {
                        if (!self.loaded) {
                            $self.trigger("appear");
                        }
                    });
                }
            });
    
            /* Check if something appears when window is resized. */
            $window.bind("resize", function() {
                update();
            });
    
            /* With IOS5 force loading images when navigating with back button. */
            /* Non optimal workaround. */
            if ((/(?:iphone|ipod|ipad).*os 5/gi).test(navigator.appVersion)) {
                $window.bind("pageshow", function(event) {
                    if (event.originalEvent && event.originalEvent.persisted) {
                        elements.each(function() {
                            $(this).trigger("appear");
                        });
                    }
                });
            }
    
            /* Force initial check if images should appear. */
            $(document).ready(function() {
                update();
            });
    
            return this;
        };
    
        /* Convenience methods in jQuery namespace.           */
        /* Use as  $.belowthefold(element, {threshold : 100, container : window}) */
    
        $.belowthefold = function(element, settings) {
            var fold;
    
            if (settings.container === undefined || settings.container === window) {
                fold = (window.innerHeight ? window.innerHeight : $window.height()) + $window.scrollTop();
            } else {
                fold = $(settings.container).offset().top + $(settings.container).height();
            }
    
            return fold <= $(element).offset().top - settings.threshold;
        };
    
        $.rightoffold = function(element, settings) {
            var fold;
    
            if (settings.container === undefined || settings.container === window) {
                fold = $window.width() + $window.scrollLeft();
            } else {
                fold = $(settings.container).offset().left + $(settings.container).width();
            }
    
            return fold <= $(element).offset().left - settings.threshold;
        };
    
        $.abovethetop = function(element, settings) {
            var fold;
    
            if (settings.container === undefined || settings.container === window) {
                fold = $window.scrollTop();
            } else {
                fold = $(settings.container).offset().top;
            }
    
            return fold >= $(element).offset().top + settings.threshold  + $(element).height();
        };
    
        $.leftofbegin = function(element, settings) {
            var fold;
    
            if (settings.container === undefined || settings.container === window) {
                fold = $window.scrollLeft();
            } else {
                fold = $(settings.container).offset().left;
            }
    
            return fold >= $(element).offset().left + settings.threshold + $(element).width();
        };
    
        $.inviewport = function(element, settings) {
             return !$.rightoffold(element, settings) && !$.leftofbegin(element, settings) &&
                    !$.belowthefold(element, settings) && !$.abovethetop(element, settings);
         };
    
        /* Custom selectors for your convenience.   */
        /* Use as $("img:below-the-fold").something() or */
        /* $("img").filter(":below-the-fold").something() which is faster */
    
        $.extend($.expr[":"], {
            "below-the-fold" : function(a) { return $.belowthefold(a, {threshold : 0}); },
            "above-the-top"  : function(a) { return !$.belowthefold(a, {threshold : 0}); },
            "right-of-screen": function(a) { return $.rightoffold(a, {threshold : 0}); },
            "left-of-screen" : function(a) { return !$.rightoffold(a, {threshold : 0}); },
            "in-viewport"    : function(a) { return $.inviewport(a, {threshold : 0}); },
            /* Maintain BC for couple of versions. */
            "above-the-fold" : function(a) { return !$.belowthefold(a, {threshold : 0}); },
            "right-of-fold"  : function(a) { return $.rightoffold(a, {threshold : 0}); },
            "left-of-fold"   : function(a) { return !$.rightoffold(a, {threshold : 0}); }
        });
    
    })(jQuery, window, document);
  • 相关阅读:
    解决UITableView中Cell重用机制导致内容出错的方法总结
    Hdu 1052 Tian Ji -- The Horse Racing
    Hdu 1009 FatMouse' Trade
    hdu 2037 今年暑假不AC
    hdu 1559 最大子矩阵
    hdu 1004 Let the Balloon Rise
    Hdu 1214 圆桌会议
    Hdu 1081 To The Max
    Hdu 2845 Beans
    Hdu 2955 Robberies 0/1背包
  • 原文地址:https://www.cnblogs.com/yangjunhua/p/3871763.html
Copyright © 2011-2022 走看看