懒加载的方方面面

当访问一个页面的时候,只有当图片出现在浏览器的可视区域内时,才设置图片正真的路径,让图片显示出来。这就是图片懒加载。
  • 首先在html 中设置data-src属性,其次页面滚动时,图片在可视区域内才去加载,有三个函数,滚动函数,判断是否再可视区,图片真正加载
  • 滚动函数
 function onscroll() {
       if (imgs.length === 0) {
           return window.removeEventListener('scroll', onscroll)  // 无图片则取消监听
       }
       imgs = imgs.filter(img => img.classList.contains('lazyload'))
       imgs.forEach(img => {
           inViewport(img) && loadImage(img)
       })
   }
  • 判断是否在可视区
function inViewport(img) {
        let { top,right,left, bottom } = img.getBoundingClientRect()
        let vpWidth = document.documentElement.clientWidth
        let vpHeight = document.documentElement.clientHeight
        return (top > 0 && top < vpHeight || bottom > 0 && bottom < vpHeight) &&
            (left > 0 && left < vpWidth || right > 0 && right < vpWidth)
    }
  • 图片真正加载
 function loadImage(img) {
        let image = new Image()
        image.src = img.dataset.src
        image.onload = function () {
            img.src = image.src
            img.classList.remove('lazyload') // 已经加载过就不再加载,上面有过滤
        }
    }
  • 一个小bug,首屏时,在页面不滚动的时候,图片位置显示空白/占位图,可以用事件派发主动触发一次
window.dispatchEvent(new Event('scroll'))
此时,已经完成一半了。

由于一旦滚动便会监听,判断,加载图片,所以以上三个方法调用会非常频繁,影响性能,甚至如果有些操作,在滚动的时候会出现卡顿的体验。为此,就有了下面的节流

节流

  • 有些现成的库提供了方法,如:underscord.js中_.throttle(function(){},wait),返回一个新的函数,该函数调用之间最少是wait的间隔:如图,在滚动的时,1s内触发2次
  • 自己实现一个throttle
  function throttle(func,wait){  // 节流函数
        let prev ,timer
        return  function fn(){
            let curr = Date.now()
            let diff = curr - prev
            if(!prev || diff >= wait){
                func()
                prev = curr
            }else if(diff < wait){
                clearTimeout(timer)
                timer = setTimeout(func,wait-diff)
            }
        }
    }
  • 新的API Intersection Observer 很简洁的实现了懒加载
 let observer = new IntersectionObserver(function (entries) {
            entries.forEach(entry => {
                if (entry.intersectionRatio > 0) {
                    loadImage(entry.target, () => {
                        observer.unobserve(entry.target)
                    })
                }
            })
        }, {
            threshold: 0.01
        })
        img.forEach(img => observer.observe(img))
  • 此时的loadImage需要有个回调函数
 function loadImage(img, callback) {
        let image = new Image()
        image.src = img.dataset.src
        image.onload = function () {
            img.src = image.src
            img.classList.remove('lazyload') // 已经加载过就不再加载,上面有过滤
            if (typeof callback === 'function') callback() // 只加此句判断
        }
    }

后记

因为用到了新的函数getBoundingClientRect(),为了扩展知识,将其他的可视区范围的相关函数罗列下

屏幕可视窗口大小 
**原生方法**:
        window.innerHeight 标准浏览器及IE9+ ||
        document.documentElement.clientHeight 标准浏览器及低版本IE标准模式 ||document.body.clientHeight  低版本混杂模式
**jQuery方法**: 
        $(window).height();
浏览器窗口顶部与文档顶部之间的距离,也就是滚动条滚动的距离:
**原生方法**:
          window.pagYoffset 标准浏览器及IE9+ ||
          document.documentElement.scrollTop 兼容ie低版本的标准模式 ||
          document.body.scrollTop 兼容混杂模式;
    **jQuery方法**:
          $(document).scrollTop();
获取元素的尺寸
          $(0).width() = $(0).style.width;
          $(0).innerWidth() = $(0).style.width+o.style.padding;
          $(0).outerWidth() = $(0).offsetWidth = $(0).style.width+o.style.padding+o.style.border;
          $(0).outerWidth(true) = $(0).style.width+o.style.padding+o.style.border+o.style.margin;

你可能感兴趣的:(懒加载的方方面面)