Open
Description
简介
interSection Observer 提供了一种异步检测目标元素与指定祖先元素相交情况变化的方法;基于这个API,最常见的两个使用场景是:
- 移动端无限滚动;
- 图片懒加载;
下边具体看看这两个场景的具体例子;
图片懒加载
传统的做法都是绑定scroller事件,滚动过程通过Element.getBoundingClientRect()
计算是否出现在可视区,然后加载图片,然而这些都是在主线程频繁触发,会影响性能;
import React, { useState, useEffect, useRef } from 'react';
import { imgs } from './imgs.ts' //这个文件会返回一个很长的图片列表
export default () => {
useEffect(() => {
const interSectionObserver = new IntersectionObserver(entries => {
// 当目标元素与视窗(默认的祖先元素)交叉超过阈值(默认为0)时的回调,entries是所有观测的目标元素
entries.forEach(item => {
if (item.isIntersecting) { // true表示超过交叉阈值,取dataset.src复制给src,实现图片加载
const imgDom = item.target
const imgSrc = imgDom.dataset.src
imgDom.src = imgSrc
interSectionObserver.unobserve(imgDom) // 图片加载完毕之后,取消观测该元素,否则来回滚动会继续触发;
}
})
}, {
rootMargin: "350px 0px" // 这个会使得根元素的矩形大小上下各增加350px; 实现图片没有出现在屏幕可视区,但是开始加载;
})
Array.from(document.querySelectorAll('img')).forEach(dom => {
interSectionObserver.observe(dom) // 找到所有的img元素,并观测;
})
}, [])
return (<div style={{ height: "600px", overflow: "scroll" }}>
{
imgs.map((item, index) => {
return <div style={{ 'justifyContent': "center" }} key={item}>
{/** 先把图片资源存储在data-src中 */}
<img data-src={item} style={{ width: "300px", height: "165px", margin: "20px" }} />
<span>{index}</span>
</div>
})
}
</div>)
}
无限滚动
这个场景在移动端会比较常见,滚定到底部实现自动分页,一般的做法也是绑定scoller
事件,判断距离底部一定的值时发接口。问题和图片懒加载是一样的,利用IntersectionObserver
就可以改善很多,在最底部放一个元素,观测该元素的交叉状况即可实现分页;
import React, { useState, useEffect, useRef } from 'react';
const bgColors = ['#4e61d4', '#ccc', '#999', 'pink', 'yellow']
const generateBlock = (counts: Number) => {
return new Array(counts).fill(0).map(() => {
const backgroundColor = bgColors[Math.floor(Math.random() * bgColors.length)]
return <div style={{ height: "50px", margin: "20px 0", backgroundColor }} />
})
}
export default function () {
const [blocks, setBlocks] = useState(generateBlock(10));
const footerRef = useRef('')
useEffect(() => {
const interSectionObserver = new IntersectionObserver((enteries) => {
// 可见
if (enteries[0].isIntersecting) {
addblocks(10) //增加10个背景色随机的块(模拟分页)
}
},{
rootMargin:"50px 0px"
});
if (footerRef.current) {
interSectionObserver.observe(footerRef.current)
}
}, [])
const addblocks = (counts: Number) => {
setBlocks((prevBlocks) => ([...prevBlocks, ...generateBlock(counts)]))
}
return (
<>
<div>无线向下滚动</div>
{[...blocks]}
<div ref={footerRef} style={{ height: "1px" }}>footer</div>
</>
);
}
Metadata
Metadata
Assignees
Labels
No labels