Added pan and zoom to map

This commit is contained in:
Mitchell McCaffrey 2020-03-20 21:46:52 +11:00
parent b7ce6892f0
commit 91c7c9f9ad
2 changed files with 99 additions and 42 deletions

View File

@ -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

View File

@ -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;