added map dragging
This commit is contained in:
+125
-48
@@ -1,9 +1,9 @@
|
||||
import {MapLocation} from "../../types/MapLocation.ts";
|
||||
import {MapRegion} from "../../types/MapRegion.ts";
|
||||
import React, { useEffect, useRef, useState } from "react";
|
||||
import { MapLocation } from "../../types/MapLocation.ts";
|
||||
import { MapRegion } from "../../types/MapRegion.ts";
|
||||
import Capital from "./locations/Capital.tsx";
|
||||
import City from "./locations/City.tsx";
|
||||
import Poi from "./locations/Poi.tsx";
|
||||
import {useState} from "react";
|
||||
import MapLocationTooltip from "./tooltips/MapLocationTooltip.tsx";
|
||||
import MapRegionTooltip from "./tooltips/MapRegionTooltip.tsx";
|
||||
|
||||
@@ -15,7 +15,9 @@ interface MapProps {
|
||||
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 }>({
|
||||
visible: false,
|
||||
x: 0,
|
||||
@@ -23,6 +25,79 @@ const Map: React.FC<MapProps> = ({imageUrl, regions = [], cities = [], pois = []
|
||||
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) => {
|
||||
setTooltip({
|
||||
visible: true,
|
||||
@@ -55,50 +130,52 @@ const Map: React.FC<MapProps> = ({imageUrl, regions = [], cities = [], pois = []
|
||||
|
||||
return (
|
||||
<>
|
||||
<svg id="map" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2641 2035">
|
||||
<image href={imageUrl} width="100%" />
|
||||
{regions.map((region, index) => (
|
||||
<polygon
|
||||
key={index}
|
||||
className="region"
|
||||
points={region.points}
|
||||
fill="transparent"
|
||||
stroke="transparent"
|
||||
strokeWidth={4}
|
||||
onMouseEnter={(e) => handleMouseEnterRegion(e, region)}
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
/>
|
||||
))}
|
||||
{capitals.map((location, index) => (
|
||||
<Capital
|
||||
key={index}
|
||||
city={location}
|
||||
onMouseEnter={(e) => handleMouseEnterLocation(e, location)}
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
/>
|
||||
))}
|
||||
{cities.map((location, index) => (
|
||||
<City
|
||||
key={index}
|
||||
city={location}
|
||||
onMouseEnter={(e) => handleMouseEnterLocation(e, location)}
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
/>
|
||||
))}
|
||||
{pois.map((location, index) => (
|
||||
<Poi
|
||||
key={index}
|
||||
poi={location}
|
||||
onMouseEnter={(e) => handleMouseEnterLocation(e, location)}
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
/>
|
||||
))}
|
||||
<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%" />
|
||||
{regions.map((region, index) => (
|
||||
<polygon
|
||||
key={index}
|
||||
className="region"
|
||||
points={region.points}
|
||||
fill="transparent"
|
||||
stroke="transparent"
|
||||
strokeWidth={4}
|
||||
onMouseEnter={(e) => handleMouseEnterRegion(e, region)}
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
/>
|
||||
))}
|
||||
{capitals.map((location, index) => (
|
||||
<Capital
|
||||
key={index}
|
||||
city={location}
|
||||
onMouseEnter={(e) => handleMouseEnterLocation(e, location)}
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
/>
|
||||
))}
|
||||
{cities.map((location, index) => (
|
||||
<City
|
||||
key={index}
|
||||
city={location}
|
||||
onMouseEnter={(e) => handleMouseEnterLocation(e, location)}
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
/>
|
||||
))}
|
||||
{pois.map((location, index) => (
|
||||
<Poi
|
||||
key={index}
|
||||
poi={location}
|
||||
onMouseEnter={(e) => handleMouseEnterLocation(e, location)}
|
||||
onMouseMove={handleMouseMove}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
/>
|
||||
))}
|
||||
</g>
|
||||
</svg>
|
||||
{tooltip.visible && (
|
||||
{tooltip.visible && !isDragging && (
|
||||
<div
|
||||
id={"tooltip"}
|
||||
className={"tooltip"}
|
||||
@@ -113,6 +190,6 @@ const Map: React.FC<MapProps> = ({imageUrl, regions = [], cities = [], pois = []
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Map;
|
||||
Reference in New Issue
Block a user