A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

© 不二晨 金牌黑马   /  2018-10-11 09:06  /  1062 人查看  /  1 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

前言 图片懒加载在一些图片密集型的网站中运用比较多,通过图片懒加载可以让一些不可视的图片不去加载,避免一次性加载过多的图片导致请求阻塞(浏览器一般对同一域名下的并发请求的连接数有限制),这样就可以提高网站的加载速度,提高用户体验。
效果预览

如何做 第一步: 首先我们需要让我们html中需要懒加载的img标签的src设置缩略图或者不设置src,然后自定义一个属性,值为真正的图片或者原图的地址(比如下面的data-src),并且定义一个类名,表示该图片是需要懒加载的(比如下面例子的lazy-image),这有两个作用:
1、为以后获取需要懒加载图片的img元素
2、可以给这个类名设置背景图片,作为图片未加载前的过度图片,比如显示为loading的图片。



<img data-src="https://tb1.bdstatic.com/tb/cms/liveshow/ent_slid2.jpg" class="lazy-image"/> // css部分 .lazy-image {     background: url('../img/loading.gif') no-repeat center; } 复制代码
第二步:页面加载完后,我们需要获取所有需要懒加载的图片的元素集合,判断是否在可视区域,如果是在可视区域的话,设置元素的src属性值为真正图片的地址。



inViewShow() {         let imageElements = Array.prototype.slice.call(document.querySelectorAll('.lazy-image'))        let len = imageElements.length         for(let i = 0; i < len; i++) {                 let imageElement = imageElements                const rect = imageElement.getBoundingClientRect() // 出现在视野的时候加载图片                 if(rect.top < document.documentElement.clientHeight) {                         imageElement.src = imageElement.dataset.src // 移除掉已经显示的                         imageElements.splice(i, 1)                         len--                         i--                 }         } }复制代码
这里判断是否出现在可视区域内,是通过获取元素的getBoundingClientRect属性的top值和页面的clientHeight进行对比,如果top值小于clientHeight,则说明元素出现在可视区域了。BoundingClientRect是获取某个元素相对于视窗的位置集合,见下图,注意bottom和right和我们平时的right和bottom不一样。


第三步:当用户滚动窗口的时候,遍历所有需要懒加载的元素,通过每个元素的BoundingClientRect属性来判断元素是否出现在可视区域内,判断方法同第二步一样。



document.addEventListener('scroll', inViewShow)复制代码
这里我们可以优化下,可以通过函数节流优化滚动事件的处理函数。
利用高级特性Intersection Observer来判断元素是否可见上面我们利用元素的BoundingClientRect的top属性和body的clientHeight来判断元素是否可见,这种传统方式获取元素是否可见的一个缺点是我们还需要绑定scroll事件,scroll事件是伴随着大量计算的,会造成资源浪费,虽然我们可以通过节流函数来提高性能,但还是会有性能浪费的问题,而Intersection Observer可以不用监听scroll事件,做到元素一可见便调用回调,在回调里面我们来判断元素是否可见。



if ("IntersectionObserver" in window) {            let lazyImageObserver = new IntersectionObserver((entries, observer) => {                  entries.forEach((entry, index) => {                        // 如果元素可见                        if (entry.isIntersecting) {                              let lazyImage = entry.target                              lazyImage.src = lazyImage.dataset.src                              lazyImage.classList.remove("lazy-image")                              lazyImageObserver.unobserve(lazyImage)                              // this.lazyImages.splice(index, 1)                        }                  })            })            this.lazyImages.forEach(function(lazyImage) {                  lazyImageObserver.observe(lazyImage);            })      }复制代码
完整代码


class LazyImage {        constructor(selector) {          // 懒记载图片列表,将伪数组转为数组,以便可以使用数组的api              this.imageElements = Array.prototype.slice.call(document.querySelectorAll(selector))        this.init()        }          inViewShow() {              let len = this.imageElements.length              for(let i = 0; i < len; i++) {                    let imageElement = this.imageElements                    const rect = imageElement.getBoundingClientRect()                    // 出现在视野的时候加载图片                    if(rect.top < document.documentElement.clientHeight) {                          imageElement.src = imageElement.dataset.src                          // 移除掉已经显示的                          this.imageElements.splice(i, 1)                          len--                          i--                          if(this.imageElements.length === 0) {                               // 如果全部都加载完 则去掉滚动事件监听                                document.removeEventListener('scroll', this._throttleFn)                          }                    }              }        }          throttle(fn, delay = 15, mustRun = 30) {              let t_start = null             let timer = null              let context = this              return function() {                    let t_current = +(new Date())                    let args = Array.prototype.slice.call(arguments)                    clearTimeout(timer)                    if(!t_start) {                          t_start = t_current                    }                    if(t_current - t_start > mustRun) {                          fn.apply(context, args)                          t_start = t_current                    } else {                          timer = setTimeout(() => {                                fn.apply(context, args)                          }, delay)                    }              }        }          init() {             // 通过IntersectionObserver api判断图片是否出现在可视区域内,不需要监听Scroll来判断             if ("IntersectionObserver" in window) {                    let lazyImageObserver = new IntersectionObserver((entries, observer) => {                  entries.forEach((entry, index) => {                                // 如果元素可见                                if (entry.isIntersecting) {                                      let lazyImage = entry.target                                      lazyImage.src = lazyImage.dataset.src                                      lazyImage.classList.remove("lazy-image")                                      lazyImageObserver.unobserve(lazyImage)                                      // this.lazyImages.splice(index, 1)                                }                          })                    })                    this.lazyImages.forEach(function(lazyImage) {                          lazyImageObserver.observe(lazyImage);                    })          } else {                this.inViewShow()                this._throttleFn = this.throttle(this.inViewShow)                document.addEventListener('scroll', this._throttleFn)          }  }  }// 调用例子new LazyImage('.lazy-image')复制代码

git地址:github.com/VikiLee/Laz…


【转载】
链接:https://juejin.im/post/5bbc60e8f265da0af609cd04



1 个回复

正序浏览
奈斯
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马