之前项目都是使用现成的lazyLoad之类的懒加载插件,趁项目没那么赶的时候自己试着实现一下。
思路
<img>
标签属性src的值不为空的时,浏览器就会根据这个值发送请求。
所以我们可以先给所有的图片设置一个loading的图片,当图片出现在浏览器的可视区域内时,才设置图片真正的路径,让图片显示出来。
实现
HTML
<div class="container">
<div class="img-area">
<img class="my-photo" src="img/loading.png" alt="1" data-src="img/img1.png">
</div>
<div class="img-area">
<img class="my-photo" src="img/loading.png" alt="2" data-src="img/img2.png">
</div>
<div class="img-area">
<img class="my-photo" src="img/loading.png" alt="3" data-src="img/img3.png">
</div>
<div class="img-area">
<img class="my-photo" src="img/loading.png" alt="4" data-src="img/img4.png">
</div>
<div class="img-area">
<img class="my-photo" src="img/loading.png" alt="5" data-src="img/img5.png">
</div>
<div class="img-area">
<img class="my-photo" src="img/loading.png" alt="6" data-src="img/img6.png">
</div>
<div class="img-area">
<img class="my-photo" src="img/loading.png" alt="7" data-src="img/img7.png">
</div>
</div>
注意:alt是必须的属性,它规定在图像无法显示时的替代文本。
data-* 全局属性:构成一类名称为自定义数据属性的属性,可以通过HTMLElement.dataset来访问。
判断元素在可视区内
function isInArea(el) {
const bound = el.getBoundingClientRect();
const clientHeight = window.innerHeight;
return bound.top <= clientHeight;
}
加载图片
页面打开时需要对所有图片进行判断,在可视区内就加载。
function checkImgs() {
const imgs = document.querySelectorAll('.my-photo');
Array.from(imgs).forEach(el => {
if (isInArea(el)) {
loadImg(el);
}
})
}
function loadImg(el) {
if (el.src==="img/loading.png") {
const source = el.dataset.src;
el.src = source;
}
}
这里可以做一个优化的地方,设一个标识符标识已经加载图片的index,当滚动条滚动时就不需要遍历所有的图片,只需要遍历未加载的图片即可。
函数节流
因为onScroll这种频繁的DOM操作,也会影响页面性能,所以我们可以做个节流优化。
function throttle(fn, mustRun = 300) {
const timer = null;
let previous = null;
return function() {
const now = new Date();
const context = this;
const args = arguments;
if (!previous){
previous = 0;
}
const remaining = now - previous;
if (mustRun && remaining >= mustRun) {
fn.apply(context, args);
previous = now;
}
}
}
完成
这样我们就实现了简单的图片懒加载~