来源于resize-observer-polyfill
1.浏览器resizeObserver兼容性存在问题
如果没有原生的ResizeObserver方法 将使用垫片提供的方法
export default (() => {
// Export existing implementation if available.
// 启用原生api
if (typeof global.ResizeObserver !== 'undefined') {
return global.ResizeObserver;
}
// 启用垫片api
return ResizeObserverPolyfill;
})();
2.监听的属性变化和刷新
在树形变化后调用响应的对象 和mobx,formily类似的一套实现
// 会影响size的有 onResize只在window生效
window.addEventListener('resize', this.refresh);
// 普通元素怎么处理 拖拽完成形变的时候触发trannsitionend事件
document.addEventListener('transitionend', this.onTransitionEnd_);
// transition的形变可能影响到的属性
const transitionKeys = ['top', 'right', 'bottom', 'left', 'width', 'height', 'size', 'weight'];
// 记录和计算前后属性变化 如果变化 触发sizeChange通知
function getHTMLElementContentRect(target) {
const {clientWidth, clientHeight} = target;
if (!clientWidth && !clientHeight) {
return emptyRect;
}
const styles = getWindowOf(target).getComputedStyle(target);
const paddings = getPaddings(styles);
const horizPad = paddings.left + paddings.right;
const vertPad = paddings.top + paddings.bottom;
let width = toFloat(styles.width),
height = toFloat(styles.height);
// model is applied (except for IE).
if (styles.boxSizing === 'border-box') {
if (Math.round(width + horizPad) !== clientWidth) {
width -= getBordersSize(styles, 'left', 'right') + horizPad;
}
if (Math.round(height + vertPad) !== clientHeight) {
height -= getBordersSize(styles, 'top', 'bottom') + vertPad;
}
}
if (!isDocumentElement(target)) {
const vertScrollbar = Math.round(width + horizPad) - clientWidth;
const horizScrollbar = Math.round(height + vertPad) - clientHeight;
if (Math.abs(vertScrollbar) !== 1) {
width -= vertScrollbar;
}
if (Math.abs(horizScrollbar) !== 1) {
height -= horizScrollbar;
}
}
return createRectInit(paddings.left, paddings.top, width, height);
}
3.react-component层面组装
// 用useRef不变特性保存各阶段size数据
const sizeRef = React.useRef({
width: -1,
height: -1,
offsetWidth: -1,
offsetHeight: -1,
});
<SingleObserver {...props} key={key}>
{child}
</SingleObserver>
// 第一顺位获取children的ref做被监听对象的dom
const elementRef = React.useRef<Element>(null);
...
const mergedChildren = isRenderProps ? children(elementRef) : children;
// 备选通过当前reactNode的挂载找到上层dom
ReactDom.findDOMNode(node)
const currentElement: HTMLElement =
findDOMNode(elementRef.current) || findDOMNode(wrapperRef.current);
// 通过两层dom处理的意思是 原始组件最好自带一个wapper包裹要onResize处理的部分
// 否则往顶层冒泡拿一个wapper 不一定符合
// 启用监听
React.useEffect(() => {
const currentElement: HTMLElement =
findDOMNode(elementRef.current) || findDOMNode(wrapperRef.current);
if (currentElement && !disabled) {
// 开启监听 回调为onInternalResize
observe(currentElement, onInternalResize);
}
return () => unobserve(currentElement, onInternalResize);
}, [elementRef.current, disabled]);
// onInternalResize
onCollectionResize?.(sizeInfo, target, data);
// defer the callback but not defer to next frame
// 顺延一个frame刷新时间处理 微任务 frame开启后会查询
Promise.resolve().then(() => {
onResize(sizeInfo, target);
});