搭建图片懒加载场景
可以设置这样一个html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Lazy-Load</title>
<style>
.container {
display: flex;
flex-wrap: wrap;
}
.img {
width: 400px;
height: 400px;
margin: 10px;
background: gray;
}
</style>
</head>
<body>
<div class="container">
<div class="img">
<img alt="加载中1" class="pic" data-src="./images/image.avif" />
</div>
<div class="img">
<img alt="加载中2" class="pic" data-src="./images/image.avif" />
</div>
<div class="img">
<img alt="加载中3" class="pic" data-src="./images/image.avif" />
</div>
<div class="img">
<img alt="加载中4" class="pic" data-src="./images/image.avif" />
</div>
<div class="img">
<img alt="加载中5" class="pic" data-src="./images/image.avif" />
</div>
<div class="img">
<img alt="加载中6" class="pic" data-src="./images/image.avif" />
</div>
<div class="img">
<img alt="加载中7" class="pic" data-src="./images/image.avif" />
</div>
<div class="img">
<img alt="加载中8" class="pic" data-src="./images/image.avif" />
</div>
<div class="img">
<img alt="加载中9" class="pic" data-src="./images/image.avif" />
</div>
<div class="img">
<img alt="加载中10" class="pic" data-src="./images/image.avif" />
</div>
<div class="img">
<img alt="加载中11" class="pic" data-src="./images/image.avif" />
</div>
<div class="img">
<img alt="加载中12" class="pic" data-src="./images/image.avif" />
</div>
<div class="img">
<img alt="加载中13" class="pic" data-src="./images/image.avif" />
</div>
<div class="img">
<img alt="加载中14" class="pic" data-src="./images/image.avif" />
</div>
<div class="img">
<img alt="加载中15" class="pic" data-src="./images/image.avif" />
</div>
<div class="img">
<img alt="加载中16" class="pic" data-src="./images/image.avif" />
</div>
<div class="img">
<img alt="加载中17" class="pic" data-src="./images/image.avif" />
</div>
<div class="img">
<img alt="加载中18" class="pic" data-src="./images/image.avif" />
</div>
<div class="img">
<img alt="加载中19" class="pic" data-src="./images/image.avif" />
</div>
<div class="img">
<img alt="加载中20" class="pic" data-src="./images/image.avif" />
</div>
</div>
</body>
</html>
懒加载计算滚动到可视窗口
当前可视区域的高度, 在和现代浏览器及 IE9 以上的浏览器中,可以用 window.innerHeight
属性获取。在低版本 IE 的标准模式中,可以用 document.documentElement.clientHeight
获取,这里我们兼容两种情况:
const viewHeight = window.innerHeight || document.documentElement.clientHeight
而元素距离可视区域顶部的高度,我们这里选用 getBoundingClientRect()
方法来获取返回元素的大小及其相对于视口的位置。对此 MDN 给出了非常清晰的解释:
lazyload方法
<script>
// 获取所有的图片标签
const imgs = document.getElementsByTagName('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>
加载效果图
节流throttle
优化懒加载
throttle
的中心思想在于:在某段时间内,不管你触发了多少次回调,我都只认第一次,并在计时结束时给予响应。
function throttle(fn, delay) {
let last = 0;
return function () {
let args = arguments;
let now = +new Date();
if (now - last >= delay) {
fn.apply(this, args);
last = now;
}
};
}
function throttle2(fn, delay) {
let timer = null;
return function () {
let context = this;//记住this
let args = arguments;//参数
if (!timer) {
timer = setTimeout(() => {
fn.apply(context, args);//执行fn
timer = null;
}, delay);
}
};
}
const throttleScroll = throttle(lazyLoad, 3000);
window.addEventListener("scroll", throttleScroll);
节流效果
实现防抖debounce
function debounce(fn, delay) {
let timer = null;
return function () {
let context = this;
let args = arguments;
clearTimeout(timer); //每次都清空定时器
timer = setTimeout(() => {
//定时器执行fn
fn.apply(context, args);
}, delay);
};
}
鼠标滚动添加防抖效果
const debounceScroll = debounce(lazyLoad, 1000);
window.addEventListener("scroll", debounceScroll);
防抖效果
为了避免弄巧成拙,我们需要借力 throttle
的思想,打造一个“有底线”的 debounce
——等你可以,但我有我的原则:delay
时间内,我可以为你重新生成定时器;但只要delay
的时间到了,我必须要给用户一个响应。这个 throttle
与 debounce
“合体”思路,已经被很多成熟的前端库应用到了它们的加强版 throttle
函数的实现中
有底线的防抖——防抖和节流结合体
function debounce2(fn, delay) {
let last = 0;
let timer = null;
return function (...args) {
const context = this;
const now = +new Date();
if (now - last < delay) {
//防抖
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(context, args);
last = now;
}, delay);
} else {
fn.apply(context, args);
last = now;
}
};
}
效果如下,即使鼠标滚动没有停止,到了指定时间一定会执行