Moved drawing to use native konva events

This commit is contained in:
Mitchell McCaffrey 2020-06-21 16:29:42 +10:00
parent cb8b66e55f
commit 0bec582116
2 changed files with 85 additions and 156 deletions

View File

@ -1,8 +1,9 @@
import React, { useContext, useState, useCallback } from "react"; import React, { useContext, useState, useEffect } from "react";
import shortid from "shortid"; import shortid from "shortid";
import { Group, Line, Rect, Circle } from "react-konva"; import { Group, Line, Rect, Circle } from "react-konva";
import MapInteractionContext from "../../contexts/MapInteractionContext"; import MapInteractionContext from "../../contexts/MapInteractionContext";
import MapStageContext from "../../contexts/MapStageContext";
import { compare as comparePoints } from "../../helpers/vector2"; import { compare as comparePoints } from "../../helpers/vector2";
import { import {
@ -12,9 +13,9 @@ import {
simplifyPoints, simplifyPoints,
getStrokeWidth, getStrokeWidth,
} from "../../helpers/drawing"; } from "../../helpers/drawing";
import { getRelativePointerPositionNormalized } from "../../helpers/konva";
import colors from "../../helpers/colors"; import colors from "../../helpers/colors";
import useMapBrush from "../../helpers/useMapBrush";
function MapDrawing({ function MapDrawing({
shapes, shapes,
@ -25,6 +26,7 @@ function MapDrawing({
gridSize, gridSize,
}) { }) {
const { stageScale, mapWidth, mapHeight } = useContext(MapInteractionContext); const { stageScale, mapWidth, mapHeight } = useContext(MapInteractionContext);
const mapStageRef = useContext(MapStageContext);
const [drawingShape, setDrawingShape] = useState(null); const [drawingShape, setDrawingShape] = useState(null);
const [isBrushDown, setIsBrushDown] = useState(false); const [isBrushDown, setIsBrushDown] = useState(false);
const [erasingShapes, setErasingShapes] = useState([]); const [erasingShapes, setErasingShapes] = useState([]);
@ -42,57 +44,53 @@ function MapDrawing({
selectedToolSettings.type === "circle" || selectedToolSettings.type === "circle" ||
selectedToolSettings.type === "triangle"); selectedToolSettings.type === "triangle");
const handleBrushUp = useCallback(() => { useEffect(() => {
setIsBrushDown(false); if (!isEditing) {
if (erasingShapes.length > 0) { return;
onShapesRemove(erasingShapes.map((shape) => shape.id));
setErasingShapes([]);
} }
}, [erasingShapes, onShapesRemove]); const mapStage = mapStageRef.current;
const handleShapeDraw = useCallback( function getBrushPosition() {
(brushState, mapBrushPosition) => { const mapImage = mapStage.findOne("#mapImage");
function startShape() { return getBrushPositionForTool(
const brushPosition = getBrushPositionForTool( getRelativePointerPositionNormalized(mapImage),
mapBrushPosition, selectedToolId,
selectedToolId, selectedToolSettings,
selectedToolSettings, gridSize,
gridSize, shapes
shapes );
); }
const commonShapeData = {
color: selectedToolSettings && selectedToolSettings.color, function handleBrushDown() {
blend: selectedToolSettings && selectedToolSettings.useBlending, const brushPosition = getBrushPosition();
id: shortid.generate(), const commonShapeData = {
}; color: selectedToolSettings && selectedToolSettings.color,
if (isBrush) { blend: selectedToolSettings && selectedToolSettings.useBlending,
setDrawingShape({ id: shortid.generate(),
type: "path", };
pathType: selectedToolSettings.type === "brush" ? "stroke" : "fill", if (isBrush) {
data: { points: [brushPosition] }, setDrawingShape({
strokeWidth: selectedToolSettings.type === "brush" ? 1 : 0, type: "path",
...commonShapeData, pathType: selectedToolSettings.type === "brush" ? "stroke" : "fill",
}); data: { points: [brushPosition] },
} else if (isShape) { strokeWidth: selectedToolSettings.type === "brush" ? 1 : 0,
setDrawingShape({ ...commonShapeData,
type: "shape", });
shapeType: selectedToolSettings.type, } else if (isShape) {
data: getDefaultShapeData(selectedToolSettings.type, brushPosition), setDrawingShape({
strokeWidth: 0, type: "shape",
...commonShapeData, shapeType: selectedToolSettings.type,
}); data: getDefaultShapeData(selectedToolSettings.type, brushPosition),
} strokeWidth: 0,
setIsBrushDown(true); ...commonShapeData,
});
} }
setIsBrushDown(true);
}
function continueShape() { function handleBrushMove() {
const brushPosition = getBrushPositionForTool( const brushPosition = getBrushPosition();
mapBrushPosition, if (isBrushDown && drawingShape) {
selectedToolId,
selectedToolSettings,
gridSize,
shapes
);
if (isBrush) { if (isBrush) {
setDrawingShape((prevShape) => { setDrawingShape((prevShape) => {
const prevPoints = prevShape.data.points; const prevPoints = prevShape.data.points;
@ -127,49 +125,51 @@ function MapDrawing({
})); }));
} }
} }
}
function endShape() { function handleBrushUp() {
if (isBrush && drawingShape) { if (isBrush && drawingShape) {
if (drawingShape.data.points.length > 1) { if (drawingShape.data.points.length > 1) {
onShapeAdd(drawingShape);
}
} else if (isShape && drawingShape) {
onShapeAdd(drawingShape); onShapeAdd(drawingShape);
} }
setDrawingShape(null); } else if (isShape && drawingShape) {
handleBrushUp(); onShapeAdd(drawingShape);
} }
switch (brushState) { if (erasingShapes.length > 0) {
case "first": onShapesRemove(erasingShapes.map((shape) => shape.id));
startShape(); setErasingShapes([]);
return;
case "drawing":
continueShape();
return;
case "last":
endShape();
return;
default:
return;
} }
},
[
selectedToolId,
selectedToolSettings,
gridSize,
stageScale,
onShapeAdd,
shapes,
drawingShape,
handleBrushUp,
isBrush,
isShape,
]
);
// Move away from this as it is too slow to respond setDrawingShape(null);
useMapBrush(isEditing, handleShapeDraw); setIsBrushDown(false);
}
mapStage.on("mousedown touchstart", handleBrushDown);
mapStage.on("mousemove touchmove", handleBrushMove);
mapStage.on("mouseup touchend", handleBrushUp);
return () => {
mapStage.off("mousedown touchstart", handleBrushDown);
mapStage.off("mousemove touchmove", handleBrushMove);
mapStage.off("mouseup touchend", handleBrushUp);
};
}, [
drawingShape,
erasingShapes,
gridSize,
isBrush,
isBrushDown,
isEditing,
isShape,
mapStageRef,
onShapeAdd,
onShapesRemove,
selectedToolId,
selectedToolSettings,
shapes,
stageScale,
]);
function handleShapeOver(shape, isDown) { function handleShapeOver(shape, isDown) {
if (shouldHover && isDown) { if (shouldHover && isDown) {

View File

@ -1,71 +0,0 @@
import { useContext, useRef, useEffect } from "react";
import MapInteractionContext from "../contexts/MapInteractionContext";
import { compare } from "./vector2";
import usePrevious from "./usePrevious";
/**
* @callback onBrushUpdate
* @param {string} drawState "first" | "drawing" | "last"
* @param {Object} brushPosition the normalized x and y coordinates of the brush on the map
*/
/**
* Helper to get the maps drag position as it changes
* @param {boolean} shouldUpdate
* @param {onBrushUpdate} onBrushUpdate
*/
function useMapBrush(shouldUpdate, onBrushUpdate) {
const { stageDragState, mapDragPositionRef } = useContext(
MapInteractionContext
);
const requestRef = useRef();
const previousDragState = usePrevious(stageDragState);
const previousBrushPositionRef = useRef(mapDragPositionRef.current);
useEffect(() => {
function updateBrush(forceUpdate) {
const drawState =
stageDragState === "dragging" ? "drawing" : stageDragState;
const brushPosition = mapDragPositionRef.current;
const previousBrushPostition = previousBrushPositionRef.current;
// Only update brush when it has moved
if (
!compare(brushPosition, previousBrushPostition, 0.0001) ||
forceUpdate
) {
onBrushUpdate(drawState, brushPosition);
previousBrushPositionRef.current = brushPosition;
}
}
function animate() {
if (!shouldUpdate) {
return;
}
requestRef.current = requestAnimationFrame(animate);
updateBrush(false);
}
requestRef.current = requestAnimationFrame(animate);
if (stageDragState !== previousDragState && shouldUpdate) {
updateBrush(true);
}
return () => {
cancelAnimationFrame(requestRef.current);
};
}, [
shouldUpdate,
onBrushUpdate,
stageDragState,
mapDragPositionRef,
previousDragState,
]);
}
export default useMapBrush;