JS进阶篇--怎样实现图片的懒加载以及jquery.lazyload.js的使用

比特幽篁引
• 阅读 4760

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

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

下面介绍一下常用的延迟加载插件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/project...

/*!
     * 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/a...

点赞
收藏
评论区
推荐文章
浩浩 浩浩
4年前
【Flutter实战】图片和Icon
3.5图片及ICON3.5.1图片Flutter中,我们可以通过Image组件来加载并显示图片,Image的数据源可以是asset、文件、内存以及网络。ImageProviderImageProvider是一个抽象类,主要定义了图片数据获取的接口load(),从不同的数据源获取图片需要实现不同的ImageProvi
疯震震 疯震震
4年前
【单例深思】饿汉式
1、抛出问题1.为什么饿汉式单例是线程安全的?2.为什么饿汉式单例没有延迟加载(LazyLoading)?2、JVM类加载机制饿汉式单例的实现:javapublicclassSingletonprivatestaticSingletonsingletonnewSingleton();privateS
Stella981 Stella981
3年前
50 Android Hack 读书笔记
1、可以指定android:weightSum属性2、使用include标签来应对变化3、使用ViewStub标签延迟加载有可能不需要加载的数据标签中可以指定inflateId属性4、使用自定义ViewGroup,重写onMeasure、onLayout5、使用Android的PreferenceCategory6、使用TextSwitcher
Stella981 Stella981
3年前
JavaScript——图片懒加载
前言有一个朋友问我这个问题,刚好有时间,现在就简单的写个Demo~github|https://github.com/wangyang0210/bky/tree/picLoadLazy(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fgithub.com%2Fwan
Wesley13 Wesley13
3年前
Java多线程编程之单例模式
延迟加载:“懒汉模式”延迟加载是指在调用getInstance()方法时创建实例。常见的方法是在getInstance()方法中实例化new。实现代码如下:!(https://oscimg.oschina.net/oscnet/0b194956e9fd68db32050dd6439225bb86a.png)但是因为ge
Stella981 Stella981
3年前
Hibernate通用序列化方案,避免延迟加载问题及序列化整个数据库至
在使用Ajax:HibernateEntityjson, FlexRemoteObject: HibernateEntityActionScriptObject的过程,经常碰到如下问题:问题:1.Hibernate碰到延迟加载的属性访问时如果session被关闭则抛出LazyInitializationException
Stella981 Stella981
3年前
Cocos Creator 资源加载流程剖析【三】——Load部分
Load流程是整个资源加载管线的最后一棒,由Loader这个pipe负责(loader.js)。通过Download流程拿到内容之后,需要对内容做一些“加载”处理。使得这些内容可以在游戏中使用。这里并不是所有的资源都需要进行一个加载处理,目前只有图片、Json、Plist、Uuid(Prefab、场景)等资源才会执行加载的流程,其他的资源在Downloa
Wesley13 Wesley13
3年前
CSS3实现页面加载进度条
什么情况下会使用到页面加载进度条?当页面中的需要加载的内容很多,用户直接进入则看不到任何内容,体验不好,这个时候就需要使用到页面加载的一个动画,在页面加载结束后再显示主体内容。实现页面加载进度条有哪几种方式?一般可分为两种,1.设置多少时间后显示页面,2.根据页面加载的文档状态来实现如何根据文档状态来实现?
深入理解 Flutter 图片加载原理 | 京东云技术团队
Flutter官方为我们提供了Image控件可实现图片的加载及显示,Image控件本身是一个StatefulWidget,那么在图片显示过程中是如何加载及显示出来的呢?本篇文章将逐步分析Flutter中图片加载原理,理解了Flutter图片源码的加载原理后对我们有什么帮助?
融云IM即时通讯 融云IM即时通讯
7个月前
融云IM干货丨有没有插件能帮我优化uni-app的页面加载速度?
根据您的需求,以下是一些可以帮助优化uniapp页面加载速度的插件和方法:1.图片懒加载插件:使用图片懒加载可以显著减少首屏的加载时间。可以在页面滚动时才加载图片,减少初次加载的压力。2.代码拆分和懒加载:根据页面和功能的使用情况,将代码拆分为多个模块,并
布局王 布局王
1个月前
鸿蒙Next仓颉语言开发实战教程:懒加载
今天要分享的是仓颉开发语言中的懒加载。先和初学者朋友们解释一下什么是懒加载。懒加载在代码中叫做LazyForEach,看到名字你一定能猜到它和ForEach的功能类似。只不过和ForEach的一次性加载所有数据不同,懒加载会根据屏幕可使区域按需加载数据,并