图片懒加载(滚动加载)原理

一、什么是图片懒加载?

即在页面载入的时候将页面上的img标签的src指向一个小图片,把真实地址存放在一个自定义属性中,这里我用data-src来存放,如下。

<img src="loading.gif" data-src="http://xxx.ooo.com" />

然后将页面img标签获取并保存,开启一个定时器,遍历保存的img标签,判断其位置是否出现在了可视区域内。如果出现在可视区域了那么就把真实的src地址给赋值上。
并且从数组中删除,避免重复判断。我们可以获取当前img的相对于文档顶的偏移距离减去scrollTop的距离,然后和浏览器窗口高度在进行比较,如果小于浏览器窗口则出现在了可视区域内了,反之,则没有。


二、为什要使用这个技术?

对于图片过多的页面,为了加速页面加载速度,比如一个页面中有很多图片,如淘宝、京东首页等等,如果一上来就发送这么多请求,页面加载就会很漫长,如果js文件都放在了文档的底部,恰巧页面的头部又依赖这个js文件,那就不好办了。更为要命的是:一上来就发送百八十个请求,服务器可能就吃不消了(不是只有一两个人在访问这个页面)。
所以很多时候我们需要将页面内未出现在可视区域内的图片先不做加载, 等到滚动到可视区域后再去加载。
这样子对于页面加载性能上会有很大的提升,也提高了用户体验。


三、实现


1)屏幕可视窗口大小:对应于图中1、2位置处

    原生方法:window.innerHeight 标准浏览器及IE9+ || document.documentElement.clientHeight 标准浏览器及低版本IE标准模式 ||

           document.body.clientHeight 低版本混杂模式

       jQuery方法: $(window).height() 

  2)浏览器窗口顶部与文档顶部之间的距离,也就是滚动条滚动的距离:也就是图中3、4处对应的位置;

    原生方法:window.pagYoffset——IE9+及标准浏览器 || document.documentElement.scrollTop 兼容ie低版本的标准模式 ||

         document.body.scrollTop 兼容混杂模式;

        jQuery方法:$(document).scrollTop(); 

  3)获取元素的尺寸:对应于图中5、6位置处;左边jquery方法,右边原生方法

    $(o).width() = o.style.width; 

    $(o).innerWidth() = o.style.width+o.style.padding;

    $(o).outerWidth() = o.offsetWidth = o.style.width+o.style.padding+o.style.border;

    $(o).outerWidth(true) = o.style.width+o.style.padding+o.style.border+o.style.margin;

    注意:要使用原生的style.xxx方法获取属性,这个元素必须已经有内嵌的样式,如<div style="...."></div>;

    如果原先是通过外部或内部样式表定义css样式,必须使用o.currentStyle[xxx] || document.defaultView.getComputedStyle(0)[xxx]来获取样式值

  4)获取元素的位置信息:对应与图中7、8位置处

    1)返回元素相对于文档document顶部、左边的距离;

    jQuery:$(o).offset().top元素距离文档顶的距离,$(o).offset().left元素距离文档左边缘的距离

    原生:getoffsetTop(),高程上有具体说明,这边就忽略了;

      顺便提一下返回元素相对于第一个以定位的父元素的偏移距离,注意与上面偏移距的区别;

        jQuery:position()返回一个对象,$(o).position().left = style.left,$(o).position().top = style.top;


5)知道如何获取元素尺寸、偏移距离后,接下来一个问题就是:如何判断某个元素进入或者即将进入可视窗口区域?

    (1)外面最大的框为实际页面的大小,中间浅蓝色的框代表父元素的大小,对象1~8代表元素位于页面上的实际位置;以水平方向来做如下说明!

    (2)对象8左边界相对于页面左边界的偏移距离(offsetLeft)大于父元素右边界相对于页面左边界的距离,此时可判读元素位于父元素之外;

    (3)对象7左边界跨过了父元素右边界,此时:对象7左边界相对于页面左边界的偏移距离(offsetLeft)小于 父元素右边界相对于

      页面左边界的距离,因此对象7就进入了父元素可视区;

    (4)在对象6的位置处,对象5的右边界与页面左边界的距离 大于 父元素左边界与页面左边界的距离;

    (5)在对象5位置处时,对象5的右边界与页面左边界的距离 小于 父元素左边界与页面左边界的距离;此时,可判断元素处于父元素可视区外;

      (6)因此水平方向必须买足两个条件,才能说明元素位于父元素的可视区内;同理垂直方向也必须满足两个条件。


四、扩展为jquery插件

  使用方法:$("selector").scrollLoad();

(function($) {
    $.fn.scrollLoading = function(options) {
        var defaults = {
            // 在html标签中存放的属性名称;
            attr: "data-url",
            // 父元素默认为window
            container: window,
            callback: $.noop
        };
        // 不管有没有传入参数,先合并再说;
        var params = $.extend({}, defaults, options || {});
        // 把父元素转为jquery对象;
        var container = $(params.container);
        // 新建一个数组,然后调用each方法,用于存储每个dom对象相关的数据;
        params.cache = [];
        $(this).each(function() {
            // 取出jquery对象中每个dom对象的节点类型,取出每个dom对象上设置的图片路径
            var node = this.nodeName.toLowerCase(), url = $(this).attr(params["attr"]);
            //重组,把每个dom对象上的属性存为一个对象;
            var data = {
                obj: $(this),
                tag: node,
                url: url
            };
            // 把这个对象加到一个数组中;
            params.cache.push(data);
        });

        var callback = function(call) {
            if ($.isFunction(params.callback)) {
                params.callback.call(call);
            }
        };
        
        //每次触发滚动事件时,对每个dom元素与container元素进行位置判断,如果满足条件,就把路径赋予这个dom元素!
        var loading = function() {
            // 获取父元素的高度
            var contHeight = container.outerHeight();
            var contWidth = container.outerWidth();

            // 获取父元素相对于文档页顶部的距离,这边要注意了,分为以下两种情况;
            if (container.get(0) === window) {
                // 第一种情况父元素为window,获取浏览器滚动条已滚动的距离;$(window)没有offset()方法;
                var contop = $(window).scrollTop();
                var conleft = $(window).scrollLeft();
            } else {
                // 第二种情况父元素为非window元素,获取它的滚动条滚动的距离;
                var contop = container.offset().top;
                var conleft = container.offset().left;
            }

            $.each(params.cache, function(i, data) {
                var o = data.obj, tag = data.tag, url = data.url, post, posb, posl, posr;
                if (o) {
                    //对象顶部与文档顶部之间的距离,如果它小于父元素底部与文档顶部的距离,则说明垂直方向上已经进入可视区域了;
                    post = o.offset().top - (contop + contHeight);
                    //对象底部与文档顶部之间的距离,如果它大于父元素顶部与文档顶部的距离,则说明垂直方向上已经进入可视区域了;
                    posb = o.offset().top + o.height() - contop;

                    // 水平方向上同理;
                    posl = o.offset().left - (conleft + contWidth);
                    posr = o.offset().left + o.width() - conleft;

                    // 只有当这个对象是可视的,并且这四个条件都满足时,才能给这个对象赋予图片路径;
                    if ( o.is(':visible') && (post < 0 && posb > 0) && (posl < 0 && posr > 0) ) {
                        if (url) {
                            //在浏览器窗口内
                            if (tag === "img") {
                                //设置图片src
                                callback(o.attr("src", url));
                            } else {
                                // 设置除img之外元素的背景url
                                callback(o.css("background-image", "url("+ url +")"));
                            }
                        } else {
                            // 无地址,直接触发回调
                            callback(o);
                        }
                        // 给对象设置完图片路径之后,把params.cache中的对象给清除掉;对象再进入可视区,就不再进行重复设置了;
                        data.obj = null;
                    }
                }
            });
        };
        //加载完毕即执行
        loading();
        //滚动执行
        container.bind("scroll", loading);
    };
})(jQuery);








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值