计算出盒子底边距离body(a)的距离 和 浏览器底边距离bo._img.onloa">
当前位置:   article > 正文

图片延迟加载_img.onload

img.onload

前言

图片延迟加载:真实项目中非常重要的一个性能优化手段;

  • 在没有加载图片之前,图片区域用默认的背景图(背景色)占位即可
  • 首屏:先加载首屏的其他数据(内容,样式,文本等),等待其它数据加载完成,再去加载真实的图片
  • 其他屏:当我们滚动到指定区域(一露面,出来一半,完全出现在屏幕中),在去加载真实图片

   onscroll特点:

只要页面滚动就会触发 < 在浏览器最快反应时间内(谷歌4 - 6ms), 触发一次, 触发频率特别频繁 >, 重复加载a <= b的这个条件, 只要页面一直向上滚动, 在第一次符合条件后, 每次滚动条件也是符合的, 这样 lazyImage方法会被执行多次(也就是延迟加载会被处理多次)

处理频率太快,我们需要降低他的频率 函数的节流throttle

意义: 如果不做延迟加载,那么页面渲染的时候,同时也要把资源请求回来,并且进行渲染,这样会阻碍页面的渲染进度,导致首次加载页面变慢,延迟加载一方面,可以提高页面加载速度,另外一方面也可以减少用户没必要的网路消耗

单张图片延迟加载 方案一

需要延迟加载的图片放置在一个盒子中,

盒子有一个默认占位的背景图,在没有加载真实图片之前,我们给用户呈现的是一个默认的占位图

img一开始不能设置src,因为器一但设置src页面加载的时候,就会加载图片了,达不到延迟加载的效果

把真实加载的图片地址放到image的lazy-image自定义属性上(自定义属性名自己设定即可)

注意: 开始图片是隐藏的:在某些IE浏览器中,如果图片的SRC是空的,或者加载的图片是错误的,图片不隐藏,会显示一个 X 的效果,很难看,所以图片没有加载真实地址,并且加载正确之前,还是让图片隐藏吧!

  • 办法一:display: none;  ->这种办法在加载完成真实图片后,还需要让其display: block;这样触发了DOM的回流重汇,性能消耗比较大...
  •  办法二:opacity: 0; transition: opacity .3s;  ->推荐这种方案,一方面后期加载真实图片后,我们只需要设置opacity: 1;,这样一方面不会引发DOM的回流重绘,一方面还可以CSS3过度动画实现出渐现的效果...
  1. //节流函数
  2. function throttle(func, wait = 500) {
  3. let timer = null,
  4. previous = 0;
  5. return function anonymous(...params) {
  6. let now = new Date(),
  7. remaining = wait - (now - previous);
  8. if (remaining <= 0) {
  9. clearTimeout(timer);
  10. timer = null;
  11. previous = now;
  12. func.call(this, ...params);
  13. } else if (!timer) {
  14. timer = setTimeout(() => {
  15. clearTimeout(timer);
  16. timer = null;
  17. previous = new Date();
  18. func.call(this, ...params);
  19. }, remaining);
  20. }
  21. };
  22. }
  23. //盒子顶部距离body上偏移
  24. function offset(element) {
  25. let l = element.offsetLeft,
  26. t = element.offsetTop,
  27. p = element.offsetParent;
  28. while (p && p.tagName !== 'body') {
  29. if (!/MSIE 8/.test(navigator.userAgent)) {
  30. l += p.clientLeft;
  31. t += p.clientTop;
  32. }
  33. l += p.offsetLeft;
  34. t += p.offsetTop;
  35. p = p.offsetParent;
  36. }
  37. return {
  38. top: t,
  39. left: l
  40. };
  41. }
  42. //获取占位盒子 && html
  43. let imageLazyBox = document.querySelector('.imageLazyBox'),
  44. html = document.documentElement;
  45. //执行lazyImage方法,传递需要延迟加载的盒子,实现单张图片延迟加载
  46. function lazyImage(imageLazyBox) {
  47. // 获取图片
  48. let imageItem = imageLazyBox.querySelector('img'),
  49. // 获取图片的自定义属性
  50. lazy_image = imageItem.getAttribute('lazy-image');
  51. // if (lazy_image === null) return;//性能不好
  52. imageItem.onload = function () {
  53. //当图片加载成功后触发的触发 onload vs onerror
  54. imageItem.style.opacity = 1;
  55. };
  56. //图片的地址赋值给image的src
  57. imageItem.src = lazy_image;
  58. // 也有在不论图片是否加载成功,只要处理过,则就把lazy-image属性移除掉(证明其已经处理过了)
  59. imageItem.removeAttribute('lazy-image');
  60. //第一次执行这个方法,我们设置一个自定义属性isload = true 证明当前图片已经看过了
  61. imageLazyBox.isload = true;
  62. }
  63. //监听滚动条图片全部显示后加载图片
  64. window.onscroll = throttle(function () {
  65. console.log('ok');
  66. if (imageLazyBox.isload) return;
  67. // 盒子底部距离body的距离:盒子顶部的距离+盒子本身高度
  68. let a = offset(imageLazyBox).top + imageLazyBox.offsetHeight,
  69. // 浏览器底边距离body: 一屏的高度-滚动条卷去的高度
  70. b = html.clientHeight + html.scrollTop;
  71. if (a <= b) {
  72. lazyImage(imageLazyBox)
  73. }
  74. })
  75. //设定定时器,延迟多久之后进行加载
  76. // setTimeout(lazyImage, 1000);
  77. // 或者触发条件:页面中所有资源都加载完成
  78. // window.onload = function () { };

 思路:  //在浏览器滚动条滚动(页面滚动)过程中,等待图片完全出现在可视窗口内的时候,我们加载真实图片 "这是非首屏图片的处理步骤"-->计算出盒子底边距离body(a)的距离 和 浏览器底边距离body(b)的距离 做比较如果a<=b的时候加载真实图片即可

方案二

思路:基于getBoundingClientRect来判断图片的加载条件,不用自己写offset方法了

  • a:获取当前盒子距离浏览器可视窗口的位置信息
  • b可视窗口一屏的的大小
  • 做比较如果a<=b的时候加载真实图片即可
  1. function throttle(func, wait = 500) {
  2. let timer = null,
  3. previous = 0;
  4. return function anonymous(...params) {
  5. let now = new Date(),
  6. remaining = wait - (now - previous);
  7. if (remaining <= 0) {
  8. clearTimeout(timer);
  9. timer = null;
  10. previous = now;
  11. func.call(this, ...params);
  12. } else if (!timer) {
  13. timer = setTimeout(() => {
  14. clearTimeout(timer);
  15. timer = null;
  16. previous = new Date();
  17. func.call(this, ...params);
  18. }, remaining);
  19. }
  20. };
  21. }
  22. let imageLazyBox = document.querySelector('.imageLazyBox'),
  23. html = document.documentElement;
  24. function lazyImage(imageLazyBox) {
  25. let imageItem = imageLazyBox.querySelector('img'),
  26. lazy_image = imageItem.getAttribute('lazy-image');
  27. imageItem.onload = function () {
  28. imageItem.style.opacity = 1;
  29. };
  30. imageItem.src = lazy_image;
  31. imageItem.removeAttribute('lazy-image');
  32. imageLazyBox.isload = true;
  33. }
  34. window.onscroll = throttle(function () {
  35. //获取当前盒子距离浏览器可视窗口的位置信息
  36. let a = imageLazyBox.getBoundingClientRect().bottom,
  37. // 可视窗口一屏的的大小
  38. b = html.clientHeight;
  39. if (imageLazyBox.isload) return;
  40. if (a <= b) {
  41. lazyImage(imageLazyBox)
  42. }
  43. })

方案三

基于 IntersectionObserver实现图片延迟加载

设置一个监听器,后期可以监听指定的dom元素(可以多个),当监听的元素dom元素与可视窗口交叉位置发生改变,触发回调函数执行,-->changes是一个集合,集合中存储了一个dom元素与可视窗口交叉的状态信息

 Observer.observe(imageLazyBox)监听dom元素

Observer.unobserve(imageLazyBox):解除监听

默认:第一次监听dom触发一次页面刚开始出现在页面中触发一次,元素消失在页面中触发一次

         基于threshold配置项,控制触发监听条件,(0刚出现, 0.5出现一半, 1完全出现)

  •  IntersectionObserverEntry 每一个dom元素与可视窗口交叉的信息
  • boundingClientRect获取就是基于imageLazyBox.getBoundingClientRect()获取的结果
  • target:监听的dom元素
  • isIntersecting:是否与可视窗口交叉了(布尔),如果为true说明元素出现在可视窗口中,反之如果为false说明不在可视窗口中
  1. let imageLazyBox = document.querySelector('.imageLazyBox'),
  2. //配置项只有完全触发出现
  3. options = {
  4. threshold: [1]
  5. };
  6. function lazyImage(imageLazyBox) {
  7. let imageItem = imageLazyBox.querySelector('img'),
  8. lazy_image = imageItem.getAttribute('lazy-image');
  9. imageItem.onload = function () {
  10. imageItem.style.opacity = 1;
  11. };
  12. imageItem.src = lazy_image;
  13. imageItem.removeAttribute('lazy-image');
  14. imageLazyBox.isload = true;
  15. }
  16. let ob = new IntersectionObserver(change => {
  17. // item监听的dom元素与可视窗口的交叉信息
  18. // target存储当前监听的那个盒子
  19. let item = change[0],
  20. target = item.target;
  21. if (item.isIntersecting) {
  22. //符合条件:盒子已经完全出现在视口中
  23. lazyImage(target);
  24. // 处理过一次延迟加载,以后当前这个元素无需在次监听处理了
  25. ob.unobserve(imageLazyBox)
  26. }
  27. }, options)
  28. //监听dom元素
  29. ob.observe(imageLazyBox)

基于IntersectionObserver,实现延迟加载,相对于之前 window.onscroll实现延迟加载的优势

  • 优势:更方便 ,onscrol方法中需要自己编写throttle函数进行节流处理,以此优化性能;但是IntersectionObserver内置节流处理,而且性能上比onscroll也好很多!!
  •  弊端:兼容性不好,IE中只有EDGE新版本兼容,其余都不兼容,火狐谷歌等也得是 >=60 左右的新版本才兼容,所以移动端开发目前使用的比较多

Can I use... Support tables for HTML5, CSS3, etc测试方法兼容性的

方案四

 loading="lazy" 浏览器会自己计算该何时加载,帮助我们加载真实的图片

     <img src="images/1.jpg" loading="lazy">

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/120879
推荐阅读
相关标签
  

闽ICP备14008679号