Moved map and map tokens to Konva

This commit is contained in:
Mitchell McCaffrey
2020-05-21 16:46:50 +10:00
parent 542388a67f
commit 5b70f69fb7
12 changed files with 420 additions and 503 deletions

View File

@@ -1,76 +1,88 @@
import React, { useRef, useContext } from "react";
import { Box, Image } from "theme-ui";
import React, { useContext, useState, useEffect, useRef } from "react";
import { Image as KonvaImage } from "react-konva";
import useImage from "use-image";
import TokenLabel from "../token/TokenLabel";
import TokenStatus from "../token/TokenStatus";
import usePreventTouch from "../../helpers/usePreventTouch";
import useDataSource from "../../helpers/useDataSource";
import useDebounce from "../../helpers/useDebounce";
import AuthContext from "../../contexts/AuthContext";
import MapInteractionContext from "../../contexts/MapInteractionContext";
import { tokenSources, unknownSource } from "../../tokens";
function MapToken({ token, tokenState, tokenSizePercent, className }) {
function MapToken({
token,
tokenState,
tokenSizePercent,
onTokenStateChange,
onTokenMenuOpen,
}) {
const { userId } = useContext(AuthContext);
const imageSource = useDataSource(token, tokenSources, unknownSource);
const {
setPreventMapInteraction,
mapWidth,
mapHeight,
stageScale,
} = useContext(MapInteractionContext);
const tokenSource = useDataSource(token, tokenSources, unknownSource);
const [tokenSourceImage, tokenSourceStatus] = useImage(tokenSource);
const [tokenAspectRatio, setTokenAspectRatio] = useState(1);
useEffect(() => {
if (tokenSourceImage) {
setTokenAspectRatio(tokenSourceImage.width / tokenSourceImage.height);
}
}, [tokenSourceImage]);
function handleDragEnd(event) {
onTokenStateChange({
...tokenState,
x: event.target.x() / mapWidth,
y: event.target.y() / mapHeight,
lastEditedBy: userId,
});
}
function handleClick(event) {
const tokenImage = event.target;
onTokenMenuOpen(tokenState.id, tokenImage);
}
const tokenWidth = tokenSizePercent * mapWidth * tokenState.size;
const tokenHeight =
tokenSizePercent * (mapWidth / tokenAspectRatio) * tokenState.size;
const debouncedStageScale = useDebounce(stageScale, 50);
const imageRef = useRef();
// Stop touch to prevent 3d touch gesutre on iOS
usePreventTouch(imageRef);
useEffect(() => {
const image = imageRef.current;
if (image) {
image.cache({
pixelRatio: debouncedStageScale,
});
image.drawHitFromCache();
// Force redraw
image.parent.draw();
}
}, [debouncedStageScale, tokenWidth, tokenHeight, tokenSourceStatus]);
return (
<Box
style={{
transform: `translate(${tokenState.x * 100}%, ${tokenState.y * 100}%)`,
width: "100%",
height: "100%",
transition:
tokenState.lastEditedBy === userId
? "initial"
: "transform 0.5s ease",
}}
sx={{
position: "absolute",
pointerEvents: "none",
}}
>
<Box
style={{
width: `${tokenSizePercent * (tokenState.size || 1)}%`,
}}
sx={{
position: "absolute",
pointerEvents: "all",
}}
>
<Box
sx={{
position: "absolute",
display: "flex", // Set display to flex to fix height being calculated wrong
width: "100%",
flexDirection: "column",
}}
>
<Image
className={className}
sx={{
userSelect: "none",
touchAction: "none",
width: "100%",
// Fix image from being clipped when transitioning
willChange: "transform",
}}
src={imageSource}
// pass id into the dom element which is then used by the ProxyToken
data-id={tokenState.id}
ref={imageRef}
/>
{tokenState.statuses && <TokenStatus token={tokenState} />}
{tokenState.label && <TokenLabel token={tokenState} />}
</Box>
</Box>
</Box>
<KonvaImage
ref={imageRef}
width={tokenWidth}
height={tokenHeight}
x={tokenState.x * mapWidth}
y={tokenState.y * mapHeight}
image={tokenSourceImage}
draggable
onDragEnd={handleDragEnd}
onClick={handleClick}
onMouseDown={() => setPreventMapInteraction(true)}
onMouseUp={() => setPreventMapInteraction(false)}
onTouchStart={() => setPreventMapInteraction(true)}
onTouchEnd={() => setPreventMapInteraction(false)}
/>
);
}