当前位置:网站首页>图片的懒加载
图片的懒加载
2022-07-25 14:59:00 【盖玥希圈外男友(섭소우)】
目录
需求
工作的时候暂时没有活,所以写个懒加载的踩坑记录。需求就是最平常的图片懒加载,使用一个数组存储所有图片的地址,等到图片出现在可视区域内的时候才去设置其src属性,这样图片才会加载。
思路分析
判断图片是否在可视区域内
这个是最关键的地方,只有正确判断图片中可视区域中才可以正确加载图片。img是图片,parent是其容器,我的具体判断公式如下:
img.offsetHeight + img.offsetTop > parent.scrollTop && img.offsetTop < parent.scrollTop + parent.clientHeight
公式分为两部分。前半部分是判断图片是否能够进入可视区域,后半部分是判断图片是否离开可视区域。其逻辑可以画图捋一下,不再细说。
图片设置
对于一个图片,我们一开始不想加载它,所以用background设置背景图片作为占位。并且,我们还要把它的地址存到它的身上,以便于加载。可以考虑存在元素的dataset中,比如img.dataset.src。这样,对于每个图片,应该做如下设置:
<img data-src="https://acg.toubiec.cn/random.php?1" class="lazy">这是使用原生HTML写的,如果采用vue等框架可以直接遍历数组地址产生img。还需要给img标签指定类名,我写为lazy。然后给lazy类设置统一样式:
.lazy {
width: 400px;
height: 400px;
background-image: url("https://cn.vuejs.org/images/logo.svg");
background-repeat: no-repeat;
background-size: contain;
margin-bottom: 50px;
}这里用背景实现占位,background-image就是默认图片。后面如果给图片设置了src,就可以盖住背景了。
父元素设置
我简单使用一个类名为container的div包裹图片:
<div class="container">
<img data-src="https://acg.toubiec.cn/random.php?1" class="lazy">
<img data-src="https://acg.toubiec.cn/random.php?2" class="lazy">
<img data-src="https://acg.toubiec.cn/random.php?3" class="lazy">
<img data-src="https://acg.toubiec.cn/random.php?4" class="lazy">
<img data-src="https://acg.toubiec.cn/random.php?5" class="lazy">
<img data-src="https://acg.toubiec.cn/random.php?6" class="lazy">
</div>然后给container类设置样式:
.container {
background-color: floralwhite;
width: 60vw;
height: 100%;
overflow-y: auto;
margin: 0 auto;
display: flex;
flex-direction: column;
align-items: center;
}这样,所有图片就是水平居中排列的了。
滚动事件回调函数
要实现懒加载,必须监听滚动事件,在监听函数中判断哪些图片在可视区域,然后加载它。刚刚已经实现了判断图片是否在可视区域的逻辑,现在封装滚动事件的回调函数:
// 判断图片是不是在可视区域内
// parent:父元素 img:图片元素
function ifInview(parent, img) {
return img.offsetHeight + img.offsetTop > parent.scrollTop && img.offsetTop < parent.scrollTop + parent.clientHeight
}
// 滚动事件的回调函数
function load() {
console.log('load')
let parent = document.getElementsByClassName('container')[0];
let img_list = document.getElementsByClassName('lazy');
for (let i = 0; i < arr.length; i++) {
let img = img_list[i];
if (ifInview(parent, img)) {
img.src = img.dataset.src;
}
}
}然后给父元素绑定scroll事件监听即可:
let container = document.getElementsByClassName('container')[0]
container.addEventListener('scroll', load);大功告成。
问题
写完这些代码发现,滚动的时候并没有触发scroll事件的回调函数。这是为什么?
检查代码,发现问题:在container中,给它设置高度为100%。container的高度继承自body,body继承自html。html中包含了6个图片,因此它就具有6个图片的高度,因此container就具有6个图片的高度。因此,对于container来讲,它根本没有溢出,所以它的overflow-y:auto根本就没有起作用!!!
解决方法就是,给container设置一个固定高度,我设为100vh。
设置后发现仍然不能触发。再细读,发现我对container使用了flex布局,这样就导致子元素的高度被限制在父元素内,无法超过父元素的高度,自然也就无法触发scroll事件。因此,需要放弃flex布局。我将img设置为块元素,然后让它们居中显示,效果一样。
改动后的代码:
.container {
background-color: floralwhite;
width: 60vw;
height: 100vh;
overflow-y: scroll;
margin: 0 auto;
}
.lazy {
width: 400px;
height: 400px;
display: block;
background-image: url("https://cn.vuejs.org/images/logo.svg");
background-repeat: no-repeat;
background-size: contain;
background-position: center;
margin: 20px auto;
}再次尝试,就成功了。
优化
基本功能实现后,可以对它进行优化。
只记录还没有加载的图片
首先考虑一点,每次滚动事件都会调用load函数。但是,如果现在所有图片都已经加载完毕了,就没有必要调用它了。所以,需要记录目前还没有加载的图片,下次调用只需要遍历它们即可。为此,我把获取到的子元素列表存在load函数外面,然后在每次load函数调用的时候,移除掉加载完成的图片。要实现移除,我使用Array.from将子元素伪数组转化为数组,这样就可以调用splice方法了。
改进后的代码如下:
// 获取父元素和所有子元素
let parent = document.getElementsByClassName('container')[0];
// 转换为数组
let img_list = Array.from(document.getElementsByClassName('lazy'));
// 滚动事件的回调函数
function load() {
if (!img_list.length) {
return;
}
for (let i = 0; i < img_list.length; i++) {
let img = img_list[i];
if (ifInview(parent, img)) {
img.src = img.dataset.src;
img_list.splice(i, 1)
}
}
}这样,每次加载一张图片,就会把它从子元素列表中移除。当img_list的长度为0时,会直接返回。
节流
现在还有一个问题,就是每次滚动,均会触发load函数,过于频繁。因此,可以采用防抖或节流的方式,减少调用的次数。这里我采用了节流。具体思路就是,封装一个实现节流函数的方法,对load进行节流包装,然后把scroll事件的回调函数设置为这个节流函数即可。
实现如下:
// 节流函数
function throttle(func, delay) {
let last = Date.now();
return function () {
let now = Date.now();
if (now - last < delay) {
return;
}
last = now;
func.apply(window);
}
}
// 获取节流后的回调函数
let throttle_load = throttle(load, 500);
let container = document.getElementsByClassName('container')[0]
// 将节流回调设置为scroll事件的回调函数
container.addEventListener('scroll', throttle_load);设置每500毫秒触发一次,实现节流效果。
实现效果
这里我使用了20张图片,效果更明显些。
懒加载演示
总结
在完成懒加载时,需要注意这么几点:
1、图片的父元素高度不能设置为100%
2、所有子元素的高度一定要超过父元素,触发溢出
3、图片是否在可视区域内的判断要准确
4、节流等优化手段
代码
附上简陋的代码。因为是实习闲暇写的,所以比较简单,没有什么复杂的逻辑,望批评指正。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset='UTF-8'>
<meta http-equiv=" X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
.container {
background-color: floralwhite;
width: 60vw;
height: 100vh;
overflow-y: scroll;
margin: 0 auto;
}
.lazy {
width: 400px;
height: 400px;
display: block;
background-image: url("https://cn.vuejs.org/images/logo.svg");
background-repeat: no-repeat;
background-size: contain;
background-position: center;
margin: 20px auto;
}
</style>
</head>
<body>
<div class="container">
<img data-src="https://acg.toubiec.cn/random.php?1" class="lazy">
<img data-src="https://acg.toubiec.cn/random.php?2" class="lazy">
<img data-src="https://acg.toubiec.cn/random.php?3" class="lazy">
<img data-src="https://acg.toubiec.cn/random.php?4" class="lazy">
<img data-src="https://acg.toubiec.cn/random.php?5" class="lazy">
<img data-src="https://acg.toubiec.cn/random.php?6" class="lazy">
<img data-src="https://acg.toubiec.cn/random.php?7" class="lazy">
<img data-src="https://acg.toubiec.cn/random.php?8" class="lazy">
<img data-src="https://acg.toubiec.cn/random.php?9" class="lazy">
<img data-src="https://acg.toubiec.cn/random.php?10" class="lazy">
<img data-src="https://acg.toubiec.cn/random.php?11" class="lazy">
<img data-src="https://acg.toubiec.cn/random.php?12" class="lazy">
<img data-src="https://acg.toubiec.cn/random.php?13" class="lazy">
<img data-src="https://acg.toubiec.cn/random.php?14" class="lazy">
<img data-src="https://acg.toubiec.cn/random.php?15" class="lazy">
<img data-src="https://acg.toubiec.cn/random.php?16" class="lazy">
<img data-src="https://acg.toubiec.cn/random.php?17" class="lazy">
<img data-src="https://acg.toubiec.cn/random.php?18" class="lazy">
<img data-src="https://acg.toubiec.cn/random.php?19" class="lazy">
<img data-src="https://acg.toubiec.cn/random.php?20" class="lazy">
</div>
<script>
// 图片地址数组
let arr = [
"https://acg.toubiec.cn/random.php?1",
"https://acg.toubiec.cn/random.php?2",
"https://acg.toubiec.cn/random.php?3",
"https://acg.toubiec.cn/random.php?4",
"https://acg.toubiec.cn/random.php?5",
"https://acg.toubiec.cn/random.php?6",
"https://acg.toubiec.cn/random.php?7",
"https://acg.toubiec.cn/random.php?8",
"https://acg.toubiec.cn/random.php?9",
"https://acg.toubiec.cn/random.php?10",
"https://acg.toubiec.cn/random.php?11",
"https://acg.toubiec.cn/random.php?12",
"https://acg.toubiec.cn/random.php?13",
"https://acg.toubiec.cn/random.php?14",
"https://acg.toubiec.cn/random.php?15",
"https://acg.toubiec.cn/random.php?16",
"https://acg.toubiec.cn/random.php?17",
"https://acg.toubiec.cn/random.php?18",
"https://acg.toubiec.cn/random.php?19",
"https://acg.toubiec.cn/random.php?20",
];
// 默认占位图片地址
let default_src = "https://cn.vuejs.org/images/logo.svg"
// 判断图片是不是在可视区域内
// parent:父元素 img:图片元素
function ifInview(parent, img) {
return img.offsetHeight + img.offsetTop > parent.scrollTop && img.offsetTop < parent.scrollTop + parent.clientHeight
}
// 获取父元素和所有子元素
let parent = document.getElementsByClassName('container')[0];
// 转换为数组
let img_list = Array.from(document.getElementsByClassName('lazy'));
// 滚动事件的回调函数
function load() {
if (!img_list.length) {
return;
}
console.log('load')
for (let i = 0; i < img_list.length; i++) {
let img = img_list[i];
if (ifInview(parent, img)) {
img.src = img.dataset.src;
img_list.splice(i, 1)
}
}
}
// 节流函数
function throttle(func, delay) {
let last = Date.now();
return function () {
let now = Date.now();
if (now - last < delay) {
return;
}
last = now;
func.apply(window);
}
}
// 获取节流后的回调函数
let throttle_load = throttle(load, 500);
let container = document.getElementsByClassName('container')[0]
// 将节流回调设置为scroll事件的回调函数
container.addEventListener('scroll', throttle_load);
</script>
</body>
</html>边栏推荐
- Ssh server rejected password
- Share a department design method that avoids recursion
- pl/sql 创建并执行oralce存储过程,并返回结果集
- 6线SPI传输模式探索
- Cmake specify opencv version
- Unable to start web server when Nacos starts
- EDA chip design solution based on AMD epyc server
- 万能通用智能JS表单验证
- Implement a simple restful API server
- MySQL 45 talks about | 06 global locks and table locks: Why are there so many obstacles to adding a field to a table?
猜你喜欢

【MySQL必知必会】触发器 | 权限管理
[Android] recyclerview caching mechanism, is it really difficult to understand? What level of cache is it?

LeetCode-198-打家劫舍

String type time comparison method with error string.compareto

Deployment and simple use of PostgreSQL learning

Implement a simple restful API server

43 box model

SSH服务器拒绝了密码

Award winning interaction | 7.19 database upgrade plan practical Summit: industry leaders gather, why do they come?
![[MySQL must know and know] trigger | permission management](/img/59/cb805d972097a6a8ed7f3ae454a91d.png)
[MySQL must know and know] trigger | permission management
随机推荐
IP地址分类,判断一个网段是子网超网
Gonzalez Digital Image Processing Chapter 1 Introduction
Add the jar package under lib directory to the project in idea
Leo-sam: tightly coupled laser inertial odometer with smoothing and mapping
45padding won't open the box
32 chrome调试工具的使用
Client error: invalid param endpoint is blank
When using jetty to run items, an error is reported: form too large or form too many keys
LeetCode-198-打家劫舍
转载----如何阅读代码?
awk从入门到入土(23)awk内置变量ARGC、ARGC--命令行参数传递
L1和L2正则化
[C题目]牛客 链表中倒数第k个结点
Reprint ---- how to read the code?
[C题目]力扣206. 反转链表
Realsense ROS installation configuration introduction and problem solving
pkg_resources动态加载插件
pl/sql 创建并执行oralce存储过程,并返回结果集
How many ways can you assign initial values to a two-dimensional array?
006 operator introduction