Added pan and zoom to map
This commit is contained in:
parent
b7ce6892f0
commit
91c7c9f9ad
@ -1,5 +1,6 @@
|
||||
import React, { useRef } from "react";
|
||||
import React, { useRef, useEffect, useState } from "react";
|
||||
import { Box, Image } from "theme-ui";
|
||||
import interact from "interactjs";
|
||||
|
||||
import Token from "../components/Token";
|
||||
import ProxyToken from "../components/ProxyToken";
|
||||
@ -7,6 +8,9 @@ import AddMapButton from "../components/AddMapButton";
|
||||
|
||||
const mapTokenClassName = "map-token";
|
||||
const defaultTokenSize = 48;
|
||||
const zoomSpeed = -0.005;
|
||||
const minZoom = 0.1;
|
||||
const maxZoom = 5;
|
||||
|
||||
function Map({
|
||||
mapSource,
|
||||
@ -26,6 +30,36 @@ function Map({
|
||||
}
|
||||
}
|
||||
|
||||
const [mapTranslate, setMapTranslate] = useState({ x: 0, y: 0 });
|
||||
const [mapScale, setMapScale] = useState(1);
|
||||
|
||||
useEffect(() => {
|
||||
interact(".map").draggable({
|
||||
inertia: true,
|
||||
listeners: {
|
||||
move: event => {
|
||||
setMapTranslate(previousMapTranslate => ({
|
||||
x: previousMapTranslate.x + event.dx,
|
||||
y: previousMapTranslate.y + event.dy
|
||||
}));
|
||||
}
|
||||
}
|
||||
});
|
||||
interact(".map").on("doubletap", event => {
|
||||
event.preventDefault();
|
||||
setMapTranslate({ x: 0, y: 0 });
|
||||
setMapScale(1);
|
||||
});
|
||||
}, []);
|
||||
|
||||
function handleZoom(event) {
|
||||
event.preventDefault();
|
||||
const deltaY = event.deltaY * zoomSpeed;
|
||||
setMapScale(mapScale =>
|
||||
Math.max(Math.min(mapScale + deltaY, maxZoom), minZoom)
|
||||
);
|
||||
}
|
||||
|
||||
const mapRef = useRef(null);
|
||||
const rows = mapData && mapData.rows;
|
||||
const tokenSizePercent = (1 / rows) * 100;
|
||||
@ -42,58 +76,74 @@ function Map({
|
||||
backgroundColor: "rgba(0, 0, 0, 0.1)"
|
||||
}}
|
||||
bg="background"
|
||||
onWheel={handleZoom}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
position: "relative",
|
||||
overflow: "hidden",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)"
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: 0,
|
||||
paddingBottom: `${(1 / aspectRatio) * 100}%`
|
||||
}}
|
||||
/>
|
||||
|
||||
<Box
|
||||
sx={{ position: "absolute", top: 0, right: 0, bottom: 0, left: 0 }}
|
||||
>
|
||||
<Image ref={mapRef} id="map" src={mapSource} />
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0
|
||||
style={{
|
||||
transform: `translate(${mapTranslate.x}px, ${mapTranslate.y}px) scale(${mapScale})`
|
||||
}}
|
||||
>
|
||||
{Object.values(tokens).map(token => (
|
||||
<Box
|
||||
key={token.id}
|
||||
style={{
|
||||
left: `${token.x * 100}%`,
|
||||
top: `${token.y * 100}%`,
|
||||
width: `${tokenSizePercent}%`
|
||||
}}
|
||||
sx={{
|
||||
position: "absolute",
|
||||
display: "flex"
|
||||
}}
|
||||
>
|
||||
<Token
|
||||
tokenId={token.id}
|
||||
image={token.image}
|
||||
className={mapTokenClassName}
|
||||
/>
|
||||
</Box>
|
||||
))}
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: 0,
|
||||
paddingBottom: `${(1 / aspectRatio) * 100}%`
|
||||
}}
|
||||
/>
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
left: 0
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
ref={mapRef}
|
||||
id="map"
|
||||
sx={{ width: "100%" }}
|
||||
src={mapSource}
|
||||
/>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0
|
||||
}}
|
||||
>
|
||||
{Object.values(tokens).map(token => (
|
||||
<Box
|
||||
key={token.id}
|
||||
style={{
|
||||
left: `${token.x * 100}%`,
|
||||
top: `${token.y * 100}%`,
|
||||
width: `${tokenSizePercent}%`
|
||||
}}
|
||||
sx={{
|
||||
position: "absolute",
|
||||
display: "flex"
|
||||
}}
|
||||
>
|
||||
<Token
|
||||
tokenId={token.id}
|
||||
image={token.image}
|
||||
className={mapTokenClassName}
|
||||
/>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box
|
||||
|
@ -4,7 +4,14 @@ import { Image } from "theme-ui";
|
||||
function Token({ image, className, tokenId }) {
|
||||
// Store the token id in the html element for the drag code to pick it up
|
||||
const idProp = tokenId ? { "data-token-id": tokenId } : {};
|
||||
return <Image className={className} src={image} {...idProp} />;
|
||||
return (
|
||||
<Image
|
||||
className={className}
|
||||
sx={{ userSelect: "none" }}
|
||||
src={image}
|
||||
{...idProp}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default Token;
|
||||
|
Loading…
Reference in New Issue
Block a user