Added basic drawing control

This commit is contained in:
Mitchell McCaffrey 2020-04-19 00:24:06 +10:00
parent c12abf5706
commit 4555c9bf71
3 changed files with 99 additions and 33 deletions

View File

@ -34,6 +34,8 @@ function Map({
const [mapTranslate, setMapTranslate] = useState({ x: 0, y: 0 });
const [mapScale, setMapScale] = useState(1);
const [selectedTool, setSelectedTool] = useState("pan");
useEffect(() => {
interact(".map")
.gesturable({
@ -42,10 +44,12 @@ function Map({
setMapScale((previousMapScale) =>
Math.max(Math.min(previousMapScale + event.ds, maxZoom), minZoom)
);
setMapTranslate((previousMapTranslate) => ({
x: previousMapTranslate.x + event.dx,
y: previousMapTranslate.y + event.dy,
}));
if (selectedTool === "pan") {
setMapTranslate((previousMapTranslate) => ({
x: previousMapTranslate.x + event.dx,
y: previousMapTranslate.y + event.dy,
}));
}
},
},
})
@ -53,10 +57,12 @@ function Map({
inertia: true,
listeners: {
move: (event) => {
// setMapTranslate((previousMapTranslate) => ({
// x: previousMapTranslate.x + event.dx,
// y: previousMapTranslate.y + event.dy,
// }));
if (selectedTool === "pan") {
setMapTranslate((previousMapTranslate) => ({
x: previousMapTranslate.x + event.dx,
y: previousMapTranslate.y + event.dy,
}));
}
},
},
});
@ -65,7 +71,7 @@ function Map({
setMapTranslate({ x: 0, y: 0 });
setMapScale(1);
});
}, []);
}, [selectedTool]);
// Reset map transform when map changes
useEffect(() => {
@ -194,10 +200,15 @@ function Map({
<MapDrawing
width={mapData ? mapData.width : 0}
height={mapData ? mapData.height : 0}
selectedTool={selectedTool}
/>
</Box>
</Box>
<MapControls onMapChange={onMapChange} />
<MapControls
onMapChange={onMapChange}
onToolChange={setSelectedTool}
selectedTool={selectedTool}
/>
</Box>
<ProxyToken
tokenClassName={mapTokenClassName}

View File

@ -9,7 +9,13 @@ import EraseToolIcon from "../icons/EraseToolIcon";
import UndoIcon from "../icons/UndoIcon";
import RedoIcon from "../icons/RedoIcon";
function MapControls({ onMapChange }) {
function MapControls({
onMapChange,
onToolChange,
selectedTool,
onUndo,
onRedo,
}) {
const divider = (
<Box
my={2}
@ -33,13 +39,28 @@ function MapControls({ onMapChange }) {
</IconButton>
<AddMapButton onMapChange={onMapChange} />
{divider}
<IconButton aria-label="Pan Tool" title="Pan Tool">
<IconButton
aria-label="Pan Tool"
title="Pan Tool"
onClick={() => onToolChange("pan")}
sx={{ color: selectedTool === "pan" ? "primary" : "text" }}
>
<PanToolIcon />
</IconButton>
<IconButton aria-label="Brush Tool" title="Brush Tool">
<IconButton
aria-label="Brush Tool"
title="Brush Tool"
onClick={() => onToolChange("brush")}
sx={{ color: selectedTool === "brush" ? "primary" : "text" }}
>
<BrushToolIcon />
</IconButton>
<IconButton aria-label="Erase Tool" title="Erase Tool">
<IconButton
aria-label="Erase Tool"
title="Erase Tool"
onClick={() => onToolChange("erase")}
sx={{ color: selectedTool === "erase" ? "primary" : "text" }}
>
<EraseToolIcon />
</IconButton>
{divider}

View File

@ -1,7 +1,7 @@
import React, { useRef, useEffect, useState } from "react";
import simplify from "simplify-js";
function MapDrawing({ width, height }) {
function MapDrawing({ width, height, selectedTool }) {
const canvasRef = useRef();
const containerRef = useRef();
@ -20,13 +20,20 @@ function MapDrawing({ width, height }) {
const [isMouseDown, setIsMouseDown] = useState(false);
function handleMouseDown(event) {
setIsMouseDown(true);
const position = getMousePosition(event);
setShapes((prevShapes) => [...prevShapes, { points: [position] }]);
if (selectedTool === "brush") {
const position = getMousePosition(event);
setShapes((prevShapes) => [...prevShapes, { points: [position] }]);
}
}
const [mousePosition, setMousePosition] = useState({ x: 0, y: 0 });
function handleMouseMove(event) {
if (isMouseDown) {
const position = getMousePosition(event);
const position = getMousePosition(event);
if (selectedTool === "erase") {
setMousePosition(position);
}
if (isMouseDown && selectedTool === "brush") {
setMousePosition(position);
setShapes((prevShapes) => {
const currentShape = prevShapes.slice(-1)[0];
const otherShapes = prevShapes.slice(0, -1);
@ -37,12 +44,14 @@ function MapDrawing({ width, height }) {
function handleMouseUp(event) {
setIsMouseDown(false);
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 }];
});
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 }];
});
}
}
useEffect(() => {
@ -51,18 +60,43 @@ function MapDrawing({ width, height }) {
const context = canvas.getContext("2d");
context.clearRect(0, 0, width, height);
for (let shape of shapes) {
context.beginPath();
context.moveTo(shape.points[0].x * width, shape.points[0].y * 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)) {
context.lineTo(point.x * width, point.y * height);
path.lineTo(point.x * width, point.y * height);
}
context.closePath();
context.stroke();
context.fill();
path.closePath();
let color = "#000000";
if (selectedTool === "erase") {
if (
context.isPointInPath(
path,
mousePosition.x * width,
mousePosition.y * height
)
) {
color = "#BB99FF";
if (isMouseDown) {
erasedShapes.push(index);
continue;
}
}
}
context.fillStyle = color;
context.strokeStyle = color;
context.stroke(path);
context.fill(path);
}
if (erasedShapes.length > 0) {
setShapes((prevShapes) =>
prevShapes.filter((_, i) => !erasedShapes.includes(i))
);
}
}
}, [shapes, width, height]);
}, [shapes, width, height, mousePosition, isMouseDown, selectedTool]);
return (
<div