предисловие
Отложенная загрузка изображений чаще используется на некоторых веб-сайтах с интенсивным использованием изображений. С помощью отложенной загрузки изображений можно предотвратить загрузку некоторых невидимых изображений, чтобы избежать одновременной загрузки слишком большого количества изображений и блокировки запросов (браузер обычно имеет один и тот же домен имя для Количество соединений для одновременных запросов ограничено), что может повысить скорость загрузки веб-сайта и улучшить взаимодействие с пользователем.
Предварительный просмотр эффекта
Как сделать
первый шаг: Сначала нам нужно сделать тег 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…