feat: 处理窗口resize
This commit is contained in:
parent
d09d852207
commit
dff24401bf
|
@ -5,6 +5,11 @@ export type ViewportId = "CT_AXIAL" | "CT_SAGITTAL" | "CT_CORONAL";
|
||||||
export const viewportId1: ViewportId = "CT_AXIAL";
|
export const viewportId1: ViewportId = "CT_AXIAL";
|
||||||
export const viewportId2: ViewportId = "CT_SAGITTAL";
|
export const viewportId2: ViewportId = "CT_SAGITTAL";
|
||||||
export const viewportId3: ViewportId = "CT_CORONAL";
|
export const viewportId3: ViewportId = "CT_CORONAL";
|
||||||
|
export const viewportIds: ViewportId[] = [
|
||||||
|
viewportId1,
|
||||||
|
viewportId2,
|
||||||
|
viewportId3,
|
||||||
|
];
|
||||||
|
|
||||||
export const viewportColors = {
|
export const viewportColors = {
|
||||||
[viewportId1]: "rgb(200, 0, 0)",
|
[viewportId1]: "rgb(200, 0, 0)",
|
||||||
|
@ -34,7 +39,8 @@ export const viewportReferenceLineSlabThicknessControlsOn = [
|
||||||
export const volumeName = "CT_VOLUME_ID"; // Id of the volume less loader prefix
|
export const volumeName = "CT_VOLUME_ID"; // Id of the volume less loader prefix
|
||||||
export const volumeLoaderScheme = "cornerstoneStreamingImageVolume"; // Loader id which defines which volume loader to use
|
export const volumeLoaderScheme = "cornerstoneStreamingImageVolume"; // Loader id which defines which volume loader to use
|
||||||
export const volumeId = `${volumeLoaderScheme}:${volumeName}`; // VolumeId with loader id + volume id
|
export const volumeId = `${volumeLoaderScheme}:${volumeName}`; // VolumeId with loader id + volume id
|
||||||
export const toolGroupMprId = "toolMprNo1";
|
export const toolGroupMprId = "toolMpr";
|
||||||
|
export const toolGroupStackId = "toolStack";
|
||||||
|
|
||||||
export function getReferenceLineColor(vpId: ViewportId) {
|
export function getReferenceLineColor(vpId: ViewportId) {
|
||||||
return viewportColors[vpId];
|
return viewportColors[vpId];
|
||||||
|
|
|
@ -26,15 +26,14 @@ import {
|
||||||
getReferenceLineDraggableRotatable,
|
getReferenceLineDraggableRotatable,
|
||||||
getReferenceLineSlabThicknessControlsOn,
|
getReferenceLineSlabThicknessControlsOn,
|
||||||
toolGroupMprId,
|
toolGroupMprId,
|
||||||
viewportId1,
|
toolGroupStackId,
|
||||||
viewportId2,
|
viewportIds,
|
||||||
viewportId3,
|
|
||||||
} from "./MprViewer/index.config";
|
} from "./MprViewer/index.config";
|
||||||
import setCtTransferFunctionForVolumeActor from "./MprViewer/CornerstoneDicomLoader/setCtTransferFunctionForVolumeActor";
|
|
||||||
import { Slider } from "@/components/ui/slider";
|
import { Slider } from "@/components/ui/slider";
|
||||||
import { stackViewportId } from "./StackViewer/index.config";
|
import { stackViewportId } from "./StackViewer/index.config";
|
||||||
import { ToolBarMenu } from "./ToolBarMenu";
|
import { ToolBarMenu } from "./ToolBarMenu";
|
||||||
import { Model3DViewer } from "./ModelViewer";
|
import { Model3DViewer } from "./ModelViewer";
|
||||||
|
import useMultiResizeObserver from "./useMultiResizeObserver";
|
||||||
|
|
||||||
const {
|
const {
|
||||||
ToolGroupManager,
|
ToolGroupManager,
|
||||||
|
@ -69,7 +68,16 @@ export const Viewer = () => {
|
||||||
const [imageIds, setImageIds] = useState<string[]>();
|
const [imageIds, setImageIds] = useState<string[]>();
|
||||||
const renderingEngineRef = useRef<RenderingEngine>();
|
const renderingEngineRef = useRef<RenderingEngine>();
|
||||||
const volumeId = SeriesInstanceUID;
|
const volumeId = SeriesInstanceUID;
|
||||||
const toolGroupStackId = "STACK_TOOL_GROUP:" + SeriesInstanceUID;
|
|
||||||
|
useMultiResizeObserver(
|
||||||
|
[
|
||||||
|
stackViewportRef,
|
||||||
|
volumeViewport1Ref,
|
||||||
|
volumeViewport2Ref,
|
||||||
|
volumeViewport3Ref,
|
||||||
|
],
|
||||||
|
() => renderingEngineRef.current?.resize()
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
cornerstoneTools.addTool(StackScrollMouseWheelTool);
|
cornerstoneTools.addTool(StackScrollMouseWheelTool);
|
||||||
|
@ -90,7 +98,7 @@ export const Viewer = () => {
|
||||||
SeriesInstanceUID,
|
SeriesInstanceUID,
|
||||||
wadoRsRoot,
|
wadoRsRoot,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 这一步会对imageIds进行排序,如果不排序imageIds会错误乱,stackViewport顺序会错误
|
// 这一步会对imageIds进行排序,如果不排序imageIds会错误乱,stackViewport顺序会错误
|
||||||
const volume = await volumeLoader.createAndCacheVolume(
|
const volume = await volumeLoader.createAndCacheVolume(
|
||||||
SeriesInstanceUID,
|
SeriesInstanceUID,
|
||||||
|
@ -102,7 +110,7 @@ export const Viewer = () => {
|
||||||
|
|
||||||
const volumeViewportInput: PublicViewportInput[] = [
|
const volumeViewportInput: PublicViewportInput[] = [
|
||||||
{
|
{
|
||||||
viewportId: viewportId1,
|
viewportId: viewportIds[0],
|
||||||
type: ViewportType.ORTHOGRAPHIC,
|
type: ViewportType.ORTHOGRAPHIC,
|
||||||
element: volumeViewport1Ref.current,
|
element: volumeViewport1Ref.current,
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
|
@ -111,7 +119,7 @@ export const Viewer = () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
viewportId: viewportId2,
|
viewportId: viewportIds[1],
|
||||||
type: ViewportType.ORTHOGRAPHIC,
|
type: ViewportType.ORTHOGRAPHIC,
|
||||||
element: volumeViewport2Ref.current,
|
element: volumeViewport2Ref.current,
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
|
@ -120,7 +128,7 @@ export const Viewer = () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
viewportId: viewportId3,
|
viewportId: viewportIds[2],
|
||||||
type: ViewportType.ORTHOGRAPHIC,
|
type: ViewportType.ORTHOGRAPHIC,
|
||||||
element: volumeViewport3Ref.current,
|
element: volumeViewport3Ref.current,
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
|
@ -151,13 +159,11 @@ export const Viewer = () => {
|
||||||
|
|
||||||
const toolGroupMpr = ToolGroupManager.createToolGroup(toolGroupMprId);
|
const toolGroupMpr = ToolGroupManager.createToolGroup(toolGroupMprId);
|
||||||
if (toolGroupMpr) {
|
if (toolGroupMpr) {
|
||||||
toolGroupMpr.addViewport(viewportId1, renderingEngineId);
|
viewportIds.forEach((vp) =>
|
||||||
toolGroupMpr.addViewport(viewportId2, renderingEngineId);
|
toolGroupMpr.addViewport(vp, renderingEngineId)
|
||||||
toolGroupMpr.addViewport(viewportId3, renderingEngineId);
|
);
|
||||||
toolGroupMpr.addTool(ZoomTool.toolName);
|
toolGroupMpr.addTool(ZoomTool.toolName);
|
||||||
toolGroupMpr.setToolActive(ZoomTool.toolName, {
|
toolGroupMpr.addTool(WindowLevelTool.toolName);
|
||||||
bindings: [{ mouseButton: MouseBindings.Secondary }],
|
|
||||||
});
|
|
||||||
toolGroupMpr.addTool(StackScrollMouseWheelTool.toolName);
|
toolGroupMpr.addTool(StackScrollMouseWheelTool.toolName);
|
||||||
toolGroupMpr.addTool(CrosshairsTool.toolName, {
|
toolGroupMpr.addTool(CrosshairsTool.toolName, {
|
||||||
getReferenceLineColor,
|
getReferenceLineColor,
|
||||||
|
@ -165,12 +171,13 @@ export const Viewer = () => {
|
||||||
getReferenceLineDraggableRotatable,
|
getReferenceLineDraggableRotatable,
|
||||||
getReferenceLineSlabThicknessControlsOn,
|
getReferenceLineSlabThicknessControlsOn,
|
||||||
});
|
});
|
||||||
|
toolGroupMpr.setToolActive(ZoomTool.toolName, {
|
||||||
|
bindings: [{ mouseButton: MouseBindings.Secondary }],
|
||||||
|
});
|
||||||
toolGroupMpr.setToolActive(CrosshairsTool.toolName, {
|
toolGroupMpr.setToolActive(CrosshairsTool.toolName, {
|
||||||
bindings: [{ mouseButton: MouseBindings.Primary }],
|
bindings: [{ mouseButton: MouseBindings.Primary }],
|
||||||
});
|
});
|
||||||
toolGroupMpr.setToolActive(StackScrollMouseWheelTool.toolName);
|
toolGroupMpr.setToolActive(StackScrollMouseWheelTool.toolName);
|
||||||
|
|
||||||
toolGroupMpr.addTool(WindowLevelTool.toolName);
|
|
||||||
toolGroupMpr.setToolActive(WindowLevelTool.toolName, {
|
toolGroupMpr.setToolActive(WindowLevelTool.toolName, {
|
||||||
bindings: [{ mouseButton: MouseBindings.Auxiliary }],
|
bindings: [{ mouseButton: MouseBindings.Auxiliary }],
|
||||||
});
|
});
|
||||||
|
@ -195,12 +202,8 @@ export const Viewer = () => {
|
||||||
await setVolumesForViewports(
|
await setVolumesForViewports(
|
||||||
renderingEngineRef.current,
|
renderingEngineRef.current,
|
||||||
[{ volumeId: SeriesInstanceUID }],
|
[{ volumeId: SeriesInstanceUID }],
|
||||||
[viewportId1, viewportId2, viewportId3]
|
viewportIds
|
||||||
);
|
);
|
||||||
|
|
||||||
// 默认windowWidtth
|
|
||||||
// const { windowCenter, windowWidth } =
|
|
||||||
// volumeRef.current.cornerstoneImageMetaData;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
initCornerstone(() => {
|
initCornerstone(() => {
|
||||||
|
@ -208,10 +211,6 @@ export const Viewer = () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
// renderingEngineRef.current?.disableElement(stackViewportId);
|
|
||||||
// renderingEngineRef.current?.disableElement(viewportId1);
|
|
||||||
// renderingEngineRef.current?.disableElement(viewportId2);
|
|
||||||
// renderingEngineRef.current?.disableElement(viewportId3);
|
|
||||||
cache.purgeCache();
|
cache.purgeCache();
|
||||||
ToolGroupManager.destroy();
|
ToolGroupManager.destroy();
|
||||||
renderingEngineRef.current?.destroy();
|
renderingEngineRef.current?.destroy();
|
||||||
|
@ -221,7 +220,7 @@ export const Viewer = () => {
|
||||||
cornerstoneTools.removeTool(WindowLevelTool);
|
cornerstoneTools.removeTool(WindowLevelTool);
|
||||||
cornerstoneTools.removeTool(ZoomTool);
|
cornerstoneTools.removeTool(ZoomTool);
|
||||||
};
|
};
|
||||||
}, [SeriesInstanceUID, StudyInstanceUID, toolGroupStackId, volumeId]);
|
}, [SeriesInstanceUID, StudyInstanceUID, volumeId]);
|
||||||
|
|
||||||
const onChangeIndex = (value: number[]) => setIndex(value[0]);
|
const onChangeIndex = (value: number[]) => setIndex(value[0]);
|
||||||
|
|
||||||
|
@ -253,25 +252,6 @@ export const Viewer = () => {
|
||||||
}
|
}
|
||||||
}, [index]);
|
}, [index]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const container = stackViewportRef.current;
|
|
||||||
if (!container) return;
|
|
||||||
let resizeTimeout: NodeJS.Timeout | null = null;
|
|
||||||
const resizeObserver = new ResizeObserver(() => {
|
|
||||||
if (resizeTimeout) clearTimeout(resizeTimeout);
|
|
||||||
resizeTimeout = setTimeout(
|
|
||||||
() => renderingEngineRef.current?.resize(),
|
|
||||||
100
|
|
||||||
);
|
|
||||||
});
|
|
||||||
resizeObserver.observe(container);
|
|
||||||
return () => {
|
|
||||||
if (resizeTimeout) clearTimeout(resizeTimeout);
|
|
||||||
resizeObserver.unobserve(container);
|
|
||||||
resizeObserver.disconnect();
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full h-full flex flex-col">
|
<div className="w-full h-full flex flex-col">
|
||||||
<div className="flex-shrink-0 border-b border-secondary">
|
<div className="flex-shrink-0 border-b border-secondary">
|
||||||
|
@ -315,15 +295,19 @@ export const Viewer = () => {
|
||||||
</ResizablePanel>
|
</ResizablePanel>
|
||||||
<ResizableHandle withHandle />
|
<ResizableHandle withHandle />
|
||||||
<ResizablePanel defaultSize={50}>
|
<ResizablePanel defaultSize={50}>
|
||||||
<div
|
<ResizablePanelGroup direction="vertical" className="w-full h-full">
|
||||||
className="w-full h-1/3 border-b border-1 border-secondary"
|
<ResizablePanel defaultSize={1 / 3}>
|
||||||
ref={volumeViewport1Ref}
|
<div className="h-full" ref={volumeViewport1Ref} />
|
||||||
/>
|
</ResizablePanel>
|
||||||
<div
|
<ResizableHandle withHandle />
|
||||||
className="w-full h-1/3 border-b border-1 border-secondary"
|
<ResizablePanel defaultSize={1 / 3}>
|
||||||
ref={volumeViewport2Ref}
|
<div className="h-full" ref={volumeViewport2Ref} />
|
||||||
/>
|
</ResizablePanel>
|
||||||
<div className="w-full h-1/3" ref={volumeViewport3Ref} />
|
<ResizableHandle withHandle />
|
||||||
|
<ResizablePanel defaultSize={1 / 3}>
|
||||||
|
<div className="h-full" ref={volumeViewport3Ref} />
|
||||||
|
</ResizablePanel>
|
||||||
|
</ResizablePanelGroup>
|
||||||
</ResizablePanel>
|
</ResizablePanel>
|
||||||
</ResizablePanelGroup>
|
</ResizablePanelGroup>
|
||||||
</div>
|
</div>
|
||||||
|
|
41
apps/desktop/src/pages/Viewer/useMultiResizeObserver.tsx
Normal file
41
apps/desktop/src/pages/Viewer/useMultiResizeObserver.tsx
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import { useEffect, RefObject } from "react";
|
||||||
|
|
||||||
|
type ResizeObserverCallback = () => void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义 Hook,用于监听多个 Ref 对象的尺寸变化
|
||||||
|
* @param refs - 要监听的 Ref 对象数组
|
||||||
|
* @param callback - 尺寸变化时调用的回调函数
|
||||||
|
*/
|
||||||
|
function useMultiResizeObserver(
|
||||||
|
refs: Array<RefObject<Element>>,
|
||||||
|
callback: ResizeObserverCallback
|
||||||
|
) {
|
||||||
|
useEffect(() => {
|
||||||
|
const elements = refs
|
||||||
|
.map((ref) => ref.current)
|
||||||
|
.filter((el): el is Element => el !== null);
|
||||||
|
|
||||||
|
if (elements.length === 0) return;
|
||||||
|
|
||||||
|
let resizeTimeout: NodeJS.Timeout | null = null;
|
||||||
|
const resizeObserver = new ResizeObserver(() => {
|
||||||
|
if (resizeTimeout) clearTimeout(resizeTimeout);
|
||||||
|
resizeTimeout = setTimeout(callback, 100);
|
||||||
|
});
|
||||||
|
|
||||||
|
elements.forEach((element) => {
|
||||||
|
resizeObserver.observe(element);
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
if (resizeTimeout) clearTimeout(resizeTimeout);
|
||||||
|
elements.forEach((element) => {
|
||||||
|
resizeObserver.unobserve(element);
|
||||||
|
});
|
||||||
|
resizeObserver.disconnect();
|
||||||
|
};
|
||||||
|
}, [refs, callback]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useMultiResizeObserver;
|
Loading…
Reference in New Issue
Block a user