• 图片懒加载


    摘自:https://juejin.im/post/5bbc60e8f265da0af609cd04#heading-2

    前言 

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

    效果预览 

    如何做 

    第一步: 首先我们需要让我们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[i]        
            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是获取某个元素相对于视窗的位置集合,见下图,注意bottomright和我们平时的rightbottom不一样。 

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

    document.addEventListener('scroll', inViewShow)
    复制代码

    这里我们可以优化下,可以通过函数节流优化滚动事件的处理函数。

    利用高级特性Intersection Observer来判断元素是否可见

    上面我们利用元素的BoundingClientRecttop属性和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[i]        
                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')
    
  • 相关阅读:
    《代码阅读与实践》阅读笔记*part1
    HDFS JAVA API
    《需求工程》阅读笔记*part3
    《需求工程》阅读笔记*part2
    HDFS文件命令
    HOG特征+SVM行人检测
    winchecksec安装踩坑
    加壳:挂起方式创建进程
    内存写入注入
    远程线程注入
  • 原文地址:https://www.cnblogs.com/apgy/p/9772757.html
Copyright © 2020-2023  润新知