From 8e1a0c9b087986dba03ffe5a6f364325365cf104 Mon Sep 17 00:00:00 2001 From: Chris Beer Date: Thu, 27 Feb 2025 14:30:30 -0800 Subject: [PATCH 1/2] Pass the world bounds to OpenSeadragonComponent. --- src/components/OpenSeadragonComponent.js | 18 +++++++++++------- src/components/OpenSeadragonViewer.js | 5 +++-- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/components/OpenSeadragonComponent.js b/src/components/OpenSeadragonComponent.js index d8eedca0bf..38d2f216f0 100644 --- a/src/components/OpenSeadragonComponent.js +++ b/src/components/OpenSeadragonComponent.js @@ -10,7 +10,7 @@ import OpenSeadragonViewerContext from '../contexts/OpenSeadragonViewerContext'; /** Handle setting up OSD for use in mirador + react */ function OpenSeadragonComponent({ - children = undefined, Container = 'div', osdConfig = {}, viewerConfig = {}, onUpdateViewport = () => {}, setViewer = () => {}, style = {}, ...passThruProps + children = undefined, Container = 'div', osdConfig = {}, viewerConfig = {}, worldBounds = undefined, onUpdateViewport = () => {}, setViewer = () => {}, style = {}, ...passThruProps }) { const id = useId(); const ref = useRef(); @@ -59,14 +59,16 @@ function OpenSeadragonComponent({ viewport.setFlip(viewerConfig.flip); } + const bounds = viewerConfig.bounds || worldBounds; + if (!viewerConfig.x && !viewerConfig.y && !viewerConfig.zoom) { - if (viewerConfig.bounds) { - viewport.fitBounds(new Openseadragon.Rect(...viewerConfig.bounds), true); + if (bounds) { + viewport.fitBounds(new Openseadragon.Rect(...bounds), true); } else { viewport.goHome(true); } } - }, [initialViewportSet, viewerConfig]); + }, [initialViewportSet, viewerConfig, worldBounds]); useEffect(() => { const viewer = viewerRef.current; @@ -100,13 +102,14 @@ function OpenSeadragonComponent({ viewport.setFlip(viewerConfig.flip); } - if (viewerConfig.bounds && !viewerConfig.x && !viewerConfig.y && !viewerConfig.zoom) { - const rect = new Openseadragon.Rect(...viewerConfig.bounds); + const bounds = viewerConfig.bounds || worldBounds; + if (bounds && !viewerConfig.x && !viewerConfig.y && !viewerConfig.zoom) { + const rect = new Openseadragon.Rect(...bounds); if (rect.equals(viewport.getBounds())) { viewport.fitBounds(rect, false); } } - }, [initialViewportSet, setInitialBounds, viewerConfig, viewerRef]); + }, [initialViewportSet, setInitialBounds, viewerConfig, viewerRef, worldBounds]); // initialize OSD stuff when this component is mounted useEffect(() => { @@ -200,6 +203,7 @@ OpenSeadragonComponent.propTypes = { y: PropTypes.number, zoom: PropTypes.number, }), + worldBounds: PropTypes.arrayOf(PropTypes.number), }; export default OpenSeadragonComponent; diff --git a/src/components/OpenSeadragonViewer.js b/src/components/OpenSeadragonViewer.js index 0e7d7fd3d1..9695649bf1 100644 --- a/src/components/OpenSeadragonViewer.js +++ b/src/components/OpenSeadragonViewer.js @@ -25,7 +25,7 @@ const StyledSection = styled('section')({ * and rendering OSD. */ export function OpenSeadragonViewer({ - children = null, label = null, windowId, osdConfig = {}, viewerConfig = null, + children = null, label = null, windowId, osdConfig = {}, viewerConfig = undefined, drawAnnotations = false, infoResponses = [], canvasWorld, nonTiledImages = [], updateViewport, ...rest }) { @@ -89,7 +89,8 @@ export function OpenSeadragonViewer({ className={classNames(ns('osd-container'))} Container={StyledSection} osdConfig={osdConfig} - viewerConfig={viewerConfig || (canvasWorld.hasDimensions() ? { bounds: canvasWorld.worldBounds() } : undefined)} + viewerConfig={viewerConfig} + worldBounds={(canvasWorld.hasDimensions() ? canvasWorld.worldBounds() : undefined)} onUpdateViewport={onViewportChange} setViewer={setViewer} aria-label={t('item', { label })} From fc3ce9aad939c264ce7dffe3947e304372523f5a Mon Sep 17 00:00:00 2001 From: Chris Beer Date: Thu, 27 Feb 2025 14:47:55 -0800 Subject: [PATCH 2/2] Only preserve the viewport if the preserved viewport 'makes sense' --- .../mirador/mirador-configs/index.js | 8 +---- src/components/OpenSeadragonComponent.js | 33 ++++++++++++++++++- src/components/OpenSeadragonViewer.js | 3 +- src/config/settings.js | 1 + 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/__tests__/integration/mirador/mirador-configs/index.js b/__tests__/integration/mirador/mirador-configs/index.js index db4e6f90f0..e1f0e3d3ad 100644 --- a/__tests__/integration/mirador/mirador-configs/index.js +++ b/__tests__/integration/mirador/mirador-configs/index.js @@ -20,12 +20,6 @@ export default { transitions: {}, }, windows: [{ - canvasId: 'https://iiif.harvardartmuseums.org/manifests/object/299843/canvas/canvas-47174892', - manifestId: 'https://iiif.harvardartmuseums.org/manifests/object/299843', - }, - { - canvasId: 'https://iiif.bodleian.ox.ac.uk/iiif/canvas/e58b8c60-005c-4c41-a22f-07d49cb25ede.json', - manifestId: 'https://iiif.bodleian.ox.ac.uk/iiif/manifest/e32a277e-91e2-4a6d-8ba6-cc4bad230410.json', - thumbnailNavigationPosition: 'far-bottom', + manifestId: 'https://wellcomelibrary.org/iiif/b18035723/manifest', }], }; diff --git a/src/components/OpenSeadragonComponent.js b/src/components/OpenSeadragonComponent.js index 38d2f216f0..6f81d85be6 100644 --- a/src/components/OpenSeadragonComponent.js +++ b/src/components/OpenSeadragonComponent.js @@ -33,6 +33,13 @@ function OpenSeadragonComponent({ bounds: viewport.getBounds(), flip: viewport.getFlip(), rotation: viewport.getRotation(), + worldBounds: (() => { + const homeBounds = viewport.viewer?.world?.getHomeBounds(); + + if (!homeBounds) return undefined; + + return [homeBounds.x, homeBounds.y, homeBounds.width, homeBounds.height]; + })(), x: Math.round(viewport.centerSpringX.target.value), y: Math.round(viewport.centerSpringY.target.value), zoom: viewport.zoomSpring.target.value, @@ -105,17 +112,41 @@ function OpenSeadragonComponent({ const bounds = viewerConfig.bounds || worldBounds; if (bounds && !viewerConfig.x && !viewerConfig.y && !viewerConfig.zoom) { const rect = new Openseadragon.Rect(...bounds); - if (rect.equals(viewport.getBounds())) { + if (!rect.equals(viewport.getBounds())) { viewport.fitBounds(rect, false); } } }, [initialViewportSet, setInitialBounds, viewerConfig, viewerRef, worldBounds]); + useEffect(() => { + if (!osdConfig.preserveViewport) return; + if (!viewerConfig?.worldBounds || !worldBounds) return; + + const viewer = viewerRef.current; + if (!viewer) return; + const { viewport } = viewer; + + const [_x, _y, width, height] = viewerConfig.worldBounds; + const [_x1, _y1, width1, height1] = worldBounds; + + const previousAspectRatio = (1.0 * width) / height; + const newAspectRatio = (1.0 * width1) / height1; + + if ((previousAspectRatio < (1 - osdConfig.resetViewportAfterAspectRatioDelta) * newAspectRatio) + || (previousAspectRatio > (1 + osdConfig.resetViewportAfterAspectRatioDelta) * newAspectRatio)) { + const rect = new Openseadragon.Rect(...worldBounds); + if (!rect.equals(viewport.getBounds())) { + viewport.fitBounds(rect, false); + } + } + }, [osdConfig, viewerConfig, worldBounds, viewerRef]); + // initialize OSD stuff when this component is mounted useEffect(() => { const viewer = Openseadragon({ element: ref.current, ...osdConfig, + preserveViewportAspectRatio: undefined, }); viewer.addHandler('canvas-drag', () => { diff --git a/src/components/OpenSeadragonViewer.js b/src/components/OpenSeadragonViewer.js index 9695649bf1..1ce85d7d43 100644 --- a/src/components/OpenSeadragonViewer.js +++ b/src/components/OpenSeadragonViewer.js @@ -33,11 +33,12 @@ export function OpenSeadragonViewer({ const apiRef = useRef(); const [viewer, setViewer] = useState(null); const onViewportChange = useCallback(({ - flip, rotation, x, y, zoom, + flip, rotation, worldBounds, x, y, zoom, }) => { updateViewport(windowId, { flip, rotation, + worldBounds, x, y, zoom, diff --git a/src/config/settings.js b/src/config/settings.js index c40dac40ff..9b8ca5a71c 100644 --- a/src/config/settings.js +++ b/src/config/settings.js @@ -545,6 +545,7 @@ export default { blendTime: 0.1, preserveImageSizeOnResize: true, preserveViewport: true, + resetViewportAfterAspectRatioDelta: 0.25, showNavigationControl: false, zoomPerClick: 1, // disable zoom-to-click zoomPerDoubleClick: 2.0