added map dragging

This commit is contained in:
2024-10-25 18:48:04 +02:00
parent 2bd6e392d4
commit ec4d0e3db2
+125 -48
View File
@@ -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,50 +130,52 @@ 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">
<image href={imageUrl} width="100%" /> <g ref={gRef} transform={`translate(${translate.x}, ${translate.y}) scale(${scale})`}>
{regions.map((region, index) => ( <image href={imageUrl} width="100%" />
<polygon {regions.map((region, index) => (
key={index} <polygon
className="region" key={index}
points={region.points} className="region"
fill="transparent" points={region.points}
stroke="transparent" fill="transparent"
strokeWidth={4} stroke="transparent"
onMouseEnter={(e) => handleMouseEnterRegion(e, region)} strokeWidth={4}
onMouseMove={handleMouseMove} onMouseEnter={(e) => handleMouseEnterRegion(e, region)}
onMouseLeave={handleMouseLeave} onMouseMove={handleMouseMove}
/> onMouseLeave={handleMouseLeave}
))} />
{capitals.map((location, index) => ( ))}
<Capital {capitals.map((location, index) => (
key={index} <Capital
city={location} key={index}
onMouseEnter={(e) => handleMouseEnterLocation(e, location)} city={location}
onMouseMove={handleMouseMove} onMouseEnter={(e) => handleMouseEnterLocation(e, location)}
onMouseLeave={handleMouseLeave} onMouseMove={handleMouseMove}
/> onMouseLeave={handleMouseLeave}
))} />
{cities.map((location, index) => ( ))}
<City {cities.map((location, index) => (
key={index} <City
city={location} key={index}
onMouseEnter={(e) => handleMouseEnterLocation(e, location)} city={location}
onMouseMove={handleMouseMove} onMouseEnter={(e) => handleMouseEnterLocation(e, location)}
onMouseLeave={handleMouseLeave} onMouseMove={handleMouseMove}
/> onMouseLeave={handleMouseLeave}
))} />
{pois.map((location, index) => ( ))}
<Poi {pois.map((location, index) => (
key={index} <Poi
poi={location} key={index}
onMouseEnter={(e) => handleMouseEnterLocation(e, location)} poi={location}
onMouseMove={handleMouseMove} onMouseEnter={(e) => handleMouseEnterLocation(e, location)}
onMouseLeave={handleMouseLeave} onMouseMove={handleMouseMove}
/> 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;