传统方案

监听scroll事件,判断图片是否在可视区域内(通过getBoundingClientRect获取图片位置信息)

缺点:scroll事件十分频繁,计算量大,容易造成性能问题,可以通过节流处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const viewHeight = window.innerHeight || document.documentElement.clientHeight
const imgs = document.querySelectorAll('img')
let threshold = 100
window.onload = lazyLoad
document.addEventListener('scroll', function (e) {
lazyLoad()
})
function lazyLoad () {
imgs.forEach(img => {
let { top } = img.getBoundingClientRect()
if (img.dataset.src && top - viewHeight < threshold) {
img.src = img.dataset.src;
img.removeAttribute('data-src')
}
})
}

源码

利用IntersectionObserver API实现的方案

关于IntersectionObserver的使用方法,可以参考阮一峰的文章IntersectionObserver API 使用教程

这种方案是去生成一个观察者,判断它观察的dom元素是否在视口之内

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
let imgs = document.querySelectorAll('img')
let imgsLen = imgs.length
let count = 0
let eachRetryTimes = 3 // 每张图片可以重试三次
// 加载后的处理
function addOneSuccess (observer, target) {
target.removeAttribute('data-src')
target.removeAttribute('data-retry')
observer.unobserve(target)
if (count === imgsLen) { // 全都加载完毕,可以关闭观察器
observer.disconnect()
}
}
// 目标元素的可见性变化时,就会调用观察器的回调函数callback。
// callback一般会触发两次。一次是目标元素刚刚进入视口(开始可见),另一次是完全离开视口(开始不可见)。
let observer = new IntersectionObserver((entries) => {
entries.forEach(e => {
// 如果同时有两个被观察的对象的可见性发生变化,entries数组就会有两个成员。
// intersectionRatio表示目标元素的可见比例,即intersectionRect占boundingClientRect的比例,完全可见时为1,完全不可见时小于等于0
if (e.intersectionRatio > 0 && e.target.dataset.src) { // 可见
e.target.src = e.target.dataset.src
let retry = Number(e.target.dataset.retry || 0)
e.target.setAttribute('data-retry', ++retry)
e.target.onload = function () {
addOneSuccess(observer, this)
}
e.target.onerror = function () {
let retry = this.dataset.retry // 重试次数
if (Number(retry) === eachRetryTimes) {
addOneSuccess(observer, this)
}
}
}
})
})
imgs.forEach(img => {
observer.observe(img)
})
imgs = null

源码

【参考】

IntersectionObserver API 使用教程

感谢您的阅读,本文由 Astar 版权所有。如若转载,请注明出处:Astar(http://example.com/2022/01/19/%E5%9B%BE%E7%89%87%E6%87%92%E5%8A%A0%E8%BD%BD%E7%9A%84%E4%B8%A4%E7%A7%8D%E5%AE%9E%E7%8E%B0%E6%96%B9%E6%A1%88/
前端生成CHANGELOG指南
css样式没生效问题排查