懒加载定义
懒加载是一种设计模式, 被运用在软件设计和网页设计当中, 对于网页界面, 其特征为用户透过鼠标, 滚动浏览页面, 直到页面下方时, 就会自动加载更多内容; 有多数网站采用这项网页设计, 例如 Google 图片搜索, Google+,Facebook,Twitter,Pinterest[6]和维基百科的 Flow 讨论系统. 也有结合无限滚动和多页, 两著特性的网页设计[7]. -- 来自维基百科
图片懒加载的好处
1. 首先它能提升用户的体验, 试想一下, 如果打开页面的时候就将页面上所有的图片全部获取加载, 如果图片数量较大, 对于用户来说简直就是灾难, 会出现卡顿现象, 影响用户体验.
2. 有选择性地请求图片, 这样能明显减少了服务器的压力和流量, 也能够减小浏览器的负担.
图片懒加载的实现原理
页面在浏览器中打开, img 标签的 src 属性会自动请求其图片地址, 实现懒加载我们先要使用自定义属性 (例: data-src) 来避免 img 的自动加载, 也相当于把图片地址存储在当前标签里. 然后 JS 监听父元素 scroll 事件, 当此图片出现在可视区域后, JS 把自定义 src 设置为图片 src.
接下来让我们看一下基于 jQuery 的实现
- $.fn.lazyload = function (options) {
- var defaults = {
- // 在 html 标签中存放的属性名称;
- attr: "data-url",
- // 父元素默认为 Windows
- container: Windows,
- 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);
- });
- console.log(params)
- var callback = function (call) {
- if ($.isFunction(params.callback)) {
- // params.callback.call(call);
- }
- };
- let canRun = true;
- // 每次触发滚动事件时, 对每个 dom 元素与 container 元素进行位置判断, 如果满足条件, 就把路径赋予这个 dom 元素!
- var loading = function () {
- if(!canRun){
- return
- }
- canRun = false;
- // 获取父元素的高度
- var contHeight = container.outerHeight();
- var contWidth = container.outerWidth();
- // 获取父元素相对于文档页顶部的距离, 这边要注意了, 分为以下两种情况;
- if (container.get(0) === Windows) {
- // 第一种情况父元素为 Windows, 获取浏览器滚动条已滚动的距离;$(Windows)没有 offset()方法;
- var contop = $(Windows).scrollTop();
- var conleft = $(Windows).scrollLeft();
- } else {
- // 第二种情况父元素为非 Windows 元素, 获取它的滚动条滚动的距离;
- var contop = container.offset().top;
- var conleft = container.offset().left;
- }
- console.log('contHeight:'+contHeight,'contWidth:'+contWidth,'contop:'+contop,'conleft:'+conleft);
- $.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;
- console.log(post,posb,posl,posr)
- // 只有当这个对象是可视的, 并且这四个条件都满足时, 才能给这个对象赋予图片路径;
- 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;
- }
- }
- });
- setTimeout(()=>{
- canRun = true;
- },300)
- };
- // 加载完毕即执行
- loading();
- // 滚动执行
- container.bind("scroll", loading);
- };
- $("img").lazyload({attr:'data-src'})
我们可以看到调用 lazyload 方法后传入自定义 src 参数, 我们会先遍历当前 jQuery 选中的元素并取出自定义 src 的 value,nodeName,this 指针然后存储到 cache 数组里, 接下来创建 loading 函数, 重点来了我们怎么判断当前图片元素是否在可视区域:
1. 先取出父元素的高度和当前距顶部的距离;
2. 循环 cache 数组, 获取每个元素的 this;
3. 计算
post = 元素距离顶部的距离 - (父距顶部的距离 + 父可视高度)
posb = 元素距离顶部的距离 + 元素的高度 - 父距顶部的距离;
4. 判断如果 post <0 && posb> 0 我们就是在垂直方向看到了此元素;
5. 可以在此判断下根据指针将自定义 src 替换为 src;
然后我们把父元素绑定 scroll 事件监听函数为 loading, 这样每次滚动 loading 函数都会进行计算把位于次区域的图片展示出来.
上面我们分析的是垂直方向判断 水平方向同理, 还有一点我们可以看到我们在上面的实现中使用了简单的函数节流, 本文不再过多赘述.
接下来我还将要写一些 vue-lazyload 的源码分析等, 有兴趣可以关注我.
来源: http://www.jianshu.com/p/d28ff957bcfe