Ленивая загрузка изображения

внешний интерфейс CDN CMS редкоземельный

предисловие

Отложенная загрузка изображений чаще используется на некоторых веб-сайтах с интенсивным использованием изображений. С помощью отложенной загрузки изображений можно предотвратить загрузку некоторых невидимых изображений, чтобы избежать одновременной загрузки слишком большого количества изображений и блокировки запросов (браузер обычно имеет один и тот же домен имя для Количество соединений для одновременных запросов ограничено), что может повысить скорость загрузки веб-сайта и улучшить взаимодействие с пользователем. 

Предварительный просмотр эффекта


Как сделать

первый шаг: Сначала нам нужно сделать тег img в нашем html, который нужно лениво загружатьsrcустановить миниатюру или нетsrc, а затем настройте атрибут со значением реального изображения или адресом исходного изображения (например, data-src ниже) и определите имя класса, чтобы указать, что изображение необходимо загружать лениво (например, lazy-image в следующем примере) Это имеет два эффекта:

1. Для получения элементов img, которым в будущем нужно лениво загружать изображения

2. Вы можете установить фоновое изображение для этого имени класса как чрезмерное изображение перед загрузкой изображения, например изображение, отображаемое как загружаемое. 

<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Чтобы получить набор положения элемента относительно просмотра визуализации, см. Рисунок ниже, обратите вниманиеbottomа такжеrightи наше обычноеrightа такжеbottomРазные. 


третий шаг: Когда пользователь прокручивает окно, проходите все элементы, которые должны быть загружены лениво, черезBoundingClientRectАтрибут для оценки того, появляется ли элемент в видимой области, метод оценки такой же, как и на втором этапе. 

document.addEventListener('scroll', inViewShow)

Здесь мы можем оптимизировать функцию обработчика событий прокрутки с помощью регулирования функции.

Используйте расширенную функцию Intersection Observer, чтобы определить, виден ли элемент

Выше мы используем элементBoundingClientRectизtopатрибуты и телоclientHeightЧтобы судить о том, виден ли элемент, одним из недостатков этого традиционного способа получения того, виден ли элемент, является то, что нам также необходимо привязать событие прокрутки, Событие прокрутки сопровождается большим количеством вычислений, что приведет к трате ресурсов, хотя мы можем улучшить производительность за счет дросселирования функций, но все равно будет проблема потери производительности, иIntersection ObserverВы можете вызвать обратный вызов, как только элемент станет видимым, не прослушивая событие прокрутки.В обратном вызове мы оцениваем, виден ли элемент.

if ("IntersectionObserver" in window) {        
    let lazyImageObserver = new IntersectionObserver((entries, observer) => {          
        entries.forEach((entry, index) => {            
            // 如果元素可见            
            if (entry.intersectionRatio > 0) {              
                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.bind(this))      
    }
  }  
}
// 调用例子
new LazyImage('.lazy-image')

Гит-адрес:GitHub.com/Вики Ли/LA Word…