1. 企业网站定制开发图片懒加载的背景
企业网站定制开发类似于大型的淘宝商城、企业网站定制开发京东等网页,企业网站定制开发设计大量的商品图片信息,企业网站定制开发如果我们使页面包含的企业网站定制开发所有图片一次性加载完成,企业网站定制开发那用户体验很差。
企业网站定制开发目前流行的做法是滚动动态加载,企业网站定制开发也就是懒加载,显示在屏幕之外的图片默认不加载,随着页面的滚动,图片进入了显示的范围,则触发图片的加载显示
这样做的好处,一是页面加载速度快(浏览器进度条和加载转圈很快就结束了,这样用户的体验也比较好),而是节省流量,因为不可能每一个用户会把页面从上到下滚动完
2. 图片懒加载的原理
-
存储图片的真实路径,把图片的真实路径绑定给一个以data开头的data-url即可,页面中的img元素,如果没有src属性,浏览器就不会发出请求去下载图片(没有请求就提高了性能)
<div class="scrollLoading" data-url="loaded.html">加载中...</div>
- 1
-
初始化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;" />
- 1
-
需要一个滚动事件,判断元素是否在浏览器窗口,一旦进入视口才进行加载,当滚动加载的时候,就把这张透明的1px.gif图片替换为真正的url地址(也就是data-url里保存的值)
-
等到图片进入视口后,利用js提取data-url的真实图片地址赋值给src属性,就会去发送请求加载图片,真正实现了按需加载
方法一:滚动监听+scrollTop+offsetTop+innerHeight
1. 获取可视窗口的大小
-
IE9和其他浏览器提供了innerWidth 和 innerHeight属性获取当前浏览器窗口的大小
-
IE8及更早版本以前没有提供取得浏览器窗口大小的属性,不过提供了dom页面可见区域的属性,即
document.documentElement.
document.documentElement.
元素的客户区大小(client dimension),指的是元素内容及其内边距所占据的空间大小。有关客户区 大小的属性有两个:clientWidth 和 clientHeight。其中,clientWidth 属性是元素内容区宽度加 上左右内边距宽度;clientHeight 属性是元素内容区高度加上上下内边距高度 -
IE6中,上述属性必须在标准模式才有效,如果是混杂模式,需要通过document.body.clientWidth 和 document.body. clientHeight 取得相同信息。
var pageWidth = window.innerWidth, pageHeight = window.innerHeight; if (typeof pageWidth != "number"){ //pageWidth的值不是数值,说明没有innerwidth属性 if (document.compatMode == "CSS1Compat"){ //标准模式 pageWidth = document.documentElement.clientWidth; pageHeight = document.documentElement.clientHeight; } else { //混在模式 pageWidth = document.body.clientWidth; pageHeight = document.body.clientHeight; } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
2. 获取内容滚动的距离
3. 如何判断图片进入了可视区域
- 上图蓝色部分表示视口大小,可以想象成手机屏幕,蓝色区域的高度和宽度也就是视口的高度和宽度,用innerHeight和innerWidth来表示
- 外面的大黑色框框表示内容的大小(手机屏幕那么小宽度不可能装下所有的网页内容,所以会有“溢出”,可以用scrollTop和scrollLeft表示上面和左边溢出的部分)
- 绿色的小方框表示图片,图片的顶部到内容的顶部称为图片的offsetTop
- 那么很明显,若内容上方偏移量(scrollTop)+视口高度(innerHeight)>图片距离内容顶部的偏移量(offsetTop),则说明图片在视口内,否则说明图片在视口外。
4. 代码实现
下面是我利用滚动事件+函数节流实现的简易版懒加载,可以下载我代码用到的图片测试一下,图片地址:
链接:https://pan.baidu.com/s/1LRQzz0hp0m8OF0w2nc6zdg
提取码:dara
<!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="./img/1.jpeg"> <img src="./img/pixel.gif" data-url="./img/2.jfif"> <img src="./img/pixel.gif" data-url="./img/3.jfif"> <img src="./img/pixel.gif" data-url="./img/4.jfif"> <img src="./img/pixel.gif" data-url="./img/5.jfif"> <img src="./img/pixel.gif" data-url="./img/6.webp"> <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; console.log(clietH, 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>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
方法二:滚动监听+getBoundingClientRect()
1. API简介
API具体介绍:
let oBounding=img.getBoundingClientRect()
- 1
oBounding此刻就等于img元素调用自己的getClientRects()方法返回的一个矩形对象,并且拥有left, top, right, bottom, x, y, width, 和 height这几个以像素为单位的只读属性用于描述整个边框。除了width 和 height 以外的属性是始终相对于视图窗口的左上角来计算的。具体见下图。
也就是说,当滚动位置发生了改变,top和left属性值就会随之立即发生变化(因此,它们的值是相对于视口的,而不是绝对的)。如果你需要获得相对于整个网页左上角定位的属性值,那么只要给top、left属性值加上当前的滚动位置(通过 window.scrollX 和 window.scrollY),这样就可以获取与当前的滚动位置无关的值。
上面的案例使用getBoudingRect的判断方法:
<!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="./img/1.jpeg"> <img src="./img/pixel.gif" data-url="./img/2.jfif"> <img src="./img/pixel.gif" data-url="./img/3.jfif"> <img src="./img/pixel.gif" data-url="./img/4.jfif"> <img src="./img/pixel.gif" data-url="./img/5.jfif"> <img src="./img/pixel.gif" data-url="./img/6.webp"> <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); if (0 <= oBounding.top && 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>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
方法三-intersectionObserve()
API的具体介绍:
刚刚的案例使用intersectionObserve()的代码如下:
<!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="./img/1.jpeg"> <img src="./img/pixel.gif" data-url="./img/2.jfif"> <img src="./img/pixel.gif" data-url="./img/3.jfif"> <img src="./img/pixel.gif" data-url="./img/4.jfif"> <img src="./img/pixel.gif" data-url="./img/5.jfif"> <img src="./img/pixel.gif" data-url="./img/6.webp"> <script> let imgs = document.getElementsByTagName('img') // 1. 一上来立即执行一次 let io = new IntersectionObserver(function (entires) { //图片进入视口时就执行回调 entires.forEach(item => { // 获取目标元素 let oImg = item.target // console.log(item); // 当图片进入视口的时候,就赋值图片的真实地址 if (item.intersectionRatio > 0 && item.intersectionRatio <= 1) { oImg.setAttribute('src', oImg.getAttribute('data-url')) } }) }) Array.from(imgs).forEach(element => { io.observe(element) //给每一个图片设置监听 }); </script></body></html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
参考文章:张鑫旭