Moved back to drag events for map and fog drawing
Moved to an event driven model for dragging
This commit is contained in:
parent
afb22c7a73
commit
9a4d047cd5
@ -25,7 +25,9 @@ function MapDrawing({
|
|||||||
selectedToolSettings,
|
selectedToolSettings,
|
||||||
gridSize,
|
gridSize,
|
||||||
}) {
|
}) {
|
||||||
const { stageScale, mapWidth, mapHeight } = useContext(MapInteractionContext);
|
const { stageScale, mapWidth, mapHeight, interactionEmitter } = useContext(
|
||||||
|
MapInteractionContext
|
||||||
|
);
|
||||||
const mapStageRef = useContext(MapStageContext);
|
const mapStageRef = useContext(MapStageContext);
|
||||||
const [drawingShape, setDrawingShape] = useState(null);
|
const [drawingShape, setDrawingShape] = useState(null);
|
||||||
const [isBrushDown, setIsBrushDown] = useState(false);
|
const [isBrushDown, setIsBrushDown] = useState(false);
|
||||||
@ -145,14 +147,14 @@ function MapDrawing({
|
|||||||
setIsBrushDown(false);
|
setIsBrushDown(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
mapStage.on("mousedown touchstart", handleBrushDown);
|
interactionEmitter.on("dragStart", handleBrushDown);
|
||||||
mapStage.on("mousemove touchmove", handleBrushMove);
|
interactionEmitter.on("drag", handleBrushMove);
|
||||||
mapStage.on("mouseup touchend", handleBrushUp);
|
interactionEmitter.on("dragEnd", handleBrushUp);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
mapStage.off("mousedown touchstart", handleBrushDown);
|
interactionEmitter.off("dragStart", handleBrushDown);
|
||||||
mapStage.off("mousemove touchmove", handleBrushMove);
|
interactionEmitter.off("drag", handleBrushMove);
|
||||||
mapStage.off("mouseup touchend", handleBrushUp);
|
interactionEmitter.off("dragEnd", handleBrushUp);
|
||||||
};
|
};
|
||||||
}, [
|
}, [
|
||||||
drawingShape,
|
drawingShape,
|
||||||
@ -169,6 +171,7 @@ function MapDrawing({
|
|||||||
selectedToolSettings,
|
selectedToolSettings,
|
||||||
shapes,
|
shapes,
|
||||||
stageScale,
|
stageScale,
|
||||||
|
interactionEmitter,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
function handleShapeOver(shape, isDown) {
|
function handleShapeOver(shape, isDown) {
|
||||||
@ -188,6 +191,7 @@ function MapDrawing({
|
|||||||
onTouchStart: () => handleShapeOver(shape, true),
|
onTouchStart: () => handleShapeOver(shape, true),
|
||||||
fill: colors[shape.color] || shape.color,
|
fill: colors[shape.color] || shape.color,
|
||||||
opacity: shape.blend ? 0.5 : 1,
|
opacity: shape.blend ? 0.5 : 1,
|
||||||
|
id: shape.id,
|
||||||
};
|
};
|
||||||
if (shape.type === "path") {
|
if (shape.type === "path") {
|
||||||
return (
|
return (
|
||||||
|
@ -31,7 +31,9 @@ function MapFog({
|
|||||||
selectedToolSettings,
|
selectedToolSettings,
|
||||||
gridSize,
|
gridSize,
|
||||||
}) {
|
}) {
|
||||||
const { stageScale, mapWidth, mapHeight } = useContext(MapInteractionContext);
|
const { stageScale, mapWidth, mapHeight, interactionEmitter } = useContext(
|
||||||
|
MapInteractionContext
|
||||||
|
);
|
||||||
const mapStageRef = useContext(MapStageContext);
|
const mapStageRef = useContext(MapStageContext);
|
||||||
const [drawingShape, setDrawingShape] = useState(null);
|
const [drawingShape, setDrawingShape] = useState(null);
|
||||||
const [isBrushDown, setIsBrushDown] = useState(false);
|
const [isBrushDown, setIsBrushDown] = useState(false);
|
||||||
@ -109,18 +111,6 @@ function MapFog({
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (selectedToolSettings.type === "polygon" && drawingShape) {
|
|
||||||
const brushPosition = getBrushPosition();
|
|
||||||
setDrawingShape((prevShape) => {
|
|
||||||
return {
|
|
||||||
...prevShape,
|
|
||||||
data: {
|
|
||||||
...prevShape.data,
|
|
||||||
points: [...prevShape.data.points.slice(0, -1), brushPosition],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleBrushUp() {
|
function handleBrushUp() {
|
||||||
@ -152,29 +142,10 @@ function MapFog({
|
|||||||
onShapeAdd(shape);
|
onShapeAdd(shape);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setDrawingShape(null);
|
setDrawingShape(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedToolSettings.type === "polygon") {
|
// Erase
|
||||||
const brushPosition = getBrushPosition();
|
|
||||||
setDrawingShape({
|
|
||||||
type: "fog",
|
|
||||||
data: {
|
|
||||||
points: [
|
|
||||||
...(drawingShape ? drawingShape.data.points : [brushPosition]),
|
|
||||||
brushPosition,
|
|
||||||
],
|
|
||||||
holes: [],
|
|
||||||
},
|
|
||||||
strokeWidth: 0.5,
|
|
||||||
color: selectedToolSettings.useFogSubtract ? "red" : "black",
|
|
||||||
blend: false,
|
|
||||||
id: shortid.generate(),
|
|
||||||
visible: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (editingShapes.length > 0) {
|
if (editingShapes.length > 0) {
|
||||||
if (selectedToolSettings.type === "remove") {
|
if (selectedToolSettings.type === "remove") {
|
||||||
onShapesRemove(editingShapes.map((shape) => shape.id));
|
onShapesRemove(editingShapes.map((shape) => shape.id));
|
||||||
@ -192,14 +163,69 @@ function MapFog({
|
|||||||
setIsBrushDown(false);
|
setIsBrushDown(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
mapStage.on("mousedown touchstart", handleBrushDown);
|
function handlePolygonClick() {
|
||||||
mapStage.on("mousemove touchmove", handleBrushMove);
|
if (selectedToolSettings.type === "polygon") {
|
||||||
mapStage.on("mouseup touchend", handleBrushUp);
|
const brushPosition = getBrushPosition();
|
||||||
|
setDrawingShape((prevDrawingShape) => {
|
||||||
|
if (prevDrawingShape) {
|
||||||
|
return {
|
||||||
|
...prevDrawingShape,
|
||||||
|
data: {
|
||||||
|
...prevDrawingShape.data,
|
||||||
|
points: [...prevDrawingShape.data.points, brushPosition],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
type: "fog",
|
||||||
|
data: {
|
||||||
|
points: [brushPosition, brushPosition],
|
||||||
|
holes: [],
|
||||||
|
},
|
||||||
|
strokeWidth: 0.5,
|
||||||
|
color: selectedToolSettings.useFogSubtract ? "red" : "black",
|
||||||
|
blend: false,
|
||||||
|
id: shortid.generate(),
|
||||||
|
visible: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handlePolygonMove() {
|
||||||
|
if (selectedToolSettings.type === "polygon" && drawingShape) {
|
||||||
|
const brushPosition = getBrushPosition();
|
||||||
|
setDrawingShape((prevShape) => {
|
||||||
|
if (!prevShape) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...prevShape,
|
||||||
|
data: {
|
||||||
|
...prevShape.data,
|
||||||
|
points: [...prevShape.data.points.slice(0, -1), brushPosition],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interactionEmitter.on("dragStart", handleBrushDown);
|
||||||
|
interactionEmitter.on("drag", handleBrushMove);
|
||||||
|
interactionEmitter.on("dragEnd", handleBrushUp);
|
||||||
|
// Use mouse events for polygon and erase to allow for single clicks
|
||||||
|
mapStage.on("mousedown touchstart", handlePolygonMove);
|
||||||
|
mapStage.on("mousemove touchmove", handlePolygonMove);
|
||||||
|
mapStage.on("click tap", handlePolygonClick);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
mapStage.off("mousedown touchstart", handleBrushDown);
|
interactionEmitter.off("dragStart", handleBrushDown);
|
||||||
mapStage.off("mousemove touchmove", handleBrushMove);
|
interactionEmitter.off("drag", handleBrushMove);
|
||||||
mapStage.off("mouseup touchend", handleBrushUp);
|
interactionEmitter.off("dragEnd", handleBrushUp);
|
||||||
|
mapStage.off("mousedown touchstart", handlePolygonMove);
|
||||||
|
mapStage.off("mousemove touchmove", handlePolygonMove);
|
||||||
|
mapStage.off("click tap", handlePolygonClick);
|
||||||
};
|
};
|
||||||
}, [
|
}, [
|
||||||
mapStageRef,
|
mapStageRef,
|
||||||
@ -216,6 +242,7 @@ function MapFog({
|
|||||||
selectedToolSettings,
|
selectedToolSettings,
|
||||||
shapes,
|
shapes,
|
||||||
stageScale,
|
stageScale,
|
||||||
|
interactionEmitter,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const finishDrawingPolygon = useCallback(() => {
|
const finishDrawingPolygon = useCallback(() => {
|
||||||
@ -317,15 +344,16 @@ function MapFog({
|
|||||||
if (shape.data.points.length === 0) {
|
if (shape.data.points.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
const isCross = shape.data.points.length < 4;
|
||||||
return (
|
return (
|
||||||
<Tick
|
<Tick
|
||||||
x={shape.data.points[0].x * mapWidth}
|
x={shape.data.points[0].x * mapWidth}
|
||||||
y={shape.data.points[0].y * mapHeight}
|
y={shape.data.points[0].y * mapHeight}
|
||||||
scale={1 / stageScale}
|
scale={1 / stageScale}
|
||||||
cross={shape.data.points.length < 4}
|
cross={isCross}
|
||||||
onClick={() => {
|
onClick={(e) => {
|
||||||
// Check that there is enough points after clicking
|
e.cancelBubble = true;
|
||||||
if (shape.data.points.length < 5) {
|
if (isCross) {
|
||||||
setDrawingShape(null);
|
setDrawingShape(null);
|
||||||
} else {
|
} else {
|
||||||
finishDrawingPolygon();
|
finishDrawingPolygon();
|
||||||
|
@ -4,6 +4,7 @@ import { useGesture } from "react-use-gesture";
|
|||||||
import ReactResizeDetector from "react-resize-detector";
|
import ReactResizeDetector from "react-resize-detector";
|
||||||
import useImage from "use-image";
|
import useImage from "use-image";
|
||||||
import { Stage, Layer, Image } from "react-konva";
|
import { Stage, Layer, Image } from "react-konva";
|
||||||
|
import { EventEmitter } from "events";
|
||||||
|
|
||||||
import usePreventOverscroll from "../../helpers/usePreventOverscroll";
|
import usePreventOverscroll from "../../helpers/usePreventOverscroll";
|
||||||
import useDataSource from "../../helpers/useDataSource";
|
import useDataSource from "../../helpers/useDataSource";
|
||||||
@ -34,15 +35,12 @@ function MapInteraction({
|
|||||||
const [stageWidth, setStageWidth] = useState(1);
|
const [stageWidth, setStageWidth] = useState(1);
|
||||||
const [stageHeight, setStageHeight] = useState(1);
|
const [stageHeight, setStageHeight] = useState(1);
|
||||||
const [stageScale, setStageScale] = useState(1);
|
const [stageScale, setStageScale] = useState(1);
|
||||||
// "none" | "first" | "dragging" | "last"
|
|
||||||
const [stageDragState, setStageDragState] = useState("none");
|
|
||||||
const [preventMapInteraction, setPreventMapInteraction] = useState(false);
|
const [preventMapInteraction, setPreventMapInteraction] = useState(false);
|
||||||
|
|
||||||
const stageWidthRef = useRef(stageWidth);
|
const stageWidthRef = useRef(stageWidth);
|
||||||
const stageHeightRef = useRef(stageHeight);
|
const stageHeightRef = useRef(stageHeight);
|
||||||
// Avoid state udpates when panning the map by using a ref and updating the konva element directly
|
// Avoid state udpates when panning the map by using a ref and updating the konva element directly
|
||||||
const stageTranslateRef = useRef({ x: 0, y: 0 });
|
const stageTranslateRef = useRef({ x: 0, y: 0 });
|
||||||
const mapDragPositionRef = useRef({ x: 0, y: 0 });
|
|
||||||
|
|
||||||
// Reset transform when map changes
|
// Reset transform when map changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -62,30 +60,13 @@ function MapInteraction({
|
|||||||
}
|
}
|
||||||
}, [map]);
|
}, [map]);
|
||||||
|
|
||||||
// Convert a client space XY to be normalized to the map image
|
|
||||||
function getMapDragPosition(xy) {
|
|
||||||
const [x, y] = xy;
|
|
||||||
const container = containerRef.current;
|
|
||||||
const mapImage = mapImageRef.current;
|
|
||||||
if (container && mapImage) {
|
|
||||||
const containerRect = container.getBoundingClientRect();
|
|
||||||
const mapRect = mapImage.getClientRect();
|
|
||||||
|
|
||||||
const offsetX = x - containerRect.left - mapRect.x;
|
|
||||||
const offsetY = y - containerRect.top - mapRect.y;
|
|
||||||
|
|
||||||
const normalizedX = offsetX / mapRect.width;
|
|
||||||
const normalizedY = offsetY / mapRect.height;
|
|
||||||
|
|
||||||
return { x: normalizedX, y: normalizedY };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const pinchPreviousDistanceRef = useRef();
|
const pinchPreviousDistanceRef = useRef();
|
||||||
const pinchPreviousOriginRef = useRef();
|
const pinchPreviousOriginRef = useRef();
|
||||||
const isInteractingWithCanvas = useRef(false);
|
const isInteractingWithCanvas = useRef(false);
|
||||||
const previousSelectedToolRef = useRef(selectedToolId);
|
const previousSelectedToolRef = useRef(selectedToolId);
|
||||||
|
|
||||||
|
const [interactionEmitter] = useState(new EventEmitter());
|
||||||
|
|
||||||
const bind = useGesture({
|
const bind = useGesture({
|
||||||
onWheelStart: ({ event }) => {
|
onWheelStart: ({ event }) => {
|
||||||
isInteractingWithCanvas.current =
|
isInteractingWithCanvas.current =
|
||||||
@ -168,15 +149,14 @@ function MapInteraction({
|
|||||||
layer.draw();
|
layer.draw();
|
||||||
stageTranslateRef.current = newTranslate;
|
stageTranslateRef.current = newTranslate;
|
||||||
}
|
}
|
||||||
mapDragPositionRef.current = getMapDragPosition(xy);
|
if (first) {
|
||||||
const newDragState = first ? "first" : last ? "last" : "dragging";
|
interactionEmitter.emit("dragStart");
|
||||||
if (stageDragState !== newDragState) {
|
} else if (last) {
|
||||||
setStageDragState(newDragState);
|
interactionEmitter.emit("dragEnd");
|
||||||
|
} else {
|
||||||
|
interactionEmitter.emit("drag");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onDragEnd: () => {
|
|
||||||
setStageDragState("none");
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function handleResize(width, height) {
|
function handleResize(width, height) {
|
||||||
@ -214,11 +194,10 @@ function MapInteraction({
|
|||||||
stageScale,
|
stageScale,
|
||||||
stageWidth,
|
stageWidth,
|
||||||
stageHeight,
|
stageHeight,
|
||||||
stageDragState,
|
|
||||||
setPreventMapInteraction,
|
setPreventMapInteraction,
|
||||||
mapWidth,
|
mapWidth,
|
||||||
mapHeight,
|
mapHeight,
|
||||||
mapDragPositionRef,
|
interactionEmitter,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Enable keyboard interaction for map stage container
|
// Enable keyboard interaction for map stage container
|
||||||
|
@ -4,11 +4,10 @@ const MapInteractionContext = React.createContext({
|
|||||||
stageScale: 1,
|
stageScale: 1,
|
||||||
stageWidth: 1,
|
stageWidth: 1,
|
||||||
stageHeight: 1,
|
stageHeight: 1,
|
||||||
stageDragState: "none",
|
|
||||||
setPreventMapInteraction: () => {},
|
setPreventMapInteraction: () => {},
|
||||||
mapWidth: 1,
|
mapWidth: 1,
|
||||||
mapHeight: 1,
|
mapHeight: 1,
|
||||||
mapDragPositionRef: { current: undefined },
|
interactionEmitter: null,
|
||||||
});
|
});
|
||||||
export const MapInteractionProvider = MapInteractionContext.Provider;
|
export const MapInteractionProvider = MapInteractionContext.Provider;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user