使用场景
移动端下拉滚动加载图片的时候,通常是使用监听scroll或者使用setInterval来判断,元素是否进入视图,其中scroll由于其特别大的计算量,会有性能问题,而setInterval由于其有间歇期,也会出现体验问题。
IntersectionObserver
允许你追踪目标元素与其祖先元素或视窗的交叉状态。IntersectionObserver API 是异步的,不随着目标元素的滚动同步触发。 即只有线程空闲下来,才会执行观察器。这意味着,这个观察器的优先级非常低,只在其他任务执行完,浏览器有了空闲才会执行。
代码演示
<body>
<div style="height: 1800px;width:100%; text-align: center;">
<h3>监听目标元素与祖先或视口交叉状态的手段</h3>
<p>./image/preview.jpg</p>
</div>
<img class="imageLoad" alt="图片懒加载">
<img class="imageLoad" alt="图片懒加载">
<img class="imageLoad" alt="图片懒加载">
<img class="imageLoad" alt="图片懒加载">
<img class="imageLoad" alt="图片懒加载">
<img class="imageLoad" alt="图片懒加载">
<img class="imageLoad" alt="图片懒加载">
<img class="imageLoad" alt="图片懒加载">
<img class="imageLoad" alt="图片懒加载">
<img class="imageLoad" alt="图片懒加载">
<img class="imageLoad" alt="图片懒加载">
<img class="imageLoad" alt="图片懒加载">
<img class="imageLoad" alt="图片懒加载">
<img class="imageLoad" alt="图片懒加载">
<img class="imageLoad" alt="图片懒加载">
<script type="module">
import { Intersection } from './IntersectionOberver.js'
import { watchScroll } from './监听滚动实现懒加载.js'
// watchScroll('.imageLoad')
Intersection('.imageLoad')
</script>
</body>
export const Intersection = (name) => {
const img = document.querySelectorAll(name)
let index = 0
const options = {
threshold: [0],//threshold:属性决定相交比例为多少时,触发回调函数。取值为 0 ~ 1,或者 0 ~ 1的数组。
rootMargin: "300px",//rootMargin: 类似于 CSS 的 margin 属性。用来缩小或扩大 rootBounds,从而影响相交的触发
root: null,//root:设置监视器的根节点,不传则默认为视口。
}
/*
entries的属性
time:发生相交到相应的时间,毫秒。
rootBounds:根元素矩形区域的信息,如果没有设置根元素则返回null,图中蓝色部分区域。
boundingClientRect:目标元素的矩形区域的信息,图中黑色边框的区域。
intersectionRect:目标元素与视口(或根元素)的交叉区域的信息,图中蓝色方块和粉红色方块相交的区域。
isIntersecting:目标元素与根元素是否相交
intersectionRatio:目标元素与视口(或根元素)的相交比例。
target:目标元素。
*/
const iso = new IntersectionObserver(entries => {
entries.forEach(v => {
if (v.isIntersecting) {
if (index >= 2) index = 0
index++
v.target.setAttribute('src', `./image/image${index}.jpg`)
iso.unobserve(v.target)
}
})
}, options)
// 开启观察
img.forEach(element => {
iso.observe(element)
});
}
效果展示
可以看到当元素与视口rootBounds=300px时,触发回调
scroll方式
const thorttle = (callback,wait=500) => {
let timer = null
let licence = true
const _this = this
return (...args) => {
if (licence) {
timer = setTimeout(() => {
callback.call(_this, args)
licence = true
clearTimeout(timer)
},wait)
}
licence = false
}
}
//getBoundingClientRect 方法会使浏览器发生回流和重绘,性能消耗稍大,但兼容性比 Intersection Observer 要好。
export const watchScroll = (name) => {
const img = document.querySelectorAll(name)
//document.documentElement.clientHeight可视窗口高度
const vHeight = window.innerHeight || document.documentElement.clientHeight
document.addEventListener('scroll', thorttle(() => {
let index = 0
img.forEach(v => {
// Element.getBoundingClientRect() 方法返回元素的大小及其相对于视口顶部的位置。
let {top,height } = v.getBoundingClientRect()
if (top <= vHeight+100) {
if (index >= 2) index = 0
index++
v.setAttribute('src', `./image/image${index}.jpg`)
}
})
}))
}