Moved map and map tokens to Konva
This commit is contained in:
@@ -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)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user