useInViewport
2022-02-25 本文已影响0人
skoll
简介
1 .观察元素是否在可见区域
2 .使用场景:
1 .图片懒加载-当图片滚动到可见位置的时候才加载
2 .内容无限滚动-用户滚动到接近内容底部时直接加载更多
3 .检测广告的曝光率,广告是否被看到了
4 .用户看到某个区域时执行任务或播放动画
3 .传统实现:监听scroll事件,调用目标元素的getBoundingClientRect().得到相对于视口左上角的坐标,在判断是否在视口之内.但是触发非常频繁,这样计算就会有性能问题
4 .最简单例子
import React,{useRef} from 'react'
import {useInViewport} from 'ahooks'
export default function Drag(props){
const ref=useRef()
const [inViewport,ratio]=useInViewport(ref,{
threshold:0.1,
})
console.log(inViewport,ratio)
return (
<div>
<div style={{ width: 300, height: 300, overflow: 'scroll', border: '1px solid',position:'absolute',top:'100px' }}>
scroll here
<div style={{ height: 800 }}>
<div
ref={ref}
style={{
border: '1px solid',
height: 100,
width: 100,
textAlign: 'center',
marginTop: 80,
}}
>
observer dom
</div>
</div>
</div>
{/* <div style={{ marginTop: 16, color: inViewport ? '#87d068' : '#f50' }}>
inViewport: {inViewport ? 'visible' : 'hidden'}
</div> */}
</div>
)
}
原生实现
1 .IntersectionObserver的实现,应该采用requestIdleCallback(),即只有线程空闲下来,才会执行观察器。这意味着,这个观察器的优先级非常低,只在其他任务执行完,浏览器有了空闲才会执行
import React,{useEffect,useState} from 'react'
export default function Drag(props){
const [url,setUrl]=useState('222')
useEffect(()=>{
const io=new IntersectionObserver((entries)=>{
entries.forEach((change)=>{
// let [container,visible]=change
// 这样操作还不行
if(change.intersectionRatio<=0)return
// 消失
let container=change.target
let visible=change.isVisible
console.log('被观察的目标出现在了视口里面')
// 可以加载更多的数据,实现无限滚动的操作.这样做的虚拟列表,我们连右边的滚动条也可以自己实现,不用浏览器自带的版本,虚拟列表也可以这么做.因为浏览器的长度列表是完全靠高度撑起来的,最高是3千万的px.有点离谱.感觉创建这么高的div应该也会耗时间吧.
// 可以更换图片
// 注意这里是每次出现都触发,但其实我们应该是这个机制只触发一次
})
},{})
io.observe(document.getElementById('target'),{
threshold:[0.5,1],
// 只有出现的时候才触发函数,也就是目标元素出现了0.5的时候就会触发函数u
root:document.getElementById('parent'),
// 触发检测的根元素
})
},[])
return (
<div>
<div style={{ width: 300, height: 300, overflow: 'scroll', border: '1px solid',position:'absolute',top:'100px'}}
id="parent"
>
<div style={{ height: 800 }}>
这是一个很长的占位操作
</div>
<div
style={{
border: '1px solid',
height: 100,
width: 100,
textAlign: 'center',
marginTop: 80,
}}
id='target'
>
observer dom{url}
</div>
</div>
{/* <div style={{ marginTop: 16, color: inViewport ? '#87d068' : '#f50' }}>
inViewport: {inViewport ? 'visible' : 'hidden'}
</div> */}
</div>
)
}
图片懒加载的实现效果
1 .img不是先来默认图片,而是一开始都没有地址
img{visibility:hidden}
img[src]{
visibility:visible
}
<img />
2 .注意这是没有src="",有的浏览器空值都会发出请求