一直都想自己实现这个功能而不是知道原理不实践,这次突然看到掘金上的文章后,跟着走了一遍代码,原来不管什么效果只知道原理是远远不够的,你必须亲自实现一下,然后才知道其中的坑,也会学到更多。程序员就要勤于动手。

先放一张自己画的图,理清楚这几个高度的关系。

clientHeight

  • 认识一个函数let rectObj = element.getBoundingClientRect()
    • 返回的对象rectObj.top 表示的是 该元素 距离视口(浏览器窗口)的距离,即图中的 clientBoundRect.top
    • 视口的高度 document.documentElement.clientHeight
    • document.body.clientHeight 获取的 网页的高度

这里参考了一下阮一峰大大的 获取元素 的绝对位置 和 相对位置

  • 相对位置
    element.getBoundingClientRect().top
  • 绝对位置
    相对位置 + 网页滚动过的距离
    element.getBoundingClientRect().top + document.documentElement.scrollTop

传统的 监听 Scroll 事件

在 scroll 事件中 我们只需要判断一下,元素距离浏览器窗口顶部的距离是否小于浏览器窗口的高度,就可以判断出 图片是否进入了 可视区域,
然后再使用节流函数, 每隔 200ms 调用一次 函数, 节省开销

lazy.js
  • js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//所有的图片
this.lazyImg.forEach((val,idx) => {
let rectObj = val.getBoundingClientRect();
// 如果当前元素距 视口 的距离 小于 视口的 高度
// 此时元素处于可见范围
if(rectObj.top < document.documentElement.clientHeight){
let actualSrc = val.dataset.src;
val.setAttribute('src',actualSrc);
val.removeAttribute('class');
// 当滚动到最后一个图片后 移除 scroll 事件
if(idx === this.lazyImg.length - 1){
window.removeEventListener('scroll', window.scrollFun);
}
}
});

节流函数

Get: 重新认识了一下 节流函数 记忆理解更深了一层

具体的节流函数可以查看我之前写的一篇防抖节流的文章

throttle.js
  • js
  • js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function throttle(fn, delay){
let start = +new Date(),
that = this,
timer = null;
return function(args){
let _args = args,
now = +new Date();
if(now - start > delay){
start = now;
clearTimeout(tiemr);
timer = setTimeout(() = > {
fn.apply(that, _args);
}, delay);
}
}
}

新的 Intersection Observer API

使用分俩步:

  • 新建 Intersection Observer 观察对象 oberver

    observer.js
    • js
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    // new IntersctionObserver(callback, options)
    let observer = new IntersctionObserver((entries, observer)=> {
    entries.forEach(entry => {
    // entry.isIntersecting 标识元素是否进入可见区域
    // entry.target 观察的当前元素
    if(entry.isIntersecting){
    let _target = entry.target;
    let actualSrc = _target.getAttribute('data-src');
    _target.removeAttribute('class');
    _target.setAttribute('src', actualSrc);
    }
    });
    });
  • observer.observe(element) 观察元素

    observer.js
    • js
    1
    this.lazyImg.forEach(img => observer.boserve(img));

参考链接

intersectionObserver - MDN
阮一峰的网络日志
掘金