阅读 213

实现懒加载的四种方式

个人封装常用方法工具包: github.com/lihai-boop/…

文章已收录: github.com/lihai-boop/…

1. 滚动距离计算

offsetTop,scrollTop,clientHeight原生API获取距离像素计算

offsetTop:获取当前元素与其父级元素顶部内边距的距离

scrollTop:当前元素滚动超出的高度

clientHeight:当前元素的可视高度

"灵魂画手"绘图

img元素offsetTop - 父级元素scrollTop<父级元素clientHeight,即说明img已滚动进入父可视区域

#view {
    width: 300px;
    height: 500px;
    margin: 30px auto;
    overflow-y: auto;
}

.parent {
    height: 1000px;
}

img {
    margin-top: 800px;
}
复制代码
<div id="view">
    <div class="parent">
        <img alt="..." loading="lazy" class="img"
             data-src="https://tse1-mm.cn.bing.net/th?id=OIP-C.FaG6dzohGs3q45-DwsEyQQHaEK&w=165&h=100&c=8&rs=1&qlt=90&o=6&pid=3.1&rm=2">
    </div>
</div>
复制代码
let view = document.getElementById('view');
let img = document.getElementsByClassName('img')[0];
view.onscroll = function () {
    let sh = this.clientHeight;
    let st = this.scrollTop;
    let ch = img.offsetTop;
    ch - st < sh && (img.src = img.dataset.src)
}

复制代码

用户频繁滚动鼠标时,会多次触发onscroll事件计算滚动距离,消耗性能又没有意义,所以需要控制函数执行的次数,减少性能的消耗,对滚动事件进行防抖处理

开发中为减少反复写防抖函数,已经将它封装入@lihai-js/tool, 开箱即用

@lihai-js/tool地址github.com/lihai-boop/…

let img = document.getElementsByClassName('img')[0];
let view = document.getElementById('view');
function debounce (fn, wait = 1000, immediate = false) {
    const self = this
    immediate && fn.call(this)
    return function () {
        self.debounceTime && clearTimeout(self.debounceTime)
        self.debounceTime = setTimeout(() => {
            fn.apply(this, arguments)
        }, wait)
    }
}

view.onscroll =debounce(function () {
    let sh = view.clientHeight;
    let st = view.scrollTop;
    let ch = img.offsetTop;
    ch - st < sh && (img.src = img.dataset.src)
}) 
复制代码

2. getBoundingClientReat方法计算

Element.getBoundingClientReat()方法返回元素的大小及其相对于视口的位置。

该方法返回一个DOMRect对象,对象包括left, top, right, bottom, x, y, width, 和 height这8个属性值。除了widthheight 以外的属性是相对于视图窗口的左上角来计算的。

Element.getBoundingClientRect() - Web API 接口参考 | MDN (mozilla.org)

滚动计算距离列举的代码例子来说,可视窗口为浏览器窗口,对imgdom对象使用getBoundingClientReat()获取top,top即为浏览器窗口左上角到img标签左上角的像素距离点

利用上述点,当top值小于浏览器窗口高度时,说明img已滚动于可视区域,即进行加载

#view {
    width: 300px;
    height: 500px;
    margin: 30px auto;
    overflow-y: auto;
}

.parent {
    height: 1000px;
}

img {
    margin-top: 800px;
    margin-bottom: 30px;
}
复制代码
<div id="view">
    <img alt="..." class="img" data-src="https://p.pstatp.com/origin/pgc-image/c206105e0c46482a85b69a67a59ce682" />
</div>
复制代码
let img = document.getElementsByClassName('img')[0];
let view = document.getElementById('view');
//省略防抖代码
window.onscroll = debounce(() => {
    let clientHeigth = view.clientHeight;
    let { top } = img.getBoundingClientRect();
    top < clientHeigth && (img.src = img.dataset.src);
})

view.onscroll =debounce(function () {
    let sh = view.clientHeight;
    let { top } = img.getBoundingClientRect();
    top<sh && (img.src = img.dataset.src)
})
复制代码

3. IntersectionObserver监听

IntersectionObserverAPI原理与2. getBoundingClientReat方法计算一样,官方认为图片懒加载,开发者既要获取top值,又要监听滚动事件,还要进行防抖处理,一系列操作麻烦不方便。为了简化它,官方提供了IntersectionObserver接口。

IntersectionObserver(callback[, options])

当监听的元素达到要求后,触发callback回调函数,它接收两个参数

IntersectionObserver API 使用教程 - 阮一峰的网络日志 (ruanyifeng.com)

let img = document.getElementsByClassName('img')[0];
let view = document.getElementById(view);
let ob = new IntersectionObserver(function (changes) {
    changes.forEach(val => {
        let { isIntersecting, target } = val;
        isIntersecting && (target.src = target.dataset.src);
    })
}, {
    root: view,
    threshold: [0]
})
ob.observe(img);
复制代码

4.<img> 标签属性loading

兼容性差,不推荐

<img>标签属性loding,可选值:

eager:立即加载图像

lazy:延迟加载图像,直到它和视口接近到一个计算得到的距离,由浏览器定义

详细介绍:developer.mozilla.org/zh-CN/docs/…

<div class="parent">
    <img loading="lazy"
         src="https://tse1-mm.cn.bing.net/th?id=OIP-C.FaG6dzohGs3q45-DwsEyQQHaEK&w=165&h=100&c=8&rs=1&qlt=90&o=6&pid=3.1&rm=2"  alt="...">
</div>
复制代码

最后

期待各位同学指点补充!

觉得文章有用的同学点赞一波!

文章分类
前端
文章标签