在网上经常能看到性能优化关于图片懒加载的文章,自己没有真正的实践过,今天任务开发完,利用了下这个时间,便自己写了一个demo实现了简单的图片懒加载功能。实现的效果如下
说一下实现的思路
- 假设页面容器内需要放置多张图片列表,先在各个img标签上加上src=“默认未加载列表中展示的图片”,data-src=“后面加载需要用到的图片地址”。例如这样
<img data-src="./assest/test.webp" src="./assest/default.webp"/>
/**
data-*属性是HTML5的新标签,
它可以让我们在所有html元素上嵌入自定义数据属性的能力,
后续我们便可以利用该属性在HTML和DOM之间实现专有数据交换。
*/
- 我们首先获取容器中图片列表的长度、容器的可视高度、以及单个img实例对应的高度。对clientHeight容器宽度高度属性还不清楚的建议可以看下这篇
// 获取图片列表长度
const length = document.getElementsByTagName("img").length;
// 获取图片示例
const img = document.getElementsByTagName("img");
// 获取容器实例
const pageContent = document.getElementsByClassName("page-content")[0];
// 获取可见区域高度
const clientHeight = pageContent.clientHeight;
// 获取单个img标签所占高度
const imgHeight = img[0].height;
// 用于判断容器内可见区域内可容纳多少张图片, 图片加载的位置
let index = Math.ceil(clientHeight / imgHeight);
// 页面初始化时的滚动距离,后续用于判断用户进行的是往下滚动还是向上滚动操作。只有向下滚动才需要去进行懒加载事件。
let beforeScrollTop = pageContent.scrollTop;
- 定义一个懒加载方法,里面需要先获取滚动的距离。对于最上边的框距离可视区域的高度小于(可视区域高度 + 滚动的距离)的图片时,进行对应的懒加载赋值操作。把先前data-src属性值赋值给src属性。
function lazyLoad() {
// 获取监听后滚动条距离顶部高度
const scrollTop = pageContent.scrollTop;
// 获取两次滚动的距离,用于判断浏览器是向上滚动还是向下滚动。向下滚动才触发加载事件。
let isUpDown = scrollTop - beforeScrollTop;
if (isUpDown < 0) {
console.log("上滚");
return;
} else {
console.log("下滚");
beforeScrollTop = scrollTop;
for(let i = 0; i < length; i ++) {
if (img[i].offsetTop < (clientHeight + scrollTop)) {
if (img[i].getAttribute("src") === "./assest/default.webp") {
img[i].src = img[i].getAttribute("data-src");
}
index = i + index;
}
}
}
}
4.这样已经能实现如文章开头的样子了,但是有一个问题就是,浏览器监听事件,只要我滚动就会去触发,这样很消耗性能。所以我们还可以加一个节流事件,让用户的滚动操作隔一段时间才触发。
function throttle(fn, delay) {
let valid = true;
return function() {
if (valid) {
setTimeout(() => {
fn.apply(this,arguments);
valid = true;
}, delay);
}
valid = false;
}
}
//最后再为该容器添加监听事件就好啦
pageContent.addEventListener("scroll", throttle(lazyLoad, 1000));
完整的代码在这里
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>图片懒加载</title>
</head>
<style>
body{
padding: 20px 0 0;
margin: 0;
background-color: antiquewhite;
width: 100%;
height: 100vh;
display: flex;
justify-content: center;
}
.page-content{
width: 30%;
height: 500px;
background-color: white;
padding: 20px;
overflow: auto;
}
img{
width: 100%;
height: 200px;
margin-bottom: 20px;
}
</style>
<body>
<div class="page-content">
<img data-src="./assest/test.webp" src="./assest/default.webp"/>
<img data-src="./assest/test.webp" src="./assest/default.webp"/>
<img data-src="./assest/test.webp" src="./assest/default.webp"/>
<img data-src="./assest/test.webp" src="./assest/default.webp"/>
<img data-src="./assest/test.webp" src="./assest/default.webp"/>
<img data-src="./assest/test.webp" src="./assest/default.webp"/>
<img data-src="./assest/test.webp" src="./assest/default.webp"/>
<img data-src="./assest/test.webp" src="./assest/default.webp"/>
<script>
// 获取图片列表长度
const length = document.getElementsByTagName("img").length;
// 获取图片示例
const img = document.getElementsByTagName("img");
// 获取容器实例
const pageContent = document.getElementsByClassName("page-content")[0];
// 获取可见区域高度
const clientHeight = pageContent.clientHeight;
// 获取单个img标签所占高度
const imgHeight = img[0].height;
// 用于判断容器内可见区域内可容纳多少张图片, 图片加载的位置
let index = Math.ceil(clientHeight / imgHeight);
// 页面初始化是的滚动距离
let beforeScrollTop = pageContent.scrollTop;
lazyLoad();
// 为容器添加滚动监听事件
pageContent.addEventListener("scroll", throttle(lazyLoad, 1000));
function lazyLoad() {
// 获取监听后滚动条距离顶部高度
const scrollTop = pageContent.scrollTop;
// 获取两次滚动的距离,用于判断浏览器是向上滚动还是向下滚动。向下滚动才触发加载事件。
let isUpDown = scrollTop - beforeScrollTop;
console.log(beforeScrollTop, scrollTop, isUpDown)
if (isUpDown < 0) {
console.log("上滚");
return;
} else {
console.log("下滚");
beforeScrollTop = scrollTop;
for(let i = 0; i < length; i ++) {
console.log(img[i].offestTop, clientHeight, scrollTop)
if (img[i].offsetTop < (clientHeight + scrollTop)) {
if (img[i].getAttribute("src") === "./assest/default.webp") {
img[i].src = img[i].getAttribute("data-src");
}
index = i + index;
}
}
}
}
function throttle(fn, delay) {
let valid = true;
return function() {
if (valid) {
setTimeout(() => {
fn.apply(this,arguments);
valid = true;
}, delay);
}
valid = false;
}
}
</script>
</div>
</body>
</html>