Added map rotation
+Added an option to rotate the map using ALT + mouse drag
This commit is contained in:
parent
68c4a4bfa8
commit
8db877c2e3
@ -8,7 +8,7 @@ export default function ZoomPanWrapper({children}) {
|
|||||||
const svgRef = useRef(null);
|
const svgRef = useRef(null);
|
||||||
const cameraWrapperRef = useRef(null);
|
const cameraWrapperRef = useRef(null);
|
||||||
const viewBoxRef = useRef({ x: -50, y: -50, w: 100, h: 100 });
|
const viewBoxRef = useRef({ x: -50, y: -50, w: 100, h: 100 });
|
||||||
const cameraRef = useRef({ x: 0, y: 0, zoom: 1 });
|
const cameraRef = useRef({ x: 0, y: 0, zoom: 1, rotation: 0 });
|
||||||
const clientRectRef = useRef(null);
|
const clientRectRef = useRef(null);
|
||||||
const rafScheduledRef = useRef(false);
|
const rafScheduledRef = useRef(false);
|
||||||
const isMouseDownRef = useRef(false);
|
const isMouseDownRef = useRef(false);
|
||||||
@ -25,9 +25,9 @@ export default function ZoomPanWrapper({children}) {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const getCameraTransformString = () => {
|
const getCameraTransformString = () => {
|
||||||
const { x, y, zoom } = cameraRef.current;
|
const { x, y, zoom, rotation } = cameraRef.current;
|
||||||
|
|
||||||
return `scale(${zoom}) translate(${-x},${-y})`;
|
return `scale(${zoom}) rotate(${rotation}) translate(${-x},${-y})`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const scheduleCameraUpdate = useCallback(() => {
|
const scheduleCameraUpdate = useCallback(() => {
|
||||||
@ -49,6 +49,27 @@ export default function ZoomPanWrapper({children}) {
|
|||||||
clientRect$.next(rect);
|
clientRect$.next(rect);
|
||||||
clientRectRef.current = rect;
|
clientRectRef.current = rect;
|
||||||
}, [updateViewBox]);
|
}, [updateViewBox]);
|
||||||
|
|
||||||
|
const handleRotation = (e) => {
|
||||||
|
const deltaAngle = e.movementX * Constants.map.rotationSensitivity;
|
||||||
|
if(deltaAngle === 0) return;
|
||||||
|
|
||||||
|
cameraRef.current.rotation = cameraRef.current.rotation + deltaAngle
|
||||||
|
|
||||||
|
scheduleCameraUpdate();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMove = (e) => {
|
||||||
|
const cx = e.movementX / cameraRef.current.zoom;
|
||||||
|
const cy = e.movementY / cameraRef.current.zoom;
|
||||||
|
const cos = Math.cos(cameraRef.current.rotation * Math.PI / 180);
|
||||||
|
const sin = Math.sin(cameraRef.current.rotation * Math.PI / 180);
|
||||||
|
|
||||||
|
cameraRef.current.x -= cx * cos + cy * sin;
|
||||||
|
cameraRef.current.y -= cy * cos - cx * sin;
|
||||||
|
|
||||||
|
scheduleCameraUpdate();
|
||||||
|
};
|
||||||
|
|
||||||
// update viewbox when the window resizes
|
// update viewbox when the window resizes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -75,6 +96,7 @@ export default function ZoomPanWrapper({children}) {
|
|||||||
const { w, h } = viewBoxRef.current;
|
const { w, h } = viewBoxRef.current;
|
||||||
|
|
||||||
const newZoom = cameraRef.current.zoom * (1.0 - (e.deltaY * Constants.map.zoomSensitivity));
|
const newZoom = cameraRef.current.zoom * (1.0 - (e.deltaY * Constants.map.zoomSensitivity));
|
||||||
|
cameraRef.current.zoom = newZoom;
|
||||||
|
|
||||||
// screen space cursor pos
|
// screen space cursor pos
|
||||||
const cx = e.clientX - left;
|
const cx = e.clientX - left;
|
||||||
@ -92,9 +114,11 @@ export default function ZoomPanWrapper({children}) {
|
|||||||
const offX = (ncx - cx) / newZoom;
|
const offX = (ncx - cx) / newZoom;
|
||||||
const offY = (ncy - cy) / newZoom;
|
const offY = (ncy - cy) / newZoom;
|
||||||
|
|
||||||
cameraRef.current.x += offX;
|
// update camera position so the cursor stays in the same place
|
||||||
cameraRef.current.y += offY;
|
const cos = Math.cos(cameraRef.current.rotation * Math.PI / 180);
|
||||||
cameraRef.current.zoom = newZoom;
|
const sin = Math.sin(cameraRef.current.rotation * Math.PI / 180);
|
||||||
|
cameraRef.current.x += offX * cos + offY * sin;
|
||||||
|
cameraRef.current.y += offY * cos - offX * sin;
|
||||||
|
|
||||||
scheduleCameraUpdate();
|
scheduleCameraUpdate();
|
||||||
};
|
};
|
||||||
@ -108,10 +132,11 @@ export default function ZoomPanWrapper({children}) {
|
|||||||
if (!isMouseDownRef.current) return;
|
if (!isMouseDownRef.current) return;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
cameraRef.current.x -= e.movementX / cameraRef.current.zoom;
|
if(e.altKey) {
|
||||||
cameraRef.current.y -= e.movementY / cameraRef.current.zoom;
|
handleRotation(e);
|
||||||
|
} else {
|
||||||
scheduleCameraUpdate();
|
handleMove(e);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const onMouseUp = (e) => {
|
const onMouseUp = (e) => {
|
||||||
|
|||||||
@ -2,16 +2,26 @@ import { getCurrentCamera, getCurrentClientRect, getCurrentViewBox } from "../..
|
|||||||
|
|
||||||
export function getSVGCoords(event) {
|
export function getSVGCoords(event) {
|
||||||
const { left, top } = getCurrentClientRect();
|
const { left, top } = getCurrentClientRect();
|
||||||
const { x, y, zoom } = getCurrentCamera();
|
const { x, y, zoom, rotation } = getCurrentCamera();
|
||||||
const { w, h } = getCurrentViewBox();
|
const { w, h } = getCurrentViewBox();
|
||||||
|
|
||||||
// screen space cursor pos
|
// screen space cursor pos
|
||||||
const cx = event.clientX - left;
|
const cx = event.clientX - left;
|
||||||
const cy = event.clientY - top;
|
const cy = event.clientY - top;
|
||||||
|
|
||||||
|
// svg space cursor offset from camera position
|
||||||
|
const dx = -(w / 2 - cx) / zoom;
|
||||||
|
const dy = -(h / 2 - cy) / zoom;
|
||||||
|
|
||||||
|
// apply camera rotation
|
||||||
|
const cos = Math.cos(rotation * Math.PI / 180);
|
||||||
|
const sin = Math.sin(rotation * Math.PI / 180);
|
||||||
|
const rdx = dx * cos + dy * sin;
|
||||||
|
const rdy = dy * cos - dx * sin;
|
||||||
|
|
||||||
// svg space cursor pos
|
// svg space cursor pos
|
||||||
const scx = x - (w / 2 - cx) / zoom;
|
const scx = x + rdx;
|
||||||
const scy = y - (h / 2 - cy) / zoom;
|
const scy = y + rdy;
|
||||||
|
|
||||||
return [scx, scy];
|
return [scx, scy];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ const Constants = {
|
|||||||
buildVersion: '1.2.1',
|
buildVersion: '1.2.1',
|
||||||
map: {
|
map: {
|
||||||
zoomSensitivity: 0.002,
|
zoomSensitivity: 0.002,
|
||||||
|
rotationSensitivity: 0.1,
|
||||||
forcePointerEvents: false
|
forcePointerEvents: false
|
||||||
},
|
},
|
||||||
parser: {
|
parser: {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user