所以我正在研究一个绘制中心和辐条图的功能(即我有一个中心 div,周围有其他 div 有 svg 线将所有外部 div 连接到中心)。您可以在此处看到一个稍微简化但仍然完整的代码沙箱示例。
为此,我制作了一个钩子,它将使用它们的 ref 获取每个 div 的边界框。它可以工作,但是触发次数过多并导致延迟。特别是当它有最大数量的盒子时
这是获取边界框的 ref。我相信这就是问题所在:
import { useCallback, useLayoutEffect, useState } from "react";
const useBoundingBoxWithListener = (ref, childRef) => {
const [bbox, setBbox] = useState({});
const set = useCallback(
() =>
setBbox(ref && ref.current ? ref.current.getBoundingClientRect() : {}),
[ref]
);
useLayoutEffect(() => {
set();
window.addEventListener("animationend", set);
window.addEventListener("resize", set);
return () => {
window.removeEventListener("animationend", set);
window.removeEventListener("resize", set);
};
}, [ref, set]);
return { bbox, ref };
};
export { useBoundingBoxWithListener };
这是为每个 ref 使用边界框钩子的组件:
import React from "react";
import { locationEnum } from "./Spoke";
import { useBoundingBoxWithListener } from "./useBoundingBoxWithListener";
const useSpokes = () => {
const middleLeftCard = React.useRef();
const middleCard = React.useRef();
const middleRightCard = React.useRef();
const topLeftCard = React.useRef();
const topMiddleCard = React.useRef();
const topRightCard = React.useRef();
const bottomLeftCard = React.useRef();
const bottomMiddleCard = React.useRef();
const bottomRightCard = React.useRef();
const middleLeftSvgRef = React.useRef();
const middleLeftFoRef = React.useRef();
const middleRightSvgRef = React.useRef();
const middleRightFoRef = React.useRef();
const topLeftSvgRef = React.useRef();
const topLeftFoRef = React.useRef();
const topMiddleSvgRef = React.useRef();
const topMiddleFoRef = React.useRef();
const topRightSvgRef = React.useRef();
const topRightFoRef = React.useRef();
const bottomLeftSvgRef = React.useRef();
const bottomLeftFoRef = React.useRef();
const bottomMiddleSvgRef = React.useRef();
const bottomMiddleFoRef = React.useRef();
const bottomRightSvgRef = React.useRef();
const bottomRightFoRef = React.useRef();
const { bbox: middleLeftBox } = useBoundingBoxWithListener(middleLeftCard);
const { bbox: middleBox } = useBoundingBoxWithListener(middleCard);
const { bbox: middleRightBox } = useBoundingBoxWithListener(middleRightCard);
const { bbox: topLeftBox } = useBoundingBoxWithListener(topLeftCard);
const { bbox: topMiddleBox } = useBoundingBoxWithListener(topMiddleCard);
const { bbox: topRightBox } = useBoundingBoxWithListener(topRightCard);
const { bbox: bottomLeftBox } = useBoundingBoxWithListener(bottomLeftCard);
const { bbox: bottomMiddleBox } = useBoundingBoxWithListener(
bottomMiddleCard
);
const { bbox: bottomRightBox } = useBoundingBoxWithListener(bottomRightCard);
const { bbox: middleLeftSvgBox } = useBoundingBoxWithListener(
middleLeftSvgRef
);
const { bbox: middleRightSvgBox } = useBoundingBoxWithListener(
middleRightSvgRef
);
const { bbox: topLeftSvgBox } = useBoundingBoxWithListener(topLeftSvgRef);
const { bbox: topMiddleSvgBox } = useBoundingBoxWithListener(topMiddleSvgRef);
const { bbox: topRightSvgBox } = useBoundingBoxWithListener(topRightSvgRef);
const { bbox: bottomLeftSvgBox } = useBoundingBoxWithListener(
bottomLeftSvgRef
);
const { bbox: bottomMiddleSvgBox } = useBoundingBoxWithListener(
bottomMiddleSvgRef
);
const { bbox: bottomRightSvgBox } = useBoundingBoxWithListener(
bottomRightSvgRef
);
const { bbox: middleLeftFoBox } = useBoundingBoxWithListener(middleLeftFoRef);
const { bbox: middleRightFoBox } = useBoundingBoxWithListener(
middleRightFoRef
);
const { bbox: topLeftFoBox } = useBoundingBoxWithListener(topLeftFoRef);
const { bbox: topMiddleFoBox } = useBoundingBoxWithListener(topMiddleFoRef);
const { bbox: topRightFoBox } = useBoundingBoxWithListener(topRightFoRef);
const { bbox: bottomLeftFoBox } = useBoundingBoxWithListener(bottomLeftFoRef);
const { bbox: bottomMiddleFoBox } = useBoundingBoxWithListener(
bottomMiddleFoRef
);
const { bbox: bottomRightFoBox } = useBoundingBoxWithListener(
bottomRightFoRef
);
const spokes = [
{},
{
from: middleBox,
to: topMiddleBox,
fromRef: middleCard,
toRef: topMiddleCard,
start: locationEnum.topMiddle,
end: locationEnum.bottomMiddle
},
{
from: middleBox,
to: bottomMiddleBox,
fromRef: middleCard,
toRef: bottomMiddleCard,
start: locationEnum.bottomMiddle,
end: locationEnum.topMiddle
},
{
from: middleBox,
to: middleLeftBox,
fromRef: middleCard,
toRef: middleLeftCard,
start: locationEnum.middleLeft,
end: locationEnum.middleRight
},
{
from: middleBox,
to: middleRightBox,
fromRef: middleCard,
toRef: middleRightCard,
start: locationEnum.middleRight,
end: locationEnum.middleLeft
},
{
from: middleBox,
to: topLeftBox,
fromRef: middleCard,
toRef: topLeftCard,
start: locationEnum.topLeft,
end: locationEnum.bottomRight
},
{
from: middleBox,
to: bottomRightBox,
fromRef: middleCard,
toRef: bottomRightCard,
start: locationEnum.bottomRight,
end: locationEnum.topLeft
},
{
from: middleBox,
to: topRightBox,
fromRef: middleCard,
toRef: topRightCard,
start: locationEnum.topRight,
end: locationEnum.bottomLeft
},
{
from: middleBox,
to: bottomLeftBox,
fromRef: middleCard,
toRef: bottomLeftCard,
start: locationEnum.bottomLeft,
end: locationEnum.topRight
}
];
const drawings = [
{},
{
svgBox: topMiddleSvgBox,
svgRef: topMiddleSvgRef,
foreignObjectChildBox: topMiddleFoBox,
foreignObjectChildRef: topMiddleFoRef
},
{
svgBox: bottomMiddleSvgBox,
svgRef: bottomMiddleSvgRef,
foreignObjectChildBox: bottomMiddleFoBox,
foreignObjectChildRef: bottomMiddleFoRef
},
{
svgBox: middleLeftSvgBox,
svgRef: middleLeftSvgRef,
foreignObjectChildBox: middleLeftFoBox,
foreignObjectChildRef: middleLeftFoRef
},
{
svgBox: middleRightSvgBox,
svgRef: middleRightSvgRef,
foreignObjectChildBox: middleRightFoBox,
foreignObjectChildRef: middleRightFoRef
},
{
svgBox: topLeftSvgBox,
svgRef: topLeftSvgRef,
foreignObjectChildBox: topLeftFoBox,
foreignObjectChildRef: topLeftFoRef
},
{
svgBox: bottomRightSvgBox,
svgRef: bottomRightSvgRef,
foreignObjectChildBox: bottomRightFoBox,
foreignObjectChildRef: bottomRightFoRef
},
{
svgBox: topRightSvgBox,
svgRef: topRightSvgRef,
foreignObjectChildBox: topRightFoBox,
foreignObjectChildRef: topRightFoRef
},
{
svgBox: bottomLeftSvgBox,
svgRef: bottomLeftSvgRef,
foreignObjectChildBox: bottomLeftFoBox,
foreignObjectChildRef: bottomLeftFoRef
}
];
return { spokes, drawings, middleCard };
};
export default useSpokes;
这是让 div 准备好显示的组件。
const MyView = ({ children }) => {
const layoutClasses = [
"subject",
"topMiddle",
"bottomMiddle",
"middleLeft",
"middleRight",
"topLeft",
"bottomRight",
"topRight",
"bottomLeft"
];
const { spokes, drawings, middleCard } = useSpokes();
return (
<div className="layout">
{children.map((c, i) => {
return (
<div
key={i}
ref={i === 0 ? middleCard : spokes[i].toRef}
className={`${layoutClasses[i]} timing`}
>
{c}
{i > 0 ? (
<Spoke
text={c.props?.subjectPredicate}
foBox={drawings[i].foreignObjectChildBox}
foRef={drawings[i].foreignObjectChildRef}
{...spokes[i]}
{...drawings[i]}
/>
) : (
""
)}
</div>
);
})}
</div>
);
};
这是实际显示所有内容的组件:
<MyView>
<div className="card"></div>
<div className="card" subjectPredicate={"node"}></div>
<div className="card" subjectPredicate={"node"}></div>
<div className="card" subjectPredicate={"node"}></div>
<div className="card" subjectPredicate={"node"}></div>
<div className="card" subjectPredicate={"node"}></div>
<div className="card" subjectPredicate={"node"}></div>
<div className="card" subjectPredicate={"node"}></div>
<div className="card" subjectPredicate={"node"}></div>
</MyView>
我在整个示例console.log的Spokes组件中添加了一个以显示它在控制台中实际被击中的次数(1000+)。如果你想分叉它并尝试直接使用它,你可以在上面的代码沙箱中看到所有东西一起工作。