fix(minimap): use translateExtent on pan
This commit is contained in:
@@ -5,13 +5,12 @@ import type { D3ZoomEvent } from 'd3-zoom';
|
|||||||
import { select, pointer } from 'd3-selection';
|
import { select, pointer } from 'd3-selection';
|
||||||
import shallow from 'zustand/shallow';
|
import shallow from 'zustand/shallow';
|
||||||
|
|
||||||
import { clamp } from '../../utils';
|
|
||||||
import useKeyPress from '../../hooks/useKeyPress';
|
import useKeyPress from '../../hooks/useKeyPress';
|
||||||
import useResizeHandler from '../../hooks/useResizeHandler';
|
import useResizeHandler from '../../hooks/useResizeHandler';
|
||||||
import { useStore, useStoreApi } from '../../hooks/useStore';
|
import { useStore, useStoreApi } from '../../hooks/useStore';
|
||||||
import { containerStyle } from '../../styles';
|
import { containerStyle } from '../../styles';
|
||||||
import type { FlowRendererProps } from '../FlowRenderer';
|
import type { FlowRendererProps } from '../FlowRenderer';
|
||||||
import { PanOnScrollMode } from '../../types';
|
import { CoordinateExtent, PanOnScrollMode } from '../../types';
|
||||||
import type { Viewport, ReactFlowState } from '../../types';
|
import type { Viewport, ReactFlowState } from '../../types';
|
||||||
|
|
||||||
type ZoomPaneProps = Omit<
|
type ZoomPaneProps = Omit<
|
||||||
@@ -71,22 +70,24 @@ const ZoomPane = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (zoomPane.current) {
|
if (zoomPane.current) {
|
||||||
|
const bbox = zoomPane.current.getBoundingClientRect();
|
||||||
const d3ZoomInstance = zoom().scaleExtent([minZoom, maxZoom]).translateExtent(translateExtent);
|
const d3ZoomInstance = zoom().scaleExtent([minZoom, maxZoom]).translateExtent(translateExtent);
|
||||||
const selection = select(zoomPane.current as Element).call(d3ZoomInstance);
|
const selection = select(zoomPane.current as Element).call(d3ZoomInstance);
|
||||||
|
const updatedTransform = zoomIdentity.translate(defaultViewport.x, defaultViewport.y).scale(defaultViewport.zoom);
|
||||||
|
const extent: CoordinateExtent = [
|
||||||
|
[0, 0],
|
||||||
|
[bbox.width, bbox.height],
|
||||||
|
];
|
||||||
|
|
||||||
const clampedX = clamp(defaultViewport.x, translateExtent[0][0], translateExtent[1][0]);
|
const constrainedTransform = d3ZoomInstance.constrain()(updatedTransform, extent, translateExtent);
|
||||||
const clampedY = clamp(defaultViewport.y, translateExtent[0][1], translateExtent[1][1]);
|
d3ZoomInstance.transform(selection, constrainedTransform);
|
||||||
const clampedZoom = clamp(defaultViewport.zoom, minZoom, maxZoom);
|
|
||||||
const updatedTransform = zoomIdentity.translate(clampedX, clampedY).scale(clampedZoom);
|
|
||||||
|
|
||||||
d3ZoomInstance.transform(selection, updatedTransform);
|
|
||||||
|
|
||||||
store.setState({
|
store.setState({
|
||||||
d3Zoom: d3ZoomInstance,
|
d3Zoom: d3ZoomInstance,
|
||||||
d3Selection: selection,
|
d3Selection: selection,
|
||||||
d3ZoomHandler: selection.on('wheel.zoom'),
|
d3ZoomHandler: selection.on('wheel.zoom'),
|
||||||
// we need to pass transform because zoom handler is not registered when we set the initial transform
|
// we need to pass transform because zoom handler is not registered when we set the initial transform
|
||||||
transform: [clampedX, clampedY, clampedZoom],
|
transform: [constrainedTransform.x, constrainedTransform.y, constrainedTransform.k],
|
||||||
domNode: zoomPane.current.closest('.react-flow') as HTMLDivElement,
|
domNode: zoomPane.current.closest('.react-flow') as HTMLDivElement,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -156,7 +157,6 @@ const ZoomPane = ({
|
|||||||
} else if (!selectionKeyPressed) {
|
} else if (!selectionKeyPressed) {
|
||||||
d3Zoom.on('zoom', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
d3Zoom.on('zoom', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||||
const { onViewportChange } = store.getState();
|
const { onViewportChange } = store.getState();
|
||||||
|
|
||||||
store.setState({ transform: [event.transform.x, event.transform.y, event.transform.k] });
|
store.setState({ transform: [event.transform.x, event.transform.y, event.transform.k] });
|
||||||
|
|
||||||
if (onMove || onViewportChange) {
|
if (onMove || onViewportChange) {
|
||||||
|
|||||||
@@ -209,8 +209,7 @@ const createRFStore = () =>
|
|||||||
set({ maxZoom });
|
set({ maxZoom });
|
||||||
},
|
},
|
||||||
setTranslateExtent: (translateExtent: CoordinateExtent) => {
|
setTranslateExtent: (translateExtent: CoordinateExtent) => {
|
||||||
const { d3Zoom } = get();
|
get().d3Zoom?.translateExtent(translateExtent);
|
||||||
d3Zoom?.translateExtent(translateExtent);
|
|
||||||
|
|
||||||
set({ translateExtent });
|
set({ translateExtent });
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
getBoundsOfRects,
|
getBoundsOfRects,
|
||||||
useStoreApi,
|
useStoreApi,
|
||||||
getNodePositionWithOrigin,
|
getNodePositionWithOrigin,
|
||||||
|
CoordinateExtent,
|
||||||
} from '@reactflow/core';
|
} from '@reactflow/core';
|
||||||
import type { ReactFlowState, Rect } from '@reactflow/core';
|
import type { ReactFlowState, Rect } from '@reactflow/core';
|
||||||
|
|
||||||
@@ -109,7 +110,7 @@ function MiniMap({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const panHandler = (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
const panHandler = (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||||
const { transform, d3Selection, d3Zoom } = store.getState();
|
const { transform, d3Selection, d3Zoom, translateExtent, width, height } = store.getState();
|
||||||
|
|
||||||
if (event.sourceEvent.type !== 'mousemove' || !d3Selection || !d3Zoom) {
|
if (event.sourceEvent.type !== 'mousemove' || !d3Selection || !d3Zoom) {
|
||||||
return;
|
return;
|
||||||
@@ -120,10 +121,15 @@ function MiniMap({
|
|||||||
x: transform[0] - event.sourceEvent.movementX * viewScaleRef.current * Math.max(1, transform[2]),
|
x: transform[0] - event.sourceEvent.movementX * viewScaleRef.current * Math.max(1, transform[2]),
|
||||||
y: transform[1] - event.sourceEvent.movementY * viewScaleRef.current * Math.max(1, transform[2]),
|
y: transform[1] - event.sourceEvent.movementY * viewScaleRef.current * Math.max(1, transform[2]),
|
||||||
};
|
};
|
||||||
|
const extent: CoordinateExtent = [
|
||||||
|
[0, 0],
|
||||||
|
[width, height],
|
||||||
|
];
|
||||||
|
|
||||||
const nextTransform = zoomIdentity.translate(position.x, position.y).scale(transform[2]);
|
const nextTransform = zoomIdentity.translate(position.x, position.y).scale(transform[2]);
|
||||||
|
const constrainedTransform = d3Zoom.constrain()(nextTransform, extent, translateExtent);
|
||||||
|
|
||||||
d3Zoom.transform(d3Selection, nextTransform);
|
d3Zoom.transform(d3Selection, constrainedTransform);
|
||||||
};
|
};
|
||||||
|
|
||||||
const zoomAndPanHandler = zoom()
|
const zoomAndPanHandler = zoom()
|
||||||
|
|||||||
Reference in New Issue
Block a user