原生js - 两种图片懒加载实现原理

目前图片懒加载的方式主要有两种:

  1、利用getBoundingClientRectAPI得到当前元素与视窗的距离来判断

  2、利用h5的新API IntersectionObserver 来实现

getBoundingClientRect

  Element.getBoundingClientRect() 方法返回值是一个 DOMRect 对象,包含了该元素一组矩形的集合:是与该元素相关的css边框集合(top, left, right, bottom)。

我们可以采用如下方法来判断是否在可视区域:

isViewport (el) {
const viewWidth = window.innerWidth || document.documentElement.clientWidth;
const viewHeight = window.innerHeight || document.documentElement.clientHeight;
let { top, left, right, bottom } = el.getBoundingClientRect()
return (
top >= 0 &&
left >= 0 &&
right <= viewWidth &&
bottom <= viewHeight
)
}

getBoundingClientRect 方式来实现需要监听 scroll 方法来配合,对于浏览器的性能会有一定的问题。

IntersectionObserver

  而 IntersectionObserver 方法是在2016年初提出来的,该API提供了一种异步观察目标元素相对与 root 元素是否进入了可视区域。作为一个新兴API,会有一定的兼容问题,点击查看兼容性
  当 IntersectionObserver 对象被创建,其被配置为监听根中一段给定比例的可见区域。一旦 IntersectionObserver 被创建,则无法更改其配置,所以一个给定的观察者对象只能用来监听可见区域的特定变化值,但是可以在同一个观察者对象中配置监听多个目标元素。
 

 属性

  root: 所监听对象的具体祖先元素。如果未传入值或值为null,则默认使用*文档的视窗。

  rootMargin: 计算交叉时添加到根(root)边界盒bounding box的矩形偏移量, 可以有效的缩小或扩大根的判定范围从而满足计算需要。

  thresholds: 可以是一个单独的number,也可以是一个number数组。当 root 元素与 target 元素相交达到该值的时候会执行回调。当值设定为0时,那么 target 元素有一个像素出现在 root 元素,回调就会执行;如果值设为1,那么就是当 target 元素完全出现在 root 元素当中才会执行。默认为0。如果当值设定为 [0, 0.25, 0.5, 0.75, 1] 时,那么每满足一个值就会回调一次。该值设定的时候不是复数,当取值的时候为复数:

var observer = new IntersectionObserver(_observer, {
root : null,
threshold: [] // 单数
}); observer.thresholds // 复数

 方法

  IntersectionObserver.disconnect: 停止所有监听工作

  IntersectionObserver.observe: 开始监听一个目标元素

  IntersectionObserver.takeRecords: 返回所有观察目标对象的数组

  IntersectionObserver.unobserve: 停止监听特定目标元素。

 
下面添上图片懒加载 js 代码:
function LazyLoad (config) {
this.default = {
root: null,
threshold: 0
}
this.settings = Object.assign(this.default, config)
this.images = []
this.observer = null
this.init()
} LazyLoad.prototype = {
init () {
if (!window.IntersectionObserver) {
this.loadImages()
return
} this.images = document.querySelectorAll(this.settings.selector || '[data-src]') let _this = this
let observeConfig = {
root: this.settings.root,
rootMargin: this.settings.rootMargin,
threshold: [this.settings.threshold]
} this.observer = new IntersectionObserver(changes => {
Array.prototype.forEach.call(changes, entry => { if (entry.isIntersecting) { let target = entry.target
_this.observer.unobserve(target)
let src = target.dataset.src if (target.tagName.toLowerCase() === 'img') {
target.src = src
} else {
target.style.backgroundImage = `url(${src})`
} target.removeAttribute('data-src')
}
})
}, observeConfig) Array.prototype.forEach.call(this.images, image => {
_this.observer.observe(image)
image.src = _this.settings.placeholder
}) }, loadImages () {
let _this = this
_this.replaceSrc() let hasDone = false
function _scroll() {
if (hasDone) return
hasDone = true
setTimeout(() => {
_this.replaceSrc()
hasDone = false
}, 100)
}
window.onscroll = _scroll
}, replaceSrc () {
let _this = this
let imgs = document.querySelectorAll(this.settings.selector || '[data-src]')
Array.prototype.forEach.call(imgs, image => {
if (!image.src) image.src = _this.settings.placeholder
let src = image.dataset.src
if (_this.isInnerView(image)) {
if (image.tagName.toLowerCase() === 'img') {
image.src = src
} else {
image.style.backgroundImage = `url(${src})`
}
image.removeAttribute('data-src')
}
})
}, isInnerView (el) {
const viewWidth = window.innerWidth || document.documentElement.clientWidth;
const viewHeight = window.innerHeight || document.documentElement.clientHeight;
let { top, left, right, bottom } = el.getBoundingClientRect()
return (
top >= 0 &&
left >= 0 &&
right <= viewWidth &&
bottom <= viewHeight
)
}, destroy () {
this.observer.disconnect();
}
}
 
 
 
上一篇:两种图片延迟加载的方法总结jquery.scrollLoading.js与jquery.lazyload.js


下一篇:iOS:图片上传时两种图片压缩方式的比较