refactor(viewport): Remove resize detector and optimize viewport resizing logic (#5006)

This commit is contained in:
Alireza 2025-05-05 11:27:26 -04:00 committed by GitHub
parent 6337d2e9cb
commit 383f45823c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 38 additions and 46 deletions

View File

@ -1,5 +1,4 @@
import React, { useEffect, useRef, useCallback, useState } from 'react'; import React, { useEffect, useRef, useCallback, useState } from 'react';
import { useResizeDetector } from 'react-resize-detector';
import * as cs3DTools from '@cornerstonejs/tools'; import * as cs3DTools from '@cornerstonejs/tools';
import { Enums, eventTarget, getEnabledElement } from '@cornerstonejs/core'; import { Enums, eventTarget, getEnabledElement } from '@cornerstonejs/core';
import { MeasurementService } from '@ohif/core'; import { MeasurementService } from '@ohif/core';
@ -110,7 +109,23 @@ const OHIFCornerstoneViewport = React.memo(
cornerstoneViewportService.resize(); cornerstoneViewportService.resize();
setImageScrollBarHeight(); setImageScrollBarHeight();
} }
}, [elementRef]); }, [elementRef, cornerstoneViewportService, setImageScrollBarHeight]);
useEffect(() => {
const element = elementRef.current;
if (!element) {
return;
}
const resizeObserver = new ResizeObserver(onResize);
resizeObserver.observe(element);
// Cleanup function
return () => {
resizeObserver.unobserve(element);
resizeObserver.disconnect();
};
}, [onResize]);
const cleanUpServices = useCallback( const cleanUpServices = useCallback(
viewportInfo => { viewportInfo => {
@ -350,10 +365,6 @@ const OHIFCornerstoneViewport = React.memo(
} }
}, [displaySets, viewportId, viewportActionCornersService, servicesManager, commandsManager]); }, [displaySets, viewportId, viewportActionCornersService, servicesManager, commandsManager]);
const { ref: resizeRef } = useResizeDetector({
onResize,
});
const Notification = customizationService.getCustomization('ui.notificationComponent'); const Notification = customizationService.getCustomization('ui.notificationComponent');
return ( return (
@ -364,10 +375,7 @@ const OHIFCornerstoneViewport = React.memo(
style={{ height: '100%', width: '100%' }} style={{ height: '100%', width: '100%' }}
onContextMenu={e => e.preventDefault()} onContextMenu={e => e.preventDefault()}
onMouseDown={e => e.preventDefault()} onMouseDown={e => e.preventDefault()}
ref={el => { ref={elementRef}
resizeRef.current = el;
elementRef.current = el;
}}
></div> ></div>
<CornerstoneOverlays <CornerstoneOverlays
viewportId={viewportId} viewportId={viewportId}

View File

@ -232,16 +232,6 @@ export default async function init({
} }
); );
// resize the cornerstone viewport service when the grid size changes
// IMPORTANT: this should happen outside of the OHIFCornerstoneViewport
// since it will trigger a rerender of each viewport and each resizing
// the offscreen canvas which would result in a performance hit, this should
// done only once per grid resize here. Doing it once here, allows us to reduce
// the refreshRage(in ms) to 10 from 50. I tried with even 1 or 5 ms it worked fine
viewportGridService.subscribe(viewportGridService.EVENTS.GRID_SIZE_CHANGED, () => {
cornerstoneViewportService.resize(true);
});
initContextMenu({ initContextMenu({
cornerstoneViewportService, cornerstoneViewportService,
customizationService, customizationService,

View File

@ -38,6 +38,9 @@ const EVENTS = {
VIEWPORT_VOLUMES_CHANGED: 'event::cornerstoneViewportService:viewportVolumesChanged', VIEWPORT_VOLUMES_CHANGED: 'event::cornerstoneViewportService:viewportVolumesChanged',
}; };
const MIN_STACK_VIEWPORTS_TO_ENQUEUE_RESIZE = 12;
const MIN_VOLUME_VIEWPORTS_TO_ENQUEUE_RESIZE = 6;
export const WITH_NAVIGATION = { withNavigation: true, withOrientation: false }; export const WITH_NAVIGATION = { withNavigation: true, withOrientation: false };
/** /**
@ -62,9 +65,6 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
beforeResizePositionPresentations: Map<string, PositionPresentation> = new Map(); beforeResizePositionPresentations: Map<string, PositionPresentation> = new Map();
// Some configs // Some configs
enableResizeDetector: true;
resizeRefreshRateMs: 200;
resizeRefreshMode: 'debounce';
servicesManager: AppTypes.ServicesManager = null; servicesManager: AppTypes.ServicesManager = null;
resizeQueue = []; resizeQueue = [];
@ -125,13 +125,8 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
/** /**
* It triggers the resize on the rendering engine, and renders the viewports * It triggers the resize on the rendering engine, and renders the viewports
* *
* @param isGridResize - if the resize is triggered by a grid resize
* this is used to avoid double resize of the viewports since if the
* grid is resized, all viewports will be resized so there is no need
* to resize them individually which will get triggered by their
* individual resize observers
*/ */
public resize(isGridResize = false) { public resize() {
// https://stackoverflow.com/a/26279685 // https://stackoverflow.com/a/26279685
// This resize() call, among other things, rerenders the viewports. But when the entire viewer is // This resize() call, among other things, rerenders the viewports. But when the entire viewer is
// display: none'd, it makes the size of all hidden elements 0, including the viewport canvas and its containers. // display: none'd, it makes the size of all hidden elements 0, including the viewport canvas and its containers.
@ -147,12 +142,24 @@ class CornerstoneViewportService extends PubSubService implements IViewportServi
return; return;
} }
const numStackViewportsInViewportGrid = Array.from(this.viewportsById.values()).filter(
viewportInfo => viewportInfo.getViewportType() === csEnums.ViewportType.STACK
).length;
const numVolumeViewportsInViewportGrid = Array.from(this.viewportsById.values()).filter(
viewportInfo => viewportInfo.getViewportType() === csEnums.ViewportType.ORTHOGRAPHIC
).length;
const isEasyResize =
numStackViewportsInViewportGrid <= MIN_STACK_VIEWPORTS_TO_ENQUEUE_RESIZE &&
numVolumeViewportsInViewportGrid <= MIN_VOLUME_VIEWPORTS_TO_ENQUEUE_RESIZE;
// if there is a grid resize happening, it means the viewport grid // if there is a grid resize happening, it means the viewport grid
// has been manipulated (e.g., panels closed, added, etc.) and we need // has been manipulated (e.g., panels closed, added, etc.) and we need
// to resize all viewports, so we will add a timeout here to make sure // to resize all viewports, so we will add a timeout here to make sure
// we don't double resize the viewports when viewports in the grid are // we don't double resize the viewports when viewports in the grid are
// resized individually // resized individually
if (isGridResize) { if (isEasyResize) {
this.performResize(); this.performResize();
this.resetGridResizeTimeout(); this.resetGridResizeTimeout();
this.resizeQueue = []; this.resizeQueue = [];

View File

@ -35,9 +35,8 @@ export interface IViewportService {
/** /**
* It creates a resize observer for the viewport element, and observes * It creates a resize observer for the viewport element, and observes
* the element for resizing events * the element for resizing events
* @param {*} elementRef
*/ */
resize(isGridResize: boolean): void; resize(): void;
/** /**
* Removes the viewport from cornerstone, and destroys the rendering engine * Removes the viewport from cornerstone, and destroys the rendering engine
*/ */

View File

@ -1,5 +1,4 @@
import React, { useEffect, useCallback, useRef, useMemo } from 'react'; import React, { useEffect, useCallback, useRef } from 'react';
import { useResizeDetector } from 'react-resize-detector';
import { Types, MeasurementService } from '@ohif/core'; import { Types, MeasurementService } from '@ohif/core';
import { ViewportGrid, ViewportPane } from '@ohif/ui-next'; import { ViewportGrid, ViewportPane } from '@ohif/ui-next';
import { useViewportGrid } from '@ohif/ui-next'; import { useViewportGrid } from '@ohif/ui-next';
@ -13,14 +12,6 @@ function ViewerViewportGrid(props: withAppTypes) {
const { layout, activeViewportId, viewports, isHangingProtocolLayout } = viewportGrid; const { layout, activeViewportId, viewports, isHangingProtocolLayout } = viewportGrid;
const { numCols, numRows } = layout; const { numCols, numRows } = layout;
const { ref: resizeRef } = useResizeDetector({
refreshMode: 'debounce',
refreshRate: 7,
refreshOptions: { leading: true },
onResize: () => {
viewportGridService.setViewportGridSizeChanged();
},
});
const layoutHash = useRef(null); const layoutHash = useRef(null);
const { const {
@ -444,10 +435,7 @@ function ViewerViewportGrid(props: withAppTypes) {
} }
return ( return (
<div <div className="border-input h-[calc(100%-0.25rem)] w-full border">
ref={resizeRef}
className="border-input h-[calc(100%-0.25rem)] w-full border"
>
<ViewportGrid <ViewportGrid
numRows={numRows} numRows={numRows}
numCols={numCols} numCols={numCols}