added map dragging
This commit is contained in:
@@ -1,9 +1,9 @@
|
|||||||
import {MapLocation} from "../../types/MapLocation.ts";
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
import {MapRegion} from "../../types/MapRegion.ts";
|
import { MapLocation } from "../../types/MapLocation.ts";
|
||||||
|
import { MapRegion } from "../../types/MapRegion.ts";
|
||||||
import Capital from "./locations/Capital.tsx";
|
import Capital from "./locations/Capital.tsx";
|
||||||
import City from "./locations/City.tsx";
|
import City from "./locations/City.tsx";
|
||||||
import Poi from "./locations/Poi.tsx";
|
import Poi from "./locations/Poi.tsx";
|
||||||
import {useState} from "react";
|
|
||||||
import MapLocationTooltip from "./tooltips/MapLocationTooltip.tsx";
|
import MapLocationTooltip from "./tooltips/MapLocationTooltip.tsx";
|
||||||
import MapRegionTooltip from "./tooltips/MapRegionTooltip.tsx";
|
import MapRegionTooltip from "./tooltips/MapRegionTooltip.tsx";
|
||||||
|
|
||||||
@@ -15,7 +15,9 @@ interface MapProps {
|
|||||||
pois?: MapLocation[];
|
pois?: MapLocation[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const Map: React.FC<MapProps> = ({imageUrl, regions = [], cities = [], pois = [], capitals = []}) => {
|
const Map: React.FC<MapProps> = ({ imageUrl, regions = [], cities = [], pois = [], capitals = [] }) => {
|
||||||
|
const svgRef = useRef<SVGSVGElement | null>(null);
|
||||||
|
const gRef = useRef<SVGGElement | null>(null);
|
||||||
const [tooltip, setTooltip] = useState<{ visible: boolean; x: number; y: number; content: JSX.Element | null }>({
|
const [tooltip, setTooltip] = useState<{ visible: boolean; x: number; y: number; content: JSX.Element | null }>({
|
||||||
visible: false,
|
visible: false,
|
||||||
x: 0,
|
x: 0,
|
||||||
@@ -23,6 +25,79 @@ const Map: React.FC<MapProps> = ({imageUrl, regions = [], cities = [], pois = []
|
|||||||
content: null,
|
content: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
|
const [startX, setStartX] = useState(0);
|
||||||
|
const [startY, setStartY] = useState(0);
|
||||||
|
const [translate, setTranslate] = useState({ x: 0, y: 0 });
|
||||||
|
const [scale, setScale] = useState(1);
|
||||||
|
|
||||||
|
const dragSensitivity = 2;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const svgElement = svgRef.current;
|
||||||
|
const gElement = gRef.current;
|
||||||
|
|
||||||
|
if (svgElement && gElement) {
|
||||||
|
const handleMouseDown = (e: MouseEvent) => {
|
||||||
|
setIsDragging(true);
|
||||||
|
setStartX(e.clientX);
|
||||||
|
setStartY(e.clientY);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseMove = (e: MouseEvent) => {
|
||||||
|
if (!isDragging) return;
|
||||||
|
|
||||||
|
// Increase deltaX and deltaY by the drag sensitivity factor
|
||||||
|
const deltaX = (e.clientX - startX) * dragSensitivity;
|
||||||
|
const deltaY = (e.clientY - startY) * dragSensitivity;
|
||||||
|
setTranslate(prev => ({
|
||||||
|
x: prev.x + deltaX,
|
||||||
|
y: prev.y + deltaY,
|
||||||
|
}));
|
||||||
|
setStartX(e.clientX);
|
||||||
|
setStartY(e.clientY);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseUp = () => setIsDragging(false);
|
||||||
|
|
||||||
|
const handleWheel = (e: WheelEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const zoomFactor = e.deltaY > 0 ? 0.9 : 1.1;
|
||||||
|
const newScale = Math.min(Math.max(scale * zoomFactor, 1), 6);
|
||||||
|
|
||||||
|
// If zooming out to the minimum scale, reset translation
|
||||||
|
if (newScale === 1) {
|
||||||
|
setTranslate({ x: 0, y: 0 });
|
||||||
|
} else {
|
||||||
|
const svgPoint = svgElement.createSVGPoint();
|
||||||
|
svgPoint.x = e.clientX;
|
||||||
|
svgPoint.y = e.clientY;
|
||||||
|
const mousePoint = svgPoint.matrixTransform(svgElement.getScreenCTM()?.inverse());
|
||||||
|
|
||||||
|
const newTranslateX = translate.x - (mousePoint.x - translate.x) * (newScale / scale - 1);
|
||||||
|
const newTranslateY = translate.y - (mousePoint.y - translate.y) * (newScale / scale - 1);
|
||||||
|
setTranslate({ x: newTranslateX, y: newTranslateY });
|
||||||
|
}
|
||||||
|
setScale(newScale);
|
||||||
|
};
|
||||||
|
|
||||||
|
svgElement.addEventListener("mousedown", handleMouseDown);
|
||||||
|
svgElement.addEventListener("mousemove", handleMouseMove);
|
||||||
|
svgElement.addEventListener("mouseup", handleMouseUp);
|
||||||
|
svgElement.addEventListener("mouseleave", handleMouseUp);
|
||||||
|
svgElement.addEventListener("wheel", handleWheel);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
svgElement.removeEventListener("mousedown", handleMouseDown);
|
||||||
|
svgElement.removeEventListener("mousemove", handleMouseMove);
|
||||||
|
svgElement.removeEventListener("mouseup", handleMouseUp);
|
||||||
|
svgElement.removeEventListener("mouseleave", handleMouseUp);
|
||||||
|
svgElement.removeEventListener("wheel", handleWheel);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [isDragging, startX, startY, scale]);
|
||||||
|
|
||||||
|
// Tooltip handling remains unchanged
|
||||||
const handleMouseEnterRegion = (event: React.MouseEvent<SVGPolygonElement>, region: MapRegion) => {
|
const handleMouseEnterRegion = (event: React.MouseEvent<SVGPolygonElement>, region: MapRegion) => {
|
||||||
setTooltip({
|
setTooltip({
|
||||||
visible: true,
|
visible: true,
|
||||||
@@ -55,7 +130,8 @@ const Map: React.FC<MapProps> = ({imageUrl, regions = [], cities = [], pois = []
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<svg id="map" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2641 2035">
|
<svg ref={svgRef} id="map" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2641 2035">
|
||||||
|
<g ref={gRef} transform={`translate(${translate.x}, ${translate.y}) scale(${scale})`}>
|
||||||
<image href={imageUrl} width="100%" />
|
<image href={imageUrl} width="100%" />
|
||||||
{regions.map((region, index) => (
|
{regions.map((region, index) => (
|
||||||
<polygon
|
<polygon
|
||||||
@@ -97,8 +173,9 @@ const Map: React.FC<MapProps> = ({imageUrl, regions = [], cities = [], pois = []
|
|||||||
onMouseLeave={handleMouseLeave}
|
onMouseLeave={handleMouseLeave}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
{tooltip.visible && (
|
{tooltip.visible && !isDragging && (
|
||||||
<div
|
<div
|
||||||
id={"tooltip"}
|
id={"tooltip"}
|
||||||
className={"tooltip"}
|
className={"tooltip"}
|
||||||
@@ -113,6 +190,6 @@ const Map: React.FC<MapProps> = ({imageUrl, regions = [], cities = [], pois = []
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
export default Map;
|
export default Map;
|
||||||
Reference in New Issue
Block a user