js效果札记:怎样实现图片的懒加载以及jquery.lazyload.js的使用

js效果笔记:怎样实现图片的懒加载以及jquery.lazyload.js的使用

在项目中有时候会用到图片的延迟加载,那么延迟加载的好处是啥呢?

我觉得主要包括两点吧,第一是在包含很多大图片长页面中延迟加载图片可以加快页面加载速度;第二是帮助降低服务器负担。

下面介绍一下常用的延迟加载插件jquery.lazyload.js以及怎样实现一个延迟加载的插件。

一:jquery.lazyload.js插件

lazyload是jQuery写的延迟加载插件,在浏览器可视区域外的图片不会被载入, 直到用户将页面滚动到它们所在的位置. 这与图片预加载的处理方式正好是相反的。

实现原理

首先选中的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) {};        // 在视口左方

具体使用

1.页面引入方式

由于延迟加载lazyload是依赖jquery的,所有页面需要引入jquery,如下:

<script src="jquery.js"></script>
<script src="jquery.lazyload.js"></script>

基本写法:

<img class="lazy" data-original="img/example.jpg" width="640" height="480">

$(function() {
    $("img.lazy").lazyload();
});

其中的data-original 属性存放真实的图片url路径。

小贴士:你必须设置图片的宽度或者高度在css中,否则插件可能不能正常工作。

设置Threshold

默认情况下图片会出现在屏幕时加载. 如果你想提前加载图片, 可以设置 threshold 选项, 设置 threshold 为 200 令图片在距离屏幕 200 像素时提前加载。

$("img.lazy").lazyload({
    threshold : 200
});

设置事件触发加载

事件可以是任何 jQuery 事件, 如: click 和 mouseover. 你还可以使用自定义的事件, 如: sporty 和 foobar. 默认情况下处于等待状态, 直到用户滚动到窗口上图片所在位置. 在灰色占位图片被点击之前阻止加载图片, 你可以这样做:

$("img.lazy").lazyload({
    event : "click"
});

当然,你也可以用下面的这种方式实现延迟加载:

$(function() {
    $("img.lazy").lazyload({
        event : "sporty"
    });
});

$(window).bind("load", function() {
    var timeout = setTimeout(function() {
        $("img.lazy").trigger("sporty")
    }, 5000);
});

即是页面加载完成 5 秒后,再去执行图片的懒加载。

延迟加载效果

当图片完全加载的时候, 插件默认地使用 show() 方法来将图显示出来. 其实你可以使用任何你想用的特效来处理. 下面的代码使用 FadeIn 效果:

$("img.lazy").lazyload({
    effect : "fadeIn"
});

不支持JavaScript的浏览器的使用方式

几乎所有浏览器的 JavaScript 都是激活的. 然而可能你仍希望能在不支持 JavaScript 的客户端展示真实图片. 当浏览器不支持 JavaScript 时优雅降级, 你可以将真实的图片片段在写 <noscript> 标签内。

<img class="lazy" data-original="img/example.jpg"  width="640" heigh="480">
<noscript>
    <img src="img/example.jpg" width="640" heigh="480">
</noscript>

可以通过 CSS 隐藏占位符:

.lazy {
  display: none;
}

在支持 JavaScript 的浏览器中, 你必须在 DOM ready 时将占位符显示出来, 这可以在插件初始化的同时完成。

$("img.lazy").show().lazyload();

设置延迟加载的图片容器

你可以将插件用在可滚动容器的图片上, 例如带滚动条的 DIV 元素. 你要做的只是将容器定义为 jQuery 对象并作为参数传到初始化方法里面:

#container {
    height: 600px;
    overflow: scroll;
}

$("img.lazy").lazyload({
    container: $("#container")
});

当图片不顺序排列

滚动页面的时候, Lazy Load 会循环为加载的图片. 在循环中检测图片是否在可视区域内. 默认情况下在找到第一张不在可见区域的图片时停止循环. 图片被认为是流式分布的, 图片在页面中的次序和 HTML 代码中次序相同. 但是在一些布局中, 这样的假设是不成立的. 不过你可以通过 failurelimit 选项来控制加载行为。

$("img.lazy").lazyload({ 
    failure_limit : 10
});

将 failurelimit 设为 10 令插件找到 10 个不在可见区域的图片是才停止搜索. 如果你有一个猥琐的布局, 请把这个参数设高一点。

设置加载隐藏的图片

可能在你的页面上埋藏可很多隐藏的图片. 比如插件用在对列表的筛选, 你可以不断地修改列表中各条目的显示状态. 为了提升性能, Lazy Load 默认忽略了隐藏图片. 如果你想要加载隐藏图片, 请将 skip_invisible 设为 false。

$("img.lazy").lazyload({
    skip_invisible : true
});

源码

官网地址:http://appelsiini.net/projects/lazyload/

/*!
     * Lazy Load - jQuery plugin for lazy loading images
     *
     * Copyright (c) 2007-2015 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.7
     *
     */
    
    (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  : false,
                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. */
                    } else if (!$.belowthefold(this, settings) &&
                        !$.rightoffold(this, settings)) {
                            $this.trigger("appear");
                            /* if we found an image we'll load, reset the counter */
                            counter = 0;
                    } 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. */
            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. */
                $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. */
                                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.                              */
                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);

二:手写一个简单的懒加载插件

js代码

window.smallDelay = (function(window, document, undefined) {
    'use strict';
    var store = [],poll;
    var settings = {
        offset:0, //离可视区域多少像素的图片可以被加载
        throttle: 250 //图片延时多少毫秒加载
    }
        
    var _inView = function(el) {
        var coords = el.getBoundingClientRect();
        return ((coords.top >= 0 && coords.left >= 0) && coords.top <= ((window.innerHeight || document.documentElement.clientHeight) + parseInt(settings.offset)));
    };

    var _pollImages = function() {
        for (var i = store.length; i--;) {
            var self = store[i];
            if (_inView(self)) {
                self.src = self.getAttribute('data-delay');
                store.splice(i, 1);
            }
        }
    };

    var _throttle = function() {
        clearTimeout(poll);
        poll = setTimeout(_pollImages, settings.throttle);
    };

    var init = function(obj) {
        var nodes = document.querySelectorAll('[data-delay]');
        var opts = obj || {};
        settings.offset = opts.offset || settings.offset;
        settings.throttle = opts.throttle || settings.throttle;

        for (var i = 0; i < nodes.length; i++) {
            store.push(nodes[i]);
        }

        _throttle();
        
        //滚动监听执行图片懒加载
        if (document.addEventListener) {
            window.addEventListener('scroll', _throttle, false);
        } else {
            window.attachEvent('onscroll', _throttle);
        }
        
        //返回该对象进行链式操作
        return this;
    };

    return {
        init: init,
        render: _throttle
    };

})(window, document);

调用方式:

smallDelay.init({
     offset: 0,//离可视区域多少像素的图片可以被加载
   throttle: 0 //图片延时多少毫秒加载
});

html代码:

<img src="images/loading.gif" data-delay="images/avatar.png" />

三:根据lazyload插件实现一个不依赖jQuery的懒加载插件

实现内容

1、增加了图片预加载可选

2、修改了图片本身就在可视范围的时候直接显示而不需要滚动条触发

3、修改了Splice删除数组的时候,会跳过下一张图片BUG

4、浏览器窗口resize的时候图片出现也会加载

5、判断图片父层包裹顶部或者底部出现在可视范围内即可显示图片

实现源码

var Lazy = {
        $:function(arg,context){
            var tagAll,n,eles=[],i,sub = arg.substring(1);
            context = context|| document;
            if(typeof arg =='string'){
                switch(arg.charAt(0)){
                    case '#':
                        return document.getElementById(sub);
                        break;
                    case '.':
                        if(context.getElementsByClassName) return context.getElementsByClassName(sub);
                        tagAll = Lazy.$('*');
                        n = tagAll.length;
                        for(i = 0;i<n;i++){
                            if(tagAll[i].className.indexOf(sub) > -1) eles.push(tagAll[i]);
                        }
                        return eles;
                        break;
                    default:
                        return context.getElementsByTagName(arg);
                        break;
                }
            }
        },
        getPos:function (node) {
            var scrollx = document.documentElement.scrollLeft || document.body.scrollLeft,
                    scrollt = document.documentElement.scrollTop || document.body.scrollTop;
            var pos = node.getBoundingClientRect();
            return {top:pos.top + scrollt, right:pos.right + scrollx, bottom:pos.bottom + scrollt, left:pos.left + scrollx }
        },
        bind:function(node,type,handler){
            node.addEventListener?node.addEventListener(type, handler, false):node.attachEvent('on'+ type, handler);
        },
        unbind:function(node,type,handler){
            node.removeEventListener?node.removeEventListener(type, handler, false):node.detachEvent('on'+ type, handler);
        },
        toArray:function(eles){
            var arr = [];
            for(var i=0,n=eles.length;i<n;i++){
                arr.push(eles[i]);
            }
            return arr;
        }
    };
    function imgLazyLoad(){
        var timer,screenHeight = document.documentElement.clientHeight;
        // 选择所有图片
        var allimg = Lazy.$('img');
        // 筛选CLASS为lazyload的图片
        var elems = Lazy.$('.lazyload',allimg);
        // 转换为真正的数组
        elems = Lazy.toArray(elems);
        if(!elems.length) return;
        // 没有发生滚动事件时如果图片在可视范围之内,也显示
        for(var i = 0;i < elems.length;i++){
            // 获取图像的父元素即包裹图像的元素,判断图像是否在可视区域即直接判断父元素是否可视
            var parent = elems[i].parentNode;
            var pos = Lazy.getPos(parent);
            var posT = pos.top;
            var posB = pos.bottom;
            // 没有滚动条情况如果距离顶部的距离小于屏幕的高度则赋值SRC
            if(posT < screenHeight){
                elems[i].src = elems[i].getAttribute('data-img');
                // 移除后,数组的长度减一,下一个下标需减一
                elems.splice(i--,1);
            }
        }
        // 绑定scroll事件
        Lazy.bind(window,'scroll',loading);
        Lazy.bind(window,'resize',loading);
        function loading(){
            timer && clearTimeout(timer);
            timer = setTimeout(function(){
                var scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
                screenHeight = document.documentElement.clientHeight;
                for(var i = 0;i < elems.length;i++){
                    var parent = elems[i].parentNode;
                    var pos = Lazy.getPos(parent);
                    var posT = pos.top;
                    var posB = pos.bottom;
                    var screenTop = screenHeight+scrollTop;
                    // 元素顶部出现在可视区  或者  元素底部出现在可视区
                    if((posT > scrollTop && posT <  screenTop) || (posB > scrollTop && posB < screenTop)){
                        elems[i].src = elems[i].getAttribute('data-img');
                        elems.splice(i--,1);
                    }else{
                        // 去掉以下注释开启图片预加载
                        // new Image().src = elems[i].getAttribute('data-img');
                    }
                }
                if(!elems.length){
                    Lazy.unbind(window,'scroll',loading);
                    Lazy.unbind(window,'resize',loading);
                }
            },300);
        }
    }
    imgLazyLoad();

使用方法

1、在图片上增加lazyload的类(class='lazyload')

2、把真实的图片地址放入自定义属性data-img 中,把图片的SRC属性设置为一个一像素的透明图片,图片需要设置width,height属性,以免布局混乱

如下:

<img data-img="a.jpg" src="loading.gif" width="640" height="480"   class='lazyload'>

3、在需要延迟加载的页面调用imgLazyLoad()函数;

该原生js实现的懒加载转载地址:http://www.cnblogs.com/NNUF/archive/2012/06/25/2561570.html