这是我参与「第五届青训营 」伴学笔记创作活动的第 13 天
懒加载
顾名思义,就是图片进入可视区再加载图片。涉及的知识点有节流、IntersectionObserver
通过事件实现
主要是获取当前的可视区域高度和元素距离可视区域顶部的高度,假设我们这里有一些图片
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Lazy-Load</title>
<style>
.img {
width: 200px;
height:200px;
background-color: gray;
}
.pic {
// 必要的img样式
}
</style>
</head>
<body>
<div class="container">
<div class="img">
// 注意我们并没有为它引入真实的src
<img class="pic" alt="加载中" data-src="./images/1.png">
</div>
<div class="img">
<img class="pic" alt="加载中" data-src="./images/2.png">
</div>
<div class="img">
<img class="pic" alt="加载中" data-src="./images/3.png">
</div>
<div class="img">
<img class="pic" alt="加载中" data-src="./images/4.png">
</div>
<div class="img">
<img class="pic" alt="加载中" data-src="./images/5.png">
</div>
<div class="img">
<img class="pic" alt="加载中" data-src="./images/6.png">
</div>
<div class="img">
<img class="pic" alt="加载中" data-src="./images/7.png">
</div>
<div class="img">
<img class="pic" alt="加载中" data-src="./images/8.png">
</div>
<div class="img">
<img class="pic" alt="加载中" data-src="./images/9.png">
</div>
<div class="img">
<img class="pic" alt="加载中" data-src="./images/10.png">
</div>
</div>
</body>
</html>
const viewHeight = window.innerHeight || document.documentElement.clientHeight
document.documentElement主要是为了兼容低版本的ie,如果不需要,直接用window获取也行
那元素的距离可视顶部的高度如何获取呢?我们可以利用 getBoundingClientRect() 来获取,也可以通过获取dom元素,然后获取他的offsetTop。
getBoundingClientRect().top 和 offsetTop 都是 JavaScript 中用于获取 DOM 元素顶部相对于页面顶部或父元素顶部的距离的方法。但是,它们的工作方式和结果略有不同。前者是相当于视口的顶部,而后者是相对于父元素的顶部,所以说,用getBoundingClientRect更加合适。
这里我们我们来实现一下
<script>
// 获取所有的图片标签
const imgs = document.querySelector('img')
// 获取可视区域的高度
const viewHeight = window.innerHeight || document.documentElement.clientHeight
// num用于统计当前显示到了哪一张图片,避免每次都从第一张图片开始检查是否露出
let num = 0
function lazyload(){
for(let i=num; i< imgs.length; i++) {
// 用可视区域高度减去元素顶部距离可视区域顶部的高度
let distance = viewHeight - imgs[i].getBoundingClientRect().top
// 如果可视区域高度大于等于元素顶部距离可视区域顶部的高度,说明元素露出
if(distance >= 0 ){
// 给元素写入真实的src,展示图片
imgs[i].src = imgs[i].getAttribute('data-src')
// 前i张图片已经加载完毕,下次从第i+1张开始检查是否露出
num = i + 1
}
}
}
// 监听Scroll事件
window.addEventListener('scroll', lazyload, false);
</script>
IntersectionObserver API
Intersection Observer API 提供了一种异步检测目标元素与祖先元素或 viewport 相交情况变化的方法。
下面注册全局自定义指定v-lazyload
Vue.directive('lazyload', {
bind: function (el, bindVal) {
const lazyLoadObser = new IntersectionObserver((entries, observer) => {
entries.forEach((entry, index) => {
const lazyImg = entry.target
// intersectionRatio:目标元素的可见比例,
// 即intersectionRect占boundingClientRect的比例,
// 完全可见时为1,完全不可见时小于等于0
if (entry.intersectionRatio > 0) {
lazyImg.src = bindVal.value
console.log(111)
lazyLoadObser.unobserve(lazyImg)
}
})
})
lazyLoadObser.observe(el)
}
})
原生js写法
function query(selector) {
return Array.from(document.querySelectorAll(selector));
}
var observer = new IntersectionObserver(
function(changes) {
changes.forEach(function(change) {
var container = change.target;
var content = container.querySelector('template').content;
container.appendChild(content);
observer.unobserve(container);
});
}
);
query('.lazy-loaded').forEach(function (item) {
observer.observe(item);
});
上面代码中,只有目标区域可见时,才会将模板内容插入真实 DOM,从而引发静态资源的加载。