图片再怎么压缩,也不能从根本上解决问题,所以我们需要一个终极解决方案——懒加载,只要看不到的图片都不加载。这样就极大的提高了首屏加载速度,同时也节省了大量流量。所以目前大型网站几乎都用到了懒加载技术,懒加载思路也很简单,监听resize和scroll事件,判断img是否在视窗内就可以了。

一、使用 getBoundingClientRect

getBoundingClientRect用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置,该函数返回一个Object对象,该对象有6个属性:top,lef,right,bottom,width,height

var lazy = document.getElementsByClassName('lazy');

window.addEventListener('load', lazyLoad);
window.addEventListener('scroll', lazyLoad);
window.addEventListener('resize', lazyLoad);

function lazyLoad(){
    for(var i=0; i<lazy.length; i++){
        if(isInViewport(lazy[i])){
            if (lazy[i].getAttribute('data-src')){
                lazy[i].src = lazy[i].getAttribute('data-src');
                lazy[i].removeAttribute('data-src');
            }
        }
    }
    
    cleanLazy();
}

function cleanLazy(){
    lazy = lazy.filter(function(l){ return l.getAttribute('data-src');});
}

function isInViewport(el){
    var rect = el.getBoundingClientRect();
    
    return (
        rect.bottom >= 0 && 
        rect.right >= 0 && 
        rect.top <= (window.innerHeight || document.documentElement.clientHeight) && 
        rect.left <= (window.innerWidth || document.documentElement.clientWidth)
     );
}

二、使用 IntersectionObserver

虽然之前我们通过getBoundingClientRect达到了计算img元素是否在视口内的效果,但是这种实现方式性能极差,每次调用 getBoundingClientRect() 都会强制浏览器重新计算整个页面的布局,而且对于在iframe里的元素,getBoundingClientRect就根本无能为力了。

所以 IntersectionObserver API 出现了,它能够能轻松决监视某个元素是否滚动进了视口或者滚动进了它的某个祖先元素的可视区域内,而且解决了性能问题,还无需监听滚动事件。

IntersectionObserver 可以异步观察目标元素与祖先元素或视口的交集变化。

let img = document.querySelector('img');
const IntersectionObserver =  new IntersectionObserver(entries => { 
  console.log('我出现了') 
},{
  root: null,               // 用于计算相交区域的根元素,默认使用最上级文档的视口
  threshold: [0, 0.5, 1],   // 触发回调函数的临界值,用 0 ~ 1 的比率指定,也可以是一个数组。
  rootMargin: "50px, 0px"
});

IntersectionObserve.observe(img);
//其他三个实例方法
//IntersectionObserve.unobserve()
//IntersectionObserve.disconnect()
//IntersectionObserve.takeRecords()

当然功能强大意味着兼容性就不好,目前只能在Chrome51以上使用,就目前来说我们可以使用lazyload.js插件,能在性能和兼容上做一个折中。

拓展阅读
1.IntersectionObserver’s Coming into View
2.IntersectionObserver API