懒加载原理
当浏览器滚动到相应的位置时再渲染相应的图片。主要利用图片src属性的重新赋值来实现。
将img
标签中的src
的路径设置为空或默认图片(空白图片、Loading图),缓存真正的图片路径,当浏览器滚动到相应的位置时,再给src属性重新赋值。
懒加载目的
优化前端压力,减少首屏请求数,延迟请求。对服务器压力有一定缓解作用。
但是若图片较大,则会带来较差的用户体验。
方案一:使用滚动事件scroll实现
将img
标签中的src
的路径设置为同一张图片(空白图片),将真正的图片路径保存在data-src
属性中,通过scrollTop
方法获取浏览器窗口顶部与文档顶部之间的距离,通过clientHeight
方法获取可视区高度。scroll
触发时,执行事件载入data-src
(对每个img
标签DOM来求其offsetTop
,如果距离顶部的高度 >=可视区高度+窗口距离文档顶部的距离 )。
.lazy-image {
background: url('../img/loading.gif') no-repeat center;
}
document.addEventListener('scroll', inViewShow)
function inViewShow() {
let imageElements = Array.prototype.slice.call(document.querySelectorAll('img.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--
}
}
}
方案二:使用IntersectionObserver实现
IntersectionObserver
可以异步监听 目标元素 与其 祖先元素 或 文档视窗 (可视区viewport) 交叉状态的方法。文档详见Intersection Observer - Web API 接口参考 | MDN (mozilla.org)
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('img.lazy-image')
本文固定连接:https://code.zuifengyun.com/2017/04/3196.html,转载须征得作者授权。