diff --git a/docs/documentation.md b/docs/documentation.md index d623e94f..03858bab 100644 --- a/docs/documentation.md +++ b/docs/documentation.md @@ -46,8 +46,8 @@ | customToolbar | - | Component | Override toolbar component | | toolbarProps | {} | Object | Toolbar settings | | toolbarProps.position | `right` | one of `none`, `top`, `right`, `bottom`, `left` | Toolbar position | -| toolbarProps.SVGAlignX | `left` | one of `left`, `center`, `right` | X Alignment used for "Fit to Viewer" action | -| toolbarProps.SVGAlignY | `top` | one of `top`, `center`, `bottom` | Y Alignment used for "Fit to Viewer" action | +| toolbarProps.SVGAlignX | `left` | one of `left`, `center`, `right`, `cover` | X Alignment used for "Fit to Viewer" and "Zoom in" actions | +| toolbarProps.SVGAlignY | `top` | one of `top`, `center`, `bottom`, `cover` | Y Alignment used for "Fit to Viewer" and "Zoom in" actions | | toolbarProps.activeToolColor | `#1CA6FC` | String | Color of active and hovered tool icons | \* handler available only with the tool `none` or `auto` @@ -57,8 +57,8 @@ |-----|------| | `pan(SVGDeltaX, SVGDeltaY)` | Apply a pan | | `zoom(SVGPointX, SVGPointY, scaleFactor)` | Zoom in or out the SVG | -| `fitSelection(selectionSVGPointX, selectionSVGPointY, selectionWidth, selectionHeight)`| Fit an SVG area to viewer | -| `fitToViewer(SVGAlignX = "left", SVGAlignY = "top")` | Fit all SVG to Viewer (`SVGAlignX`: one of `left`, `center`, `right`, `SVGAlignY`: one of `top`, `center`, `bottom`) | +| `fitSelection(selectionSVGPointX, selectionSVGPointY, selectionWidth, selectionHeight, SVGAlignX = "left", SVGAlignY = "top")`| Fit an SVG area to viewer (`SVGAlignX`: one of `left`, `center`, `right`, `cover`, `SVGAlignY`: one of `top`, `center`, `bottom`, `cover`) | +| `fitToViewer(SVGAlignX = "left", SVGAlignY = "top")` | Fit all SVG to Viewer (`SVGAlignX`: one of `left`, `center`, `right`, `cover`, `SVGAlignY`: one of `top`, `center`, `bottom`, `cover`) | | `setPointOnViewerCenter(SVGPointX, SVGPointY, zoomLevel)`| Set a point on Viewer center | | `reset()` | Reset Viewer view to default | | `zoomOnViewerCenter(scaleFactor)` | Zoom SVG on center | diff --git a/src/features/interactions.js b/src/features/interactions.js index c235a4ce..8b7afc8c 100644 --- a/src/features/interactions.js +++ b/src/features/interactions.js @@ -48,7 +48,7 @@ export function onMouseMove(event, ViewerDOM, tool, value, props, coords = null) switch (tool) { case TOOL_ZOOM_IN: if (value.mode === MODE_ZOOMING) - nextValue = forceExit ? stopZooming(value, x, y, props.scaleFactor, props) : updateZooming(value, x, y); + nextValue = forceExit ? stopZooming(value, x, y, props.scaleFactor, props.toolbarProps?.SVGAlignX, props.toolbarProps?.SVGAlignY) : updateZooming(value, x, y); break; case TOOL_AUTO: @@ -73,12 +73,12 @@ export function onMouseUp(event, ViewerDOM, tool, value, props, coords = null) { switch (tool) { case TOOL_ZOOM_OUT: if (value.mode === MODE_ZOOMING) - nextValue = stopZooming(value, x, y, 1 / props.scaleFactor, props); + nextValue = stopZooming(value, x, y, 1 / props.scaleFactor, props.toolbarProps?.SVGAlignX, props.toolbarProps?.SVGAlignY); break; case TOOL_ZOOM_IN: if (value.mode === MODE_ZOOMING) - nextValue = stopZooming(value, x, y, props.scaleFactor, props); + nextValue = stopZooming(value, x, y, props.scaleFactor, props.toolbarProps?.SVGAlignX, props.toolbarProps?.SVGAlignY); break; case TOOL_AUTO: diff --git a/src/features/zoom.js b/src/features/zoom.js index 7b828426..461a68dc 100644 --- a/src/features/zoom.js +++ b/src/features/zoom.js @@ -57,105 +57,69 @@ export function zoom(value, SVGPointX, SVGPointY, scaleFactor) { }, ACTION_ZOOM); } -//ENHANCEMENT: add ability to control alignment //ENHANCEMENT: add ability to selectively fit image inside viewer -//ENHANCEMENT: refactor some logic in order to merge with fitToViewer function -export function fitSelection(value, selectionSVGPointX, selectionSVGPointY, selectionWidth, selectionHeight) { +export function fitSelection(value, selectionSVGPointX, selectionSVGPointY, selectionWidth, selectionHeight, SVGAlignX=ALIGN_LEFT, SVGAlignY=ALIGN_TOP) { let {viewerWidth, viewerHeight} = value; let scaleX = viewerWidth / selectionWidth; let scaleY = viewerHeight / selectionHeight; - - let scaleLevel = Math.min(scaleX, scaleY); - - const matrix = transform( - scale(scaleLevel, scaleLevel), //2 - translate(-selectionSVGPointX, -selectionSVGPointY) //1 - ); - - if(isZoomLevelGoingOutOfBounds(value, scaleLevel / value.d)) { - // Do not allow scale and translation - return set(value, { - mode: MODE_IDLE, - startX: null, - startY: null, - endX: null, - endY: null - }); - } - - return set(value, { - mode: MODE_IDLE, - ...limitZoomLevel(value, matrix), - startX: null, - startY: null, - endX: null, - endY: null - }, ACTION_ZOOM); -} - -export function fitToViewer(value, SVGAlignX=ALIGN_LEFT, SVGAlignY=ALIGN_TOP) { - let {viewerWidth, viewerHeight, SVGMinX, SVGMinY, SVGWidth, SVGHeight} = value; - - let scaleX = viewerWidth / SVGWidth; - let scaleY = viewerHeight / SVGHeight; let scaleLevel = Math.min(scaleX, scaleY); let scaleMatrix = scale(scaleLevel, scaleLevel); - let translateX = -SVGMinX * scaleX; - let translateY = -SVGMinY * scaleY; + let translateX = -selectionSVGPointX * scaleX; + let translateY = -selectionSVGPointY * scaleY; - // after fitting, SVG and the viewer will match in width (1) or in height (2) or SVG will cover the container with preserving aspect ratio (0) - if (scaleX < scaleY) { - let remainderY = viewerHeight - scaleX * SVGHeight; + // after fitting, SVG and the viewer will match in width (1) or in height (2) or SVG will cover the container with preserving aspect ratio (0) + if (scaleX < scaleY) { + let remainderY = viewerHeight - scaleX * selectionHeight; //(1) match in width, meaning scaled SVGHeight <= viewerHeight switch(SVGAlignY) { case ALIGN_TOP: - translateY = -SVGMinY * scaleLevel; + translateY = -selectionSVGPointY * scaleLevel; break; case ALIGN_CENTER: - translateY = Math.round(remainderY / 2) - SVGMinY * scaleLevel; + translateY = Math.round(remainderY / 2) - selectionSVGPointY * scaleLevel; break; case ALIGN_BOTTOM: - translateY = remainderY - SVGMinY * scaleLevel; + translateY = remainderY - selectionSVGPointY * scaleLevel; break; case ALIGN_COVER: scaleMatrix = scale(scaleY, scaleY); // (0) we must now match to short edge, in this case - height - let remainderX = viewerWidth - scaleY * SVGWidth; // calculate remainder in the other scale + let remainderX = viewerWidth - scaleY * selectionWidth; // calculate remainder in the other scale - translateX = SVGMinX + Math.round(remainderX / 2); // center by the long edge + translateX = selectionSVGPointX + Math.round(remainderX / 2); // center by the long edge break; default: //no op } } else { - let remainderX = viewerWidth - scaleY * SVGWidth; + let remainderX = viewerWidth - scaleY * selectionWidth; //(2) match in height, meaning scaled SVGWidth <= viewerWidth switch(SVGAlignX) { case ALIGN_LEFT: - translateX = -SVGMinX * scaleLevel; + translateX = -selectionSVGPointX * scaleLevel; break; case ALIGN_CENTER: - translateX = Math.round(remainderX / 2) - SVGMinX * scaleLevel; + translateX = Math.round(remainderX / 2) - selectionSVGPointX * scaleLevel; break; case ALIGN_RIGHT: - translateX = remainderX - SVGMinX * scaleLevel; + translateX = remainderX - selectionSVGPointX * scaleLevel; break; case ALIGN_COVER: scaleMatrix = scale(scaleX, scaleX); // (0) we must now match to short edge, in this case - width - let remainderY = viewerHeight - scaleX * SVGHeight; // calculate remainder in the other scale + let remainderY = viewerHeight - scaleX * selectionHeight; // calculate remainder in the other scale - translateY = SVGMinY + Math.round(remainderY / 2); // center by the long edge + translateY = selectionSVGPointY + Math.round(remainderY / 2); // center by the long edge break; default: @@ -191,6 +155,11 @@ export function fitToViewer(value, SVGAlignX=ALIGN_LEFT, SVGAlignY=ALIGN_TOP) { }, ACTION_ZOOM); } +export function fitToViewer(value, SVGAlignX=ALIGN_LEFT, SVGAlignY=ALIGN_TOP) { + let {SVGMinX, SVGMinY, SVGWidth, SVGHeight} = value; + return fitSelection(value, SVGMinX, SVGMinY, SVGWidth, SVGHeight, SVGAlignX, SVGAlignY); +} + export function zoomOnViewerCenter(value, scaleFactor) { let {viewerWidth, viewerHeight} = value; let SVGPoint = getSVGPoint(value, viewerWidth / 2, viewerHeight / 2); @@ -216,7 +185,7 @@ export function updateZooming(value, viewerX, viewerY) { }); } -export function stopZooming(value, viewerX, viewerY, scaleFactor) { +export function stopZooming(value, viewerX, viewerY, scaleFactor, SVGAlignX, SVGAlignY) { const TOLERATED_DISTANCE = 7 //minimum distance to choose if area selection or drill down on point let {startX, startY} = value; @@ -225,7 +194,7 @@ export function stopZooming(value, viewerX, viewerY, scaleFactor) { if (Math.abs(startX - viewerX) > TOLERATED_DISTANCE && Math.abs(startY - viewerY) > TOLERATED_DISTANCE) { let box = calculateBox(start, end); - return fitSelection(value, box.x, box.y, box.width, box.height); + return fitSelection(value, box.x, box.y, box.width, box.height, SVGAlignX, SVGAlignY); } else { let SVGPoint = getSVGPoint(value, viewerX, viewerY); return zoom(value, SVGPoint.x, SVGPoint.y, scaleFactor); diff --git a/src/uncontrolled-viewer.js b/src/uncontrolled-viewer.js index 0df65b56..3f6076c1 100644 --- a/src/uncontrolled-viewer.js +++ b/src/uncontrolled-viewer.js @@ -32,11 +32,11 @@ export default class UncontrolledReactSVGPanZoom extends React.Component { this.Viewer.zoom(SVGPointX, SVGPointY, scaleFactor) } - fitSelection(selectionSVGPointX, selectionSVGPointY, selectionWidth, selectionHeight) { - this.Viewer.fitSelection(selectionSVGPointX, selectionSVGPointY, selectionWidth, selectionHeight) + fitSelection(selectionSVGPointX, selectionSVGPointY, selectionWidth, selectionHeight, SVGAlignX = ALIGN_LEFT, SVGAlignY = ALIGN_TOP) { + this.Viewer.fitSelection(selectionSVGPointX, selectionSVGPointY, selectionWidth, selectionHeight, SVGAlignX, SVGAlignY) } - fitToViewer(SVGAlignX, SVGAlignY) { + fitToViewer(SVGAlignX = ALIGN_LEFT, SVGAlignY = ALIGN_TOP) { this.Viewer.fitToViewer(SVGAlignX, SVGAlignY) } diff --git a/src/viewer.js b/src/viewer.js index 921f39b6..6eb90a4d 100644 --- a/src/viewer.js +++ b/src/viewer.js @@ -186,8 +186,8 @@ export default class ReactSVGPanZoom extends React.Component { this.setValue(nextValue); } - fitSelection(selectionSVGPointX, selectionSVGPointY, selectionWidth, selectionHeight) { - let nextValue = fitSelection(this.getValue(), selectionSVGPointX, selectionSVGPointY, selectionWidth, selectionHeight); + fitSelection(selectionSVGPointX, selectionSVGPointY, selectionWidth, selectionHeight, SVGAlignX = ALIGN_LEFT, SVGAlignY = ALIGN_TOP) { + let nextValue = fitSelection(this.getValue(), selectionSVGPointX, selectionSVGPointY, selectionWidth, selectionHeight, SVGAlignX, SVGAlignY); this.setValue(nextValue); }