demo is here

前两天严选的活动一个页面中图片比较多,boss让我用lazyload来提升页面性能,不过因为时间等种种原因,我还是没来得及加lazyload,没错网上的确有lazyload插件,不过那个插件(似乎)不能很好的兼容背景图,更主要的原因是我非常想自己写一个也不是很难对吧,我就开始了这个jquery插件的创造。并尽我所能进行了插件的性能优化。

首先,我们应该明白我们为什么需要lazyload,假设有两张图片在html文档的中间或者末尾,那么用户如果不往下拉,是根本看不到这两张图片的,那么在用户刚刚访问页面的时候就加载这两个图片会阻塞js执行,影响用户体验。所以我们就可以动态监测哪些图片用户即将看到(也就是距离浏览器窗口的下边缘很近)我们再加载这些图片。

lazyload的原理是这样的,通过定时触发函数检测距离浏览器窗口下面很近的地方是不是有图片,如果有的话,就给他的src或者css的background-image属性赋值。这句话令人受益匪浅,而图片真实的地址来自于标签的属性。比如有有两个标签原来打算这么写:

我用了jquery,其实原生也一样的,jquery写起来舒服一些。

<!--图片-->
<img src="one.jpg"/>
<!--背景-->
<div class="two"></div>

与之搭配的css是

.two{
    background-image:url("two.jpg");
}

为了lazyload它们,我们一开始不能写出它们的src和css的background-image属性,可以写成这样:

<img loadsrc="ong.jpg"/>
<div loadsrcbg="two.jpg"/>

浏览器是不认识loadsrc和loadsrcbg属性的,所以当然不会解析。

接下来就是判断这个图片的位置是否合适了,这里需要掌握这样几个属性:

window.innerHeight//窗口高度
window.pageYOffset//窗口距离顶部的距离
$("#a").offsetTop == $("#a").position().top()//某个元素距离顶部的距离

用一张简明的图片来表示就是这样的:

好弄明白了这个关系接下来应该不难理解什么时候该加载怎样的一张图片了,那么核心代码就在这里:

if($(this).position().top < viewportTop + window.lazyLoad.windowHeight + window.lazyLoad.imgDistance){
    if(!!$(this).attr('loadSrc')){
        $(this).attr('src',$(this).attr('loadsrc'));
    }else{
        $(this).css("background-image",'url('+($(this).attr('loadsrcbg'))+')');
    }
}

接下来就是性能优化的事情,具体的思路大概分为两步。

首先,既然是定时调用lazyload的函数,那么我不希望每次都把已经加载过的图片都重新赋值一遍,因此可以在每次为图片地址赋值之后记录下这个index,以后的每次循环都丛这个index之后曲循环,这样已经加载的图片我们就不用管了。

其次,我不想每次都获取全局的所有可以lazyload的元素,因此我们可以循环调用某个函数,但是在第一次时候初始化所有要lazyload的元素并保存在一个属性中,然后对这个函数本身进行重写,这是一种什么模式来着……不太记得。

嗯,就是这么多,我把源代码贴在下面~

'use strict';
// 懒加载方法
var lazyLoad = {
    // 图片距离窗口多远开始加载,单位px
    imgDistance : 100,
    // 窗口高度
    windowHeight : window.innerHeight,
    // 已经加载的图片数
    imgs : '',
    imgLoadedNum : 0,
    init : function () {
        //一些参数的初始化
        console.log(1);
        this.imgs = $("img");
        var divs = $("div[loadSrcBg]");
        // 放到一个数组里面
        for (var i=0;i<divs.length;i++){
            this.imgs.push(divs[i]);
        }
        //运行过一次后即可重写此函数
        this.init = this.go;
    },
    go : function () {
        // 窗口顶部距离顶部多远
        var viewportTop = window.pageYOffset,
            // 窗口底部的当前位置
            windowBottom = viewportTop + this.windowHeight;

        // 如果全都加载了,则return
        if(this.imgLoadedNum == this.imgs.length){
            return;
        }

        // 循环检测图片是否应该加载
        this.imgs.each(function(index){
            //如果已经加载,则跳过
            if(index>=window.lazyLoad.imgLoadedNum){
                //如果图片距离 < 窗口下边加上预设距离,则加载
                if($(this).position().top < viewportTop + window.lazyLoad.windowHeight + window.lazyLoad.imgDistance){
                    console.log(index);
                    if(!!$(this).attr('loadSrc')){
                        $(this).attr('src',$(this).attr('loadsrc'));
                    }else{
                        $(this).css("background-image",'url('+($(this).attr('loadsrcbg'))+')');
                    }
                    //记录以便节省性能
                    window.lazyLoad.imgLoadedNum = index;
                }
            } 
        })
    }
}

欢迎使用~