llazy.js:小而无依赖的lazyload实现

在线预览(懒更新…)

github地址:https://github.com/wslx520/llazy/

图片懒加载,是一个非常实用且使用非常广泛的功能。由于历史悠久,所以此功能的实现也达成了不少共识,我先简单列举几点:

  1. 需要后端配合:在输出html代码时,不能直接将图片真实地址赋给img.src,而是用自定义属性保存起来,图片的初始src里放一个占位图
  2. 无论是从左往右,从右往左,从上向下,从下向上,都需要能动态lazyload
  3. 初始图片(占位图)如果有明确的宽高信息就好了,不然load时会发生抖动(这也要靠后台,在输出时输出width与height)

网上有太多太多的实现,大部分都很好,完全可以满足使用需求。不过我是一个任何事情都想自已尝试下用起来心中才踏实的人,所以花了点时间自己写了个llazy.js

兼容性

网上有很多jquery的lazyload插件,而做成jq插件,就可以兼容到jq兼容的浏览器,因为最难兼容的就是选择器了。而llazy不想有依赖,所以只兼容到IE8。为什么?因为IE8就支持querySelector了啊!

判断load的时机

很多lazyload插件在判断load时机时都比较麻烦。其实load的时机从人的角度来说很简单,即:图片框出现在了可见区,就可以load了。关键就在于,如何判断图片出现在了可见区。

对这个问题,大家各显神通,什么 offset, client, page, screen, scroll等属性一长串,能把人看晕。甚至有的人实现出来才发现,只能从上向下拖时触发load,其余情况又不兼容,非常尴尬。

而我就在想,图片,是一个矩形;图片的容器,也是一个矩形。当这两个矩形相交时,不就表示可以触发load了吗?

于是google了一下:如何判断两个矩形相交,获得了一个公式——虽然公式是c语言的,但套入JS一点困难也木有。

1
2
3
4
5
// 判断两个矩形是否相交
    function cross(r1, r2) {
        return (Math.abs((r1.left + r1.right) / 2 - (r2.left + r2.right) / 2) < ((r1.right + r2.right - r1.left - r2.left) / 2)
            && Math.abs((r1.top + r1.bottom) / 2 - (r2.top + r2.bottom) / 2) < ((r1.bottom + r2.bottom - r1.top - r2.top) / 2));
    }

而有了这个公式,就一举解决了从左往右,从右往左,从上向下,从下向上滚动的问题,因为此公式不管其他,只管是否相交。

还有个问题就是,这个公式需要两个矩形,在JS中,如何分别准确获得图片与图片容器的矩形呢?答案就是getBoundingClientRect,兼容性非常好。

其他不算最重要的

譬如,性能。

托电脑科技日新月益的福,我们现在写代码不用一开始就纠结“会不会太耗资源”的问题了。所以,llazy并没有考虑性能问题————我也不认为llazy会造成性能问题。如果发现了,我再改也来得及,源码就那么几句

自从用了gulp, webpack等工具后,有人已经将一个项目用不用自动化构建工具视为个人能力水平的展现,但llazy太简单了,就一个html一个js;js带注释才3K,也不需要uglify;代码太少,也完全用不着用es6写又转成es5。所以,完全用不上这些工具。倒是让我“返璞归真”了一把。

可以优化与可以增加的功能

1. 动画载入图片

依赖于jq的lazyload插件,除了选择器不用担心外,还可以使用jq的动画效果,从而直接给load时添加“淡入”等动画。而llazy并不想手写动画,我倒是打算为它增加一个选项,即“load触发时的回调”,你可以传任何函数进来。比如,要实现“淡入”,你可以为图片添加css3动画,或手写JS动画。此功能目前已加上.

你甚至可以在img上手动添加一个onerror回调,实现载入失败时重载的功能

2. 优化

llazy还有个可以优化的点。在现在的源码中,滚动时会遍历所有指定的要lazyload的图片(当然,已load的是直接跳过的),而这实际上是不必要的。比如,从下向下拖时,循环判断出有3张达到了load的条件,突然出现了一张load的条件为false,此时就可以终止loop了,避免多余的getBoundingClientRect计算。

但要优化这个问题,就涉及到4个滚动方向的判断。而图片列表也可能因为css的原因不按照源码中的位置排序,所以算起来,要实现这个优化,性价比不高。

后记

在实现过程中还发现,document.documentElement 并没有 onscroll事件;document.body 的 onscroll 兼容性也不好。所以,默认 container 是 document.documentElement 或 body 时,则要将 onscroll 监听加在 window 上

我先用着llazy,要增加功能可以视使用情况来定。不过我决定,未压缩前代码量<5k,是我给它优化与添加功能的底限 llazy的github地址:https://github.com/wslx520/llazy/

发表评论

电子邮件地址不会被公开。