Added undo and redo and map control validation

This commit is contained in:
Mitchell McCaffrey
2020-04-19 13:33:31 +10:00
parent 1f20959940
commit 24de41fee7
4 changed files with 213 additions and 75 deletions

View File

@@ -1,12 +1,18 @@
import React, { useRef, useEffect, useState } from "react";
import simplify from "simplify-js";
import shortid from "shortid";
function MapDrawing({ width, height, selectedTool }) {
function MapDrawing({
width,
height,
selectedTool,
shapes,
onShapeAdd,
onShapeRemove,
}) {
const canvasRef = useRef();
const containerRef = useRef();
const [shapes, setShapes] = useState([]);
function getMousePosition(event) {
const container = containerRef.current;
if (container) {
@@ -17,12 +23,13 @@ function MapDrawing({ width, height, selectedTool }) {
}
}
const [brushPoints, setBrushPoints] = useState([]);
const [isMouseDown, setIsMouseDown] = useState(false);
function handleMouseDown(event) {
setIsMouseDown(true);
if (selectedTool === "brush") {
const position = getMousePosition(event);
setShapes((prevShapes) => [...prevShapes, { points: [position] }]);
setBrushPoints([position]);
}
}
@@ -34,41 +41,50 @@ function MapDrawing({ width, height, selectedTool }) {
}
if (isMouseDown && selectedTool === "brush") {
setMousePosition(position);
setShapes((prevShapes) => {
const currentShape = prevShapes.slice(-1)[0];
const otherShapes = prevShapes.slice(0, -1);
return [...otherShapes, { points: [...currentShape.points, position] }];
});
setBrushPoints((prevPoints) => [...prevPoints, position]);
}
}
function handleMouseUp(event) {
setIsMouseDown(false);
if (selectedTool === "brush") {
setShapes((prevShapes) => {
const currentShape = prevShapes.slice(-1)[0];
const otherShapes = prevShapes.slice(0, -1);
const simplified = simplify(currentShape.points, 0.001);
return [...otherShapes, { points: simplified }];
});
const simplifiedPoints = simplify(brushPoints, 0.001);
onShapeAdd({ id: shortid.generate(), points: simplifiedPoints });
setBrushPoints([]);
}
if (selectedTool === "erase" && hoveredShapeRef.current) {
onShapeRemove(hoveredShapeRef.current.id);
}
}
const hoveredShapeRef = useRef(null);
useEffect(() => {
function pointsToPath(points) {
const path = new Path2D();
path.moveTo(points[0].x * width, points[0].y * height);
for (let point of points.slice(1)) {
path.lineTo(point.x * width, point.y * height);
}
path.closePath();
return path;
}
function drawPath(path, color, context) {
context.fillStyle = color;
context.strokeStyle = color;
context.stroke(path);
context.fill(path);
}
const canvas = canvasRef.current;
if (canvas) {
const context = canvas.getContext("2d");
context.clearRect(0, 0, width, height);
let erasedShapes = [];
for (let [index, shape] of shapes.entries()) {
const path = new Path2D();
path.moveTo(shape.points[0].x * width, shape.points[0].y * height);
for (let point of shape.points.slice(1)) {
path.lineTo(point.x * width, point.y * height);
}
path.closePath();
let color = "#000000";
let hoveredShape = null;
for (let shape of shapes) {
const path = pointsToPath(shape.points);
// Detect hover
if (selectedTool === "erase") {
if (
context.isPointInPath(
@@ -77,26 +93,30 @@ function MapDrawing({ width, height, selectedTool }) {
mousePosition.y * height
)
) {
color = "#BB99FF";
if (isMouseDown) {
erasedShapes.push(index);
continue;
}
hoveredShape = shape;
}
}
context.fillStyle = color;
context.strokeStyle = color;
context.stroke(path);
context.fill(path);
drawPath(path, "#000000", context);
}
if (erasedShapes.length > 0) {
setShapes((prevShapes) =>
prevShapes.filter((_, i) => !erasedShapes.includes(i))
);
if (selectedTool === "brush" && brushPoints.length > 0) {
const path = pointsToPath(brushPoints);
drawPath(path, "#000000", context);
}
if (hoveredShape) {
const path = pointsToPath(hoveredShape.points);
drawPath(path, "#BB99FF", context);
}
hoveredShapeRef.current = hoveredShape;
}
}, [shapes, width, height, mousePosition, isMouseDown, selectedTool]);
}, [
shapes,
width,
height,
mousePosition,
isMouseDown,
selectedTool,
brushPoints,
]);
return (
<div