背景
今天做了一个功能需要实现元素大小变化控制左右按钮是否展示,首次渲染计算元素宽度大小,如果子元素超过父元素宽度则展示最后左右按钮,否则隐藏,功能类似下图
window.onresize
如果元素的大小是依赖于浏览器窗口大小的话可以使用 window.onresize
来实现,在 resize 回调里面通过重新获取元素大小判断前后宽高是否变更或者重新计算元素大小做相应的操作,不过一个明显的缺点是 resize 事件触发的频率很高,会导致多次回调触发重新计算,性能多多少少会有些影响,可以通过节流来解决。
window.onresize = _.throttle(() => {
const isShow = computedElementSizeAndCheckIfShow(); // 重新计算元素大小并且判断是否展示左右按钮
}, 100);
定时器实现
最直接暴力的做法就是通过定时器 setInterval
监听元素尺寸大小,这方案依赖于宏任务,当网站脚本过多可能会导致反应延迟,还有一点就是会导致很多无效的计算,可以采用 requestAnimationFragment
来替代,粗略实现的效果如下,虽然可以实现,但是不建议
/**
* 监听宽度变化
*/
const widthMonitor = (el, cb) => {
let lastWidth = null;
let rafId = null;
if (!el) return;
const check = () => {
const rect = el.getBoundingClientRect();
if (lastWidth !== rect.width) {
lastWidth = rect.width;
cb(rafId);
}
rafId = requestAnimationFrame(() => {
check();
});
};
check();
};
ResizeObserver 实现
ResizeObserver
是一个新的 api,目前市面上只有新版本的浏览器才支持,唯独 IE 不支持!!!😓 它的功能是能够监听任意元素尺寸大小变化,触发的时间点在浏览器开始绘制前或者完成布局后。
function App() {
const [width, setWidth] = useState(100);
const ref = useRef(null);
useEffect(() => {
const ro = new ResizeObserver((entries) => {
// 遍历改变的元素属性
for (let entry of entries) {
alert("width change", entry.target.getBoundingClientRect().width);
}
});
ro.observe(ref.current);
return () => {
ro.disconnect();
};
}, []);
return (
<div className="App">
<div
ref={ref}
style={{
width: width,
height: 100,
backgroundColor: "#f00",
}}
/>
<button
onClick={() => {
setWidth(200);
}}
>
click
</button>
</div>
);
}