背景
类似于大型的淘宝商城、京东等网页,设计大量的商品图片信息,如果我们使页面包含的所有图片一次性加载完成,那用户体验很差。
目前流行的做法是滚动动态加载,也就是懒加载,显示在屏幕之外的图片默认不加载,随着页面的滚动,图片进入了显示的范围,则触发图片的加载显示
这样做的好处,一是页面加载速度快(浏览器进度条和加载转圈很快就结束了,这样用户的体验也比较好),而是节省流量,因为不可能每一个用户会把页面从上到下滚动完
原理
存储图片的真实路径,把图片的真实路径绑定给一个以data开头的自定义属性data-url即可,页面中的img元素,如果没有src属性,浏览器就不会发出请求去下载图片(没有请求就提高了性能)
<div class="scrollLoading" data-url="loaded.html">加载中...</div>
初始化img的时候,src不能是真实的图片地址(会一次性发送请求),也不可以是空地址或者坏地址(会出现出错图标,如下所示:)
设置img的默认src为一张1px*1px,很小很小的gif透明图片(所有的img都用这一张,只会发送一次请求),之所以需要是透明的,是需要透出通过background设置的背景图(一张loading.png,就是一个转圈圈的背景效果图)
<img data-url="xxx" src="1px.gif" width="180" height="180" style="background:url(loading.gif) no-repeat center;" />
需要一个滚动事件,判断元素是否在浏览器窗口,一旦进入视口才进行加载,当滚动加载的时候,就把这张透明的1px.gif图片替换为真正的url地址(也就是data-url里保存的值)
等到图片进入视口后,利用js提取data-url的真实图片地址赋值给src属性,就会去发送请求加载图片,真正实现了按需加载
方法一:滚动监听+scrollTop+offsetTop+innerHeight
浏览器可视窗口的大小
window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
获取内容滚动的距离
window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop
获取dom元素顶部距离窗口顶部的距离
dom元素.offsetTop
若内容上方偏移量(scrollTop)+视口高度(innerHeight)>图片距离内容顶部的偏移量(offsetTop),则说明图片在视口内,否则说明图片在视口外。
demo
<!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>
img {
background: url('./img/loading.gif') no-repeat center;
width: 250px;
height: 250px;
display: block;
}
</style>
</head>
<body>
<img src="./img/pixel.gif" data-url="./1.jpg">
<img src="./img/pixel.gif" data-url="./1.jpg">
<img src="./img/pixel.gif" data-url="./1.jpg">
<img src="./img/pixel.gif" data-url="./1.jpg">
<img src="./img/pixel.gif" data-url="./1.jpg">
<img src="./img/pixel.gif" data-url="./1.jpg"> <img src="./img/pixel.gif" data-url="./1.jpg">
<img src="./img/pixel.gif" data-url="./1.jpg"> <img src="./img/pixel.gif" data-url="./1.jpg">
<img src="./img/pixel.gif" data-url="./1.jpg"> <img src="./img/pixel.gif" data-url="./1.jpg">
<img src="./img/pixel.gif" data-url="./1.jpg"> <img src="./img/pixel.gif" data-url="./1.jpg">
<img src="./img/pixel.gif" data-url="./1.jpg"> <img src="./img/pixel.gif" data-url="./1.jpg">
<img src="./img/pixel.gif" data-url="./1.jpg"> <img src="./img/pixel.gif" data-url="./1.jpg">
<img src="./img/pixel.gif" data-url="./1.jpg"> <img src="./img/pixel.gif" data-url="./1.jpg">
<img src="./img/pixel.gif" data-url="./1.jpg">
<script>
let imgs = document.getElementsByTagName('img')
// 1. 一上来立即执行一次
fn()
// 2. 监听滚动事件
window.onscroll = lazyload(fn, true)
function fn() {
// 获取视口高度和内容的偏移量
let clietH = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
var scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
for (let i = 0; i < imgs.length; i++) {
let x = scrollTop + clietH - imgs[i].offsetTop //当内容的偏移量+视口高度>图片距离内容顶部的偏移量时,说明图片在视口内
if (x > 0) {
imgs[i].src = imgs[i].getAttribute('data-url'); //从dataurl中取出真实的图片地址赋值给url
}
}
}
// 函数节流
function lazyload(fn, immediate) {
let timer = null
return function () {
let context = this;
if (!timer) {
timer = setTimeout(() => {
fn.apply(this)
timer = null
}, 200)
}
}
}
</script>
</body>
</html>
滚动监听+getBoundingClientRect()
getBoundingClientRect()获取元素位置,这个方法没有参数
getBoundingClientRect()用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置。
getBoundingClientRect()是DOM元素到浏览器可视范围的距离(不包含文档卷起的部分)。
该函数返回一个Object对象,该对象有6个属性:top,lef,right,bottom,width,height;
<div id="box"></div>
var object=document.getElementById('box');
rectObject = object.getBoundingClientRect();
rectObject.top:元素上边到视窗上边的距离;
rectObject.right:元素右边到视窗左边的距离;
rectObject.bottom:元素下边到视窗上边的距离;
rectObject.left:元素左边到视窗左边的距离;
rectObject.width:是元素自身的宽
rectObject.height是元素自身的高
如果rectObject.top<=视口高度,就该加载了
demo
<!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;
}
img {
background: url('./img/loading.gif') no-repeat center;
width: 250px;
height: 250px;
display: block;
}
</style>
</head>
<body>
<img src="./img/pixel.gif" data-url="./1.jpg">
<img src="./img/pixel.gif" data-url="./1.jpg">
<img src="./img/pixel.gif" data-url="./1.jpg">
<img src="./img/pixel.gif" data-url="./1.jpg">
<img src="./img/pixel.gif" data-url="./1.jpg">
<img src="./img/pixel.gif" data-url="./1.jpg">
<img src="./img/pixel.gif" data-url="./1.jpg">
<script>
let imgs = document.getElementsByTagName('img')
// 1. 一上来立即执行一次,因为图片已经在视口内,你不滚动还不加载它了?
fn()
// 2. 监听滚动事件
window.onscroll = lazyload(fn, true)
function fn() {
// 获取视口高度和内容的偏移量
let offsetHeight = window.innerHeight || document.documentElement.clientHeight
Array.from(imgs).forEach((item, index) => {
let oBounding = item.getBoundingClientRect() //返回一个矩形对象,包含上下左右的偏移值
console.log(index, oBounding.top, offsetHeight);
//如果图片在视口上面,那么top就为负值,如果想图片翻上去不加载,判断条件可以写oBounding.top >= 0 && oBounding.top <= offsetHeight
if (oBounding.top <= offsetHeight) {
item.setAttribute('src', item.getAttribute('data-url'))
}
})
}
// 函数节流
function lazyload(fn, immediate) {
let timer = null
return function () {
let context = this;
if (!timer) {
timer = setTimeout(() => {
fn.apply(this)
timer = null
}, 200)
}
}
}
</script>
</body>
</html>