图片懒加载与预加载
图片懒加载
在网页中,滑动页面,直到能看到图片时,再加载图片。
前置准备
offsetTop:返回元素相对带有定位父元素上方的偏移;
clientHeight:元素内部的高度(以像素为单位),包含内边距,但不包括边框、外边距和水平滚动条(如果存在)。在根元素(<html> 元素)或怪异模式下的 <body> 元素上使用 clientHeight 时,该属性将返回视口高度(不包含任何滚动条);
scrollTop:属性可以获取或设置一个元素的内容垂直滚动的像素数。
图示:
实现懒加载
假设上图中某元素就是还未显示到的图片,当移动鼠标滚轮时,导致某元素上移,某元素即出现在了浏览器可视区域。此时有,某元素.offsetTop < clientHeight + scrollTop。这个条件即为判定图片是否出现在了浏览器可视区域。
function lazyload() {
let imgs = document.querySelectorAll('img');
for (let i = 0; i < imgs.length; i++) {
if (imgs[i].offsetTop < document.documentElement.clientHeight + document.documentElement.scrollTop) {
imgs[i].src = imgs[i].getAttribute('data-src');
}
}
}
//第一次进入页面,屏幕内的图片正常加载
window.onload = lazyload;
//监听滚动事件
window.addEventListener('scroll', lazyload);
此外,<img>会通过src属性的url来加载图片,因此将需要懒加载的图片url放到data-src属性中,这样src为空就不会加载图片了,或者也可以在src属性存放loading图片。当图片到达可视区时,再给<img>添加src属性。
优化
由于监听鼠标滚轮的滚动会非常频繁,因此要进行节流处理。
附完整代码:
<!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>图片懒加载</title>
<style>
body {
background-color: rgb(241, 222, 167);
}
div img {
width: 220px;
height: 375px;
display: block;
margin: 20px auto;
border: 2px solid rgb(229, 174, 22);
background-color: rgb(243, 243, 243);
}
</style>
</head>
<body>
<div>
<img data-src="https://www.z4a.net/images/2023/02/19/QQ20210227144725.md.jpg">
<img data-src="https://www.z4a.net/images/2023/02/19/QQ20210227012315.md.jpg">
<img data-src="https://www.z4a.net/images/2023/02/19/QQ20210227031103.md.jpg">
<img data-src="https://www.z4a.net/images/2023/02/19/QQ20210227012319.md.jpg">
<img data-src="https://www.z4a.net/images/2023/02/19/QQ20210227012327.md.jpg">
<img data-src="https://www.z4a.net/images/2023/02/19/QQ20210227031058.md.jpg">
<img data-src="https://www.z4a.net/images/2023/02/19/QQ20210227012249.md.jpg">
<img data-src="https://www.z4a.net/images/2023/02/19/QQ20210227012334.md.jpg">
</div>
</body>
<script>
function lazyload() {
let imgs = document.querySelectorAll('img');
for (let i = 0; i < imgs.length; i++) {
if (imgs[i].offsetTop < document.documentElement.clientHeight + document.documentElement.scrollTop) {
imgs[i].src = imgs[i].getAttribute('data-src');
}
}
}
//第一次进入页面,屏幕内的图片正常加载
window.onload = lazyload;
//节流函数
function throttle(fn, delay) {
let preTime = Date.now();
return function () {
let context = this,
args = arguments,
nowTime = Date.now();
// 如果两次时间间隔超过了指定时间,则执行函数。
if (nowTime - preTime >= delay) {
preTime = Date.now();
return fn.apply(context, args);
}
};
}
//监听滚动事件
window.addEventListener('scroll', throttle(lazyload, 500));
</script>
</html>
其它方法:
1 getBoundingClientRect + clientHeight
getBoundingClientRect:会返回一个对象,包含当前元素的大小及其相对于视口的位置。
如图,如果某元素.getBoundingClientRect().top < clientHeight就会出现在浏览器可视区域。
function lazyload() {
let imgs = document.querySelectorAll('img');
for (let i = 0; i < imgs.length; i++) {
if (imgs[i].getBoundingClientRect().top < document.documentElement.clientHeight) {
imgs[i].src = imgs[i].getAttribute('data-src');
}
}
}
//第一次进入页面,屏幕内的图片正常加载
window.onload = lazyload;
//监听滚动事件
window.addEventListener('scroll', lazyload);
2 Intersection Observer (推荐)
Intersection Observer(交叉观察器) :一种异步查询元素相对于其他元素或窗口位置的能力。常用于追踪一个元素在窗口的可视问题。
可见本质:目标元素与视口产生的交叉区,所以叫交叉观察器。
使用:
const i = new IntersectionObserver(callback, options);
//callback是可见性变化时的回调函数,options是配置对象(该参数可选)
//callback也有参数,entries是一个数组,每个元素都是IntersectionObserver的实例。描述了目标元素与root的交叉状态。如果有三个被观察的对象的可见性发生变化,entries数组就会有三个元素。
const i2 = new IntersectionObserver(
entries => {
console.log(entries);
},options);
entries数组中的对象有如下属性、方法
属性 | 功能 |
boundingClientRect | 返回包含目标元素的边界信息,返回结果与element.getBoundingClientRect() 相同 |
intersectionRatio | 返回目标元素出现在可视区的比例 |
intersectionRect | 用来描述root和目标元素的相交区域 |
isIntersecting | 返回一个布尔值,下列两种操作均会触发callback:1. 如果目标元素出现在root可视区,返回true。2. 如果从root可视区消失,返回false |
rootBounds | 用来描述交叉区域观察者(intersection observer)中的根. |
target | 目标元素:与根出现相交区域改变的元素 (Element) |
time | 返回一个记录从 IntersectionObserver 的时间原点到交叉被触发的时间的时间戳 |
function Observer() {
let imgs = document.querySelectorAll('img');
let observer = new IntersectionObserver(entries => {
entries.forEach(entry => {
//判断目标元素是否出现在root可视区
if (entry.isIntersecting) {
entry.target.src = entry.target.dataset.src;
//当图片出现在可视区,就停止监听
observer.unobserve(entry.target);
}
})
});
//监听图片元素
imgs.forEach(img => observer.observe(img));
}
Observer();
相较于offsetTop getBoundingClientRect两种方法,使用交叉观察器性能更优。
图片预加载
提前加载好后面会用到的图片
通过css实现
利用 background-image 属性,当打开非正式页面时,可以提前利用css请求所需要的图片资源,然后浏览器会将返回的图片资源进行本地存储。当需要在正式页面引入预加载图片时,可直接从本地中拿去,减少了正式页面等待请求时间。
缺点:会导致非正式页面css加载时间过长,可能导致非正式页面短暂空白。
css
#preload1 {
background-image: url(https://www.z4a.net/images/2023/02/19/QQ20210227144725.md.jpg);
width: 0;
height: 0;
}
#preload2 {
background-image: url(https://www.z4a.net/images/2023/02/19/QQ20210227012315.md.jpg);
width: 0;
height: 0;
}
#preload3 {
background-image: url(https://www.z4a.net/images/2023/02/19/QQ20210227031103.md.jpg);
width: 0;
height: 0;
}
html
<img id="preload1">
<img id="preload2">
<img id="preload3">
通过js实现
使用 js 中的 image 对象,通过为 image 对象来设置 src 属性,来实现图片的预加载。
const imgs = [];
function preload() {
for (let i = 0; i < arguments.length; i++) {
imgs[i] = new Image();
imgs[i].src = arguments[i];
}
}
preload(
'https://www.z4a.net/images/2023/02/19/QQ20210227144725.md.jpg',
'https://www.z4a.net/images/2023/02/19/QQ20210227012315.md.jpg',
'https://www.z4a.net/images/2023/02/19/QQ20210227031103.md.jpg'
);
懒加载与预加载区别
懒加载也叫延迟加载,指的是在长网页中延迟加载图片的时机,当用户需要访问时,再去加载,这样可以提高网站的首屏加载速度,提升用户的体验,并且可以减少服务器的压力。它适用于图片很多,页面很长的电商网站的场景。
预加载指的是将所需的资源提前请求加载到本地,这样后面在需要用到时就直接从缓存取资源。通过预加载能够减少用户的等待时间,提高用户的体验。常见于漫画、图片画廊场景。
这两种方式都是提高网页性能的方式,两者主要区别是一个是提前加载,一个是迟缓甚至不加载。懒加载对服务器前端有一定的缓解压力作用,预加载则会增加服务器前端压力。