试一下自己撸一个图片懒加载…
Demo 地址: http://axuebin.com/lazyload
照片都是自己拍的哦~
懒加载其实就是延迟加载,是一种对网页性能优化的方式,比如当访问一个页面的时候,优先显示可视区域的图片而不一次性加载所有图片,当需要显示的时候再发送图片请求,避免打开网页时加载过多资源。
当页面中需要一次性载入很多图片的时候,往往都是需要用懒加载的。
我们都知道 html 中的
标签是代表文档中的一个图像。。说了个废话。。
- <img>
标签有一个属性是
- <img>
,用来表示图像的 URL,当这个属性的值不为空时,浏览器就会根据这个值发送请求。如果没有
- src
属性,就不会发送请求。
- src
嗯?貌似这点可以利用一下?
我先不设置
,需要的时候再设置?
- src
nice,就是这样。
我们先不给
设置
- <img>
,把图片真正的 URL 放在另一个属性
- src
中,在需要的时候也就是图片进入可视区域的之前,将 URL 取出放到
- data-src
中。
- src
- <div class="container">
- <div class="img-area">
- <img class="my-photo" alt="loading" data-src="./img/img1.png">
- </div>
- <div class="img-area">
- <img class="my-photo" alt="loading" data-src="./img/img2.png">
- </div>
- <div class="img-area">
- <img class="my-photo" alt="loading" data-src="./img/img3.png">
- </div>
- <div class="img-area">
- <img class="my-photo" alt="loading" data-src="./img/img4.png">
- </div>
- <div class="img-area">
- <img class="my-photo" alt="loading" data-src="./img/img5.png">
- </div>
- </div>
仔细观察一下,
标签此时是没有
- <img>
属性的,只有
- src
和
- alt
属性。
- data-src
alt 属性是一个必需的属性,它规定在图像无法显示时的替代文本。 data-* 全局属性:构成一类名称为自定义数据属性的属性,可以通过
来访问。
- HTMLElement.dataset
网上看到好多这种方法,稍微记录一下。
获取屏幕可视窗口高度
- document.documentElement.clientHeight
获取浏览器窗口顶部与文档顶部之间的距离,也就是滚动条滚动的距离
- document.documentElement.scrollTop
获取元素相对于文档顶部的距离
- element.offsetTop
然后判断②-③<①是否成立,如果成立,元素就在可视区域内。
通过
方法来获取元素的大小以及位置,MDN 上是这样描述的:
- getBoundingClientRect()
The Element.getBoundingClientRect() method returns the size of an element and its position relative to the viewport.
这个方法返回一个名为
的
- ClientRect
对象,包含了
- DOMRect
、
- top
、
- right
、
- botton
、
- left
、
- width
这些值。
- height
MDN 上有这样一张图:
可以看出返回的元素位置是相对于左上角而言的,而不是边距。
我们思考一下,什么情况下图片进入可视区域。
假设
来表示图片到可视区域顶部距离; 并设
- const bound = el.getBoundingClientRect();
来表示可视区域的高度。
- const clientHeight = window.innerHeight;
随着滚动条的向下滚动,
会越来越小,也就是图片到可视区域顶部的距离越来越小,当
- bound.top
时,图片的上沿应该是位于可视区域下沿的位置的临界点,再滚动一点点,图片就会进入可视区域。
- bound.top===clientHeight
也就是说,在
时,图片是在可视区域内的。
- bound.top<=clientHeight
我们这样判断:
- function isInSight(el) {
- const bound = el.getBoundingClientRect();
- const clientHeight = window.innerHeight;
- //如果只考虑向下滚动加载
- //const clientWidth = window.innerWeight;
- return bound.top <= clientHeight + 100;
- }
这里有个 + 100 是为了提前加载。
页面打开时需要对所有图片进行检查,是否在可视区域内,如果是就加载。
- function checkImgs() {
- const imgs = document.querySelectorAll('.my-photo');
- Array.from(imgs).forEach(el = >{
- if (isInSight(el)) {
- loadImg(el);
- }
- })
- }
- function loadImg(el) {
- if (!el.src) {
- const source = el.dataset.src;
- el.src = source;
- }
- }
这里应该是有一个优化的地方,设一个标识符标识已经加载图片的 index,当滚动条滚动时就不需要遍历所有的图片,只需要遍历未加载的图片即可。
在类似于滚动条滚动等频繁的 DOM 操作时,总会提到 "函数节流、函数去抖"。
所谓的函数节流,也就是让一个函数不要执行的太频繁,减少一些过快的调用来节流。
基本步骤:
- function throttle(fn, mustRun = 500) {
- const timer = null;
- let previous = null;
- return function() {
- const now = new Date();
- const context = this;
- const args = arguments;
- if (!previous) {
- previous = now;
- }
- const remaining = now - previous;
- if (mustRun && remaining >= mustRun) {
- fn.apply(context, args);
- previous = now;
- }
- }
- }
这里的
就是调用函数的时间间隔,无论多么频繁的调用
- mustRun
,只有
- fn
时
- remaining>=mustRun
才能被执行。
- fn
可以看出此时仅仅是加载了 img1 和 img2,其它的 img 都没发送请求,看看此时的浏览器
第一张图片是完整的呈现了,第二张图片刚进入可视区域,后面的就看不到了~
当我向下滚动,此时浏览器是这样
此时第二张图片完全显示了,而第三张图片显示了一点点,这时候我们看看请求情况
img3 的请求发出来,而后面的请求还是没发出~
当滚动条滚到最底下时,全部请求都应该是发出的,如图
在这哦: http://axuebin.com/lazyload
来源: http://www.tuicool.com/articles/QnYjI3i