2020-09-24 16:54:33 +10:00
|
|
|
import React, { useState, useRef, useEffect } from "react";
|
|
|
|
|
import { Box } from "theme-ui";
|
|
|
|
|
import { Stage, Layer, Image } from "react-konva";
|
|
|
|
|
import ReactResizeDetector from "react-resize-detector";
|
|
|
|
|
|
2020-10-02 13:35:50 +10:00
|
|
|
import useMapImage from "../../helpers/useMapImage";
|
2020-09-24 16:54:33 +10:00
|
|
|
import usePreventOverscroll from "../../helpers/usePreventOverscroll";
|
2020-10-02 13:35:50 +10:00
|
|
|
import useStageInteraction from "../../helpers/useStageInteraction";
|
2020-09-24 16:54:33 +10:00
|
|
|
|
|
|
|
|
function MapEditor({ map }) {
|
2020-10-02 13:35:50 +10:00
|
|
|
const [mapImageSource] = useMapImage(map);
|
2020-09-24 16:54:33 +10:00
|
|
|
|
|
|
|
|
const [stageWidth, setStageWidth] = useState(1);
|
|
|
|
|
const [stageHeight, setStageHeight] = useState(1);
|
|
|
|
|
const [stageScale, setStageScale] = useState(1);
|
|
|
|
|
|
|
|
|
|
const stageRatio = stageWidth / stageHeight;
|
|
|
|
|
const mapRatio = map ? map.width / map.height : 1;
|
|
|
|
|
|
|
|
|
|
let mapWidth;
|
|
|
|
|
let mapHeight;
|
|
|
|
|
if (stageRatio > mapRatio) {
|
|
|
|
|
mapWidth = map ? stageHeight / (map.height / map.width) : stageWidth;
|
|
|
|
|
mapHeight = stageHeight;
|
|
|
|
|
} else {
|
|
|
|
|
mapWidth = stageWidth;
|
|
|
|
|
mapHeight = map ? stageWidth * (map.height / map.width) : stageHeight;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const stageTranslateRef = useRef({ x: 0, y: 0 });
|
|
|
|
|
const mapLayerRef = useRef();
|
|
|
|
|
|
|
|
|
|
function handleResize(width, height) {
|
|
|
|
|
setStageWidth(width);
|
|
|
|
|
setStageHeight(height);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-02 13:35:50 +10:00
|
|
|
// Reset map translate and scale
|
2020-09-24 16:54:33 +10:00
|
|
|
useEffect(() => {
|
|
|
|
|
const layer = mapLayerRef.current;
|
|
|
|
|
const containerRect = containerRef.current.getBoundingClientRect();
|
2020-10-02 13:35:50 +10:00
|
|
|
if (layer) {
|
2020-09-24 16:54:33 +10:00
|
|
|
let newTranslate;
|
|
|
|
|
if (stageRatio > mapRatio) {
|
|
|
|
|
newTranslate = {
|
|
|
|
|
x: -(mapWidth - containerRect.width) / 2,
|
|
|
|
|
y: 0,
|
|
|
|
|
};
|
|
|
|
|
} else {
|
|
|
|
|
newTranslate = {
|
|
|
|
|
x: 0,
|
|
|
|
|
y: -(mapHeight - containerRect.height) / 2,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
layer.x(newTranslate.x);
|
|
|
|
|
layer.y(newTranslate.y);
|
|
|
|
|
layer.draw();
|
|
|
|
|
stageTranslateRef.current = newTranslate;
|
|
|
|
|
|
|
|
|
|
setStageScale(1);
|
|
|
|
|
}
|
2020-10-02 13:35:50 +10:00
|
|
|
}, [map.id, mapWidth, mapHeight, stageRatio, mapRatio]);
|
2020-09-24 16:54:33 +10:00
|
|
|
|
2020-10-02 13:35:50 +10:00
|
|
|
const bind = useStageInteraction(
|
|
|
|
|
mapLayerRef.current,
|
|
|
|
|
stageScale,
|
|
|
|
|
setStageScale,
|
|
|
|
|
stageTranslateRef
|
|
|
|
|
);
|
2020-09-24 16:54:33 +10:00
|
|
|
|
|
|
|
|
const containerRef = useRef();
|
|
|
|
|
usePreventOverscroll(containerRef);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Box
|
|
|
|
|
sx={{
|
|
|
|
|
width: "100%",
|
|
|
|
|
height: "300px",
|
2020-10-02 13:35:50 +10:00
|
|
|
cursor: "move",
|
2020-09-24 16:54:33 +10:00
|
|
|
touchAction: "none",
|
|
|
|
|
outline: "none",
|
|
|
|
|
}}
|
|
|
|
|
bg="muted"
|
|
|
|
|
ref={containerRef}
|
|
|
|
|
{...bind()}
|
|
|
|
|
>
|
|
|
|
|
<ReactResizeDetector handleWidth handleHeight onResize={handleResize}>
|
|
|
|
|
<Stage
|
|
|
|
|
width={stageWidth}
|
|
|
|
|
height={stageHeight}
|
|
|
|
|
scale={{ x: stageScale, y: stageScale }}
|
|
|
|
|
x={stageWidth / 2}
|
|
|
|
|
y={stageHeight / 2}
|
|
|
|
|
offset={{ x: stageWidth / 2, y: stageHeight / 2 }}
|
|
|
|
|
>
|
|
|
|
|
<Layer ref={mapLayerRef}>
|
2020-10-02 13:35:50 +10:00
|
|
|
<Image image={mapImageSource} width={mapWidth} height={mapHeight} />
|
2020-09-24 16:54:33 +10:00
|
|
|
</Layer>
|
|
|
|
|
</Stage>
|
|
|
|
|
</ReactResizeDetector>
|
|
|
|
|
</Box>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default MapEditor;
|