Changed fog so it is tranparent when you can edit it

Added a show preview toggle to see how others will see it.
Refactored fog and drawing props to simplify
This commit is contained in:
Mitchell McCaffrey 2020-08-04 14:51:31 +10:00
parent 96ff9a9fa3
commit 558db7d88b
7 changed files with 131 additions and 77 deletions

View File

@ -50,7 +50,12 @@ function Map({
const [selectedToolId, setSelectedToolId] = useState("pan"); const [selectedToolId, setSelectedToolId] = useState("pan");
const [toolSettings, setToolSettings] = useState({ const [toolSettings, setToolSettings] = useState({
fog: { type: "polygon", useEdgeSnapping: false, useFogCut: false }, fog: {
type: "polygon",
useEdgeSnapping: false,
useFogCut: false,
preview: false,
},
drawing: { drawing: {
color: "red", color: "red",
type: "brush", type: "brush",
@ -278,8 +283,9 @@ function Map({
shapes={mapShapes} shapes={mapShapes}
onShapeAdd={handleMapShapeAdd} onShapeAdd={handleMapShapeAdd}
onShapesRemove={handleMapShapesRemove} onShapesRemove={handleMapShapesRemove}
selectedToolId={selectedToolId} active={selectedToolId === "drawing"}
selectedToolSettings={toolSettings[selectedToolId]} toolId="drawing"
toolSettings={toolSettings.drawing}
gridSize={gridSizeNormalized} gridSize={gridSizeNormalized}
/> />
); );
@ -291,9 +297,11 @@ function Map({
onShapeSubtract={handleFogShapeSubtract} onShapeSubtract={handleFogShapeSubtract}
onShapesRemove={handleFogShapesRemove} onShapesRemove={handleFogShapesRemove}
onShapesEdit={handleFogShapesEdit} onShapesEdit={handleFogShapesEdit}
selectedToolId={selectedToolId} active={selectedToolId === "fog"}
selectedToolSettings={toolSettings[selectedToolId]} toolId="fog"
toolSettings={toolSettings.fog}
gridSize={gridSizeNormalized} gridSize={gridSizeNormalized}
transparent={allowFogDrawing && !toolSettings.fog.preview}
/> />
); );

View File

@ -21,8 +21,9 @@ function MapDrawing({
shapes, shapes,
onShapeAdd, onShapeAdd,
onShapesRemove, onShapesRemove,
selectedToolId, active,
selectedToolSettings, toolId,
toolSettings,
gridSize, gridSize,
}) { }) {
const { stageScale, mapWidth, mapHeight, interactionEmitter } = useContext( const { stageScale, mapWidth, mapHeight, interactionEmitter } = useContext(
@ -33,22 +34,17 @@ function MapDrawing({
const [isBrushDown, setIsBrushDown] = useState(false); const [isBrushDown, setIsBrushDown] = useState(false);
const [erasingShapes, setErasingShapes] = useState([]); const [erasingShapes, setErasingShapes] = useState([]);
const shouldHover = const shouldHover = toolSettings.type === "erase";
selectedToolSettings && selectedToolSettings.type === "erase";
const isEditing = selectedToolId === "drawing";
const isBrush = const isBrush =
selectedToolSettings && toolSettings.type === "brush" || toolSettings.type === "paint";
(selectedToolSettings.type === "brush" ||
selectedToolSettings.type === "paint");
const isShape = const isShape =
selectedToolSettings && toolSettings.type === "line" ||
(selectedToolSettings.type === "line" || toolSettings.type === "rectangle" ||
selectedToolSettings.type === "rectangle" || toolSettings.type === "circle" ||
selectedToolSettings.type === "circle" || toolSettings.type === "triangle";
selectedToolSettings.type === "triangle");
useEffect(() => { useEffect(() => {
if (!isEditing) { if (!active) {
return; return;
} }
const mapStage = mapStageRef.current; const mapStage = mapStageRef.current;
@ -57,8 +53,8 @@ function MapDrawing({
const mapImage = mapStage.findOne("#mapImage"); const mapImage = mapStage.findOne("#mapImage");
return getBrushPositionForTool( return getBrushPositionForTool(
getRelativePointerPositionNormalized(mapImage), getRelativePointerPositionNormalized(mapImage),
selectedToolId, toolId,
selectedToolSettings, toolSettings,
gridSize, gridSize,
shapes shapes
); );
@ -67,24 +63,24 @@ function MapDrawing({
function handleBrushDown() { function handleBrushDown() {
const brushPosition = getBrushPosition(); const brushPosition = getBrushPosition();
const commonShapeData = { const commonShapeData = {
color: selectedToolSettings && selectedToolSettings.color, color: toolSettings.color,
blend: selectedToolSettings && selectedToolSettings.useBlending, blend: toolSettings.useBlending,
id: shortid.generate(), id: shortid.generate(),
}; };
if (isBrush) { if (isBrush) {
setDrawingShape({ setDrawingShape({
type: "path", type: "path",
pathType: selectedToolSettings.type === "brush" ? "stroke" : "fill", pathType: toolSettings.type === "brush" ? "stroke" : "fill",
data: { points: [brushPosition] }, data: { points: [brushPosition] },
strokeWidth: selectedToolSettings.type === "brush" ? 1 : 0, strokeWidth: toolSettings.type === "brush" ? 1 : 0,
...commonShapeData, ...commonShapeData,
}); });
} else if (isShape) { } else if (isShape) {
setDrawingShape({ setDrawingShape({
type: "shape", type: "shape",
shapeType: selectedToolSettings.type, shapeType: toolSettings.type,
data: getDefaultShapeData(selectedToolSettings.type, brushPosition), data: getDefaultShapeData(toolSettings.type, brushPosition),
strokeWidth: selectedToolSettings.type === "line" ? 1 : 0, strokeWidth: toolSettings.type === "line" ? 1 : 0,
...commonShapeData, ...commonShapeData,
}); });
} }
@ -163,13 +159,13 @@ function MapDrawing({
gridSize, gridSize,
isBrush, isBrush,
isBrushDown, isBrushDown,
isEditing, active,
toolId,
isShape, isShape,
mapStageRef, mapStageRef,
onShapeAdd, onShapeAdd,
onShapesRemove, onShapesRemove,
selectedToolId, toolSettings,
selectedToolSettings,
shapes, shapes,
stageScale, stageScale,
interactionEmitter, interactionEmitter,

View File

@ -27,9 +27,11 @@ function MapFog({
onShapeSubtract, onShapeSubtract,
onShapesRemove, onShapesRemove,
onShapesEdit, onShapesEdit,
selectedToolId, active,
selectedToolSettings, toolId,
toolSettings,
gridSize, gridSize,
transparent,
}) { }) {
const { stageScale, mapWidth, mapHeight, interactionEmitter } = useContext( const { stageScale, mapWidth, mapHeight, interactionEmitter } = useContext(
MapInteractionContext MapInteractionContext
@ -39,16 +41,14 @@ function MapFog({
const [isBrushDown, setIsBrushDown] = useState(false); const [isBrushDown, setIsBrushDown] = useState(false);
const [editingShapes, setEditingShapes] = useState([]); const [editingShapes, setEditingShapes] = useState([]);
const isEditing = selectedToolId === "fog";
const shouldHover = const shouldHover =
isEditing && active &&
(selectedToolSettings.type === "toggle" || (toolSettings.type === "toggle" || toolSettings.type === "remove");
selectedToolSettings.type === "remove");
const [patternImage] = useImage(diagonalPattern); const [patternImage] = useImage(diagonalPattern);
useEffect(() => { useEffect(() => {
if (!isEditing) { if (!active) {
return; return;
} }
@ -58,8 +58,8 @@ function MapFog({
const mapImage = mapStage.findOne("#mapImage"); const mapImage = mapStage.findOne("#mapImage");
return getBrushPositionForTool( return getBrushPositionForTool(
getRelativePointerPositionNormalized(mapImage), getRelativePointerPositionNormalized(mapImage),
selectedToolId, toolId,
selectedToolSettings, toolSettings,
gridSize, gridSize,
shapes shapes
); );
@ -67,7 +67,7 @@ function MapFog({
function handleBrushDown() { function handleBrushDown() {
const brushPosition = getBrushPosition(); const brushPosition = getBrushPosition();
if (selectedToolSettings.type === "brush") { if (toolSettings.type === "brush") {
setDrawingShape({ setDrawingShape({
type: "fog", type: "fog",
data: { data: {
@ -75,7 +75,7 @@ function MapFog({
holes: [], holes: [],
}, },
strokeWidth: 0.5, strokeWidth: 0.5,
color: selectedToolSettings.useFogSubtract ? "red" : "black", color: toolSettings.useFogSubtract ? "red" : "black",
blend: false, blend: false,
id: shortid.generate(), id: shortid.generate(),
visible: true, visible: true,
@ -85,11 +85,7 @@ function MapFog({
} }
function handleBrushMove() { function handleBrushMove() {
if ( if (toolSettings.type === "brush" && isBrushDown && drawingShape) {
selectedToolSettings.type === "brush" &&
isBrushDown &&
drawingShape
) {
const brushPosition = getBrushPosition(); const brushPosition = getBrushPosition();
setDrawingShape((prevShape) => { setDrawingShape((prevShape) => {
const prevPoints = prevShape.data.points; const prevPoints = prevShape.data.points;
@ -114,8 +110,8 @@ function MapFog({
} }
function handleBrushUp() { function handleBrushUp() {
if (selectedToolSettings.type === "brush" && drawingShape) { if (toolSettings.type === "brush" && drawingShape) {
const subtract = selectedToolSettings.useFogSubtract; const subtract = toolSettings.useFogSubtract;
if (drawingShape.data.points.length > 1) { if (drawingShape.data.points.length > 1) {
let shapeData = {}; let shapeData = {};
@ -147,9 +143,9 @@ function MapFog({
// Erase // Erase
if (editingShapes.length > 0) { if (editingShapes.length > 0) {
if (selectedToolSettings.type === "remove") { if (toolSettings.type === "remove") {
onShapesRemove(editingShapes.map((shape) => shape.id)); onShapesRemove(editingShapes.map((shape) => shape.id));
} else if (selectedToolSettings.type === "toggle") { } else if (toolSettings.type === "toggle") {
onShapesEdit( onShapesEdit(
editingShapes.map((shape) => ({ editingShapes.map((shape) => ({
...shape, ...shape,
@ -164,7 +160,7 @@ function MapFog({
} }
function handlePolygonClick() { function handlePolygonClick() {
if (selectedToolSettings.type === "polygon") { if (toolSettings.type === "polygon") {
const brushPosition = getBrushPosition(); const brushPosition = getBrushPosition();
setDrawingShape((prevDrawingShape) => { setDrawingShape((prevDrawingShape) => {
if (prevDrawingShape) { if (prevDrawingShape) {
@ -183,7 +179,7 @@ function MapFog({
holes: [], holes: [],
}, },
strokeWidth: 0.5, strokeWidth: 0.5,
color: selectedToolSettings.useFogSubtract ? "red" : "black", color: toolSettings.useFogSubtract ? "red" : "black",
blend: false, blend: false,
id: shortid.generate(), id: shortid.generate(),
visible: true, visible: true,
@ -194,7 +190,7 @@ function MapFog({
} }
function handlePolygonMove() { function handlePolygonMove() {
if (selectedToolSettings.type === "polygon" && drawingShape) { if (toolSettings.type === "polygon" && drawingShape) {
const brushPosition = getBrushPosition(); const brushPosition = getBrushPosition();
setDrawingShape((prevShape) => { setDrawingShape((prevShape) => {
if (!prevShape) { if (!prevShape) {
@ -229,7 +225,7 @@ function MapFog({
}; };
}, [ }, [
mapStageRef, mapStageRef,
isEditing, active,
drawingShape, drawingShape,
editingShapes, editingShapes,
gridSize, gridSize,
@ -238,15 +234,15 @@ function MapFog({
onShapeSubtract, onShapeSubtract,
onShapesEdit, onShapesEdit,
onShapesRemove, onShapesRemove,
selectedToolId, toolId,
selectedToolSettings, toolSettings,
shapes, shapes,
stageScale, stageScale,
interactionEmitter, interactionEmitter,
]); ]);
const finishDrawingPolygon = useCallback(() => { const finishDrawingPolygon = useCallback(() => {
const subtract = selectedToolSettings.useFogSubtract; const subtract = toolSettings.useFogSubtract;
const data = { const data = {
...drawingShape.data, ...drawingShape.data,
// Remove the last point as it hasn't been placed yet // Remove the last point as it hasn't been placed yet
@ -263,16 +259,12 @@ function MapFog({
} }
setDrawingShape(null); setDrawingShape(null);
}, [selectedToolSettings, drawingShape, onShapeSubtract, onShapeAdd]); }, [toolSettings, drawingShape, onShapeSubtract, onShapeAdd]);
// Add keyboard shortcuts // Add keyboard shortcuts
useEffect(() => { useEffect(() => {
function handleKeyDown({ key }) { function handleKeyDown({ key }) {
if ( if (key === "Enter" && toolSettings.type === "polygon" && drawingShape) {
key === "Enter" &&
selectedToolSettings.type === "polygon" &&
drawingShape
) {
finishDrawingPolygon(); finishDrawingPolygon();
} }
if (key === "Escape" && drawingShape) { if (key === "Escape" && drawingShape) {
@ -296,7 +288,7 @@ function MapFog({
} }
return { return {
...prevShape, ...prevShape,
color: selectedToolSettings.useFogSubtract ? "black" : "red", color: toolSettings.useFogSubtract ? "black" : "red",
}; };
}); });
} }
@ -307,12 +299,7 @@ function MapFog({
interactionEmitter.off("keyDown", handleKeyDown); interactionEmitter.off("keyDown", handleKeyDown);
interactionEmitter.off("keyUp", handleKeyUp); interactionEmitter.off("keyUp", handleKeyUp);
}; };
}, [ }, [finishDrawingPolygon, interactionEmitter, drawingShape, toolSettings]);
finishDrawingPolygon,
interactionEmitter,
drawingShape,
selectedToolSettings,
]);
function handleShapeOver(shape, isDown) { function handleShapeOver(shape, isDown) {
if (shouldHover && isDown) { if (shouldHover && isDown) {
@ -349,11 +336,14 @@ function MapFog({
mapWidth, mapWidth,
mapHeight mapHeight
)} )}
visible={isEditing || shape.visible} visible={(active && !toolSettings.preview) || shape.visible}
opacity={isEditing ? 0.5 : 1} opacity={transparent ? 0.5 : 1}
fillPatternImage={patternImage} fillPatternImage={patternImage}
fillPriority={isEditing && !shape.visible ? "pattern" : "color"} fillPriority={active && !shape.visible ? "pattern" : "color"}
holes={holes} holes={holes}
// Disable collision if the fog is transparent and we're not editing it
// This allows tokens to be moved under the fog
hitFunc={transparent && !active ? () => {} : undefined}
/> />
); );
} }
@ -394,8 +384,8 @@ function MapFog({
{shapes.map(renderShape)} {shapes.map(renderShape)}
{drawingShape && renderShape(drawingShape)} {drawingShape && renderShape(drawingShape)}
{drawingShape && {drawingShape &&
selectedToolSettings && toolSettings &&
selectedToolSettings.type === "polygon" && toolSettings.type === "polygon" &&
renderPolygonAcceptTick(drawingShape)} renderPolygonAcceptTick(drawingShape)}
{editingShapes.length > 0 && editingShapes.map(renderEditingShape)} {editingShapes.length > 0 && editingShapes.map(renderEditingShape)}
</Group> </Group>

View File

@ -0,0 +1,19 @@
import React from "react";
import { IconButton } from "theme-ui";
import PreviewOnIcon from "../../../icons/FogPreviewOnIcon";
import PreviewOffIcon from "../../../icons/FogPreviewOffIcon";
function FogPreviewToggle({ useFogPreview, onFogPreviewChange }) {
return (
<IconButton
aria-label={useFogPreview ? "Disable Fog Preview" : "Enable Fog Preview"}
title={useFogPreview ? "Disable Fog Preview" : "Enable Fog Preview"}
onClick={() => onFogPreviewChange(!useFogPreview)}
>
{useFogPreview ? <PreviewOnIcon /> : <PreviewOffIcon />}
</IconButton>
);
}
export default FogPreviewToggle;

View File

@ -1,9 +1,10 @@
import React, { useContext, useEffect } from "react"; import React, { useContext, useEffect } from "react";
import { Flex } from "theme-ui"; import { Flex, Label, Checkbox } from "theme-ui";
import { useMedia } from "react-media"; import { useMedia } from "react-media";
import EdgeSnappingToggle from "./EdgeSnappingToggle"; import EdgeSnappingToggle from "./EdgeSnappingToggle";
import RadioIconButton from "./RadioIconButton"; import RadioIconButton from "./RadioIconButton";
import FogPreviewToggle from "./FogPreviewToggle";
import FogBrushIcon from "../../../icons/FogBrushIcon"; import FogBrushIcon from "../../../icons/FogBrushIcon";
import FogPolygonIcon from "../../../icons/FogPolygonIcon"; import FogPolygonIcon from "../../../icons/FogPolygonIcon";
@ -142,6 +143,10 @@ function BrushToolSettings({
onSettingChange({ useEdgeSnapping }) onSettingChange({ useEdgeSnapping })
} }
/> />
<FogPreviewToggle
useFogPreview={settings.preview}
onFogPreviewChange={(preview) => onSettingChange({ preview })}
/>
<Divider vertical /> <Divider vertical />
<UndoButton <UndoButton
onClick={() => onToolAction("fogUndo")} onClick={() => onToolAction("fogUndo")}

View File

@ -0,0 +1,18 @@
import React from "react";
function FogPreviewOffIcon() {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
height="24"
viewBox="0 0 24 24"
width="24"
fill="currentcolor"
>
<path d="M24 24H0V0h24z" fill="none" />
<path d="M12 6.5c2.76 0 5 2.24 5 5 0 .51-.1 1-.24 1.46l3.06 3.06c1.39-1.23 2.49-2.77 3.18-4.53C21.27 7.11 17 4 12 4c-1.27 0-2.49.2-3.64.57l2.17 2.17c.47-.14.96-.24 1.47-.24zM2.71 3.16c-.39.39-.39 1.02 0 1.41l1.97 1.97C3.06 7.83 1.77 9.53 1 11.5 2.73 15.89 7 19 12 19c1.52 0 2.97-.3 4.31-.82l2.72 2.72c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41L4.13 3.16c-.39-.39-1.03-.39-1.42 0zM12 16.5c-2.76 0-5-2.24-5-5 0-.77.18-1.5.49-2.14l1.57 1.57c-.03.18-.06.37-.06.57 0 1.66 1.34 3 3 3 .2 0 .38-.03.57-.07L14.14 16c-.65.32-1.37.5-2.14.5zm2.97-5.33c-.15-1.4-1.25-2.49-2.64-2.64l2.64 2.64z" />
</svg>
);
}
export default FogPreviewOffIcon;

View File

@ -0,0 +1,18 @@
import React from "react";
function FogPreviewOnIcon() {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
height="24"
viewBox="0 0 24 24"
width="24"
fill="currentcolor"
>
<path d="M24 24H0V0h24z" fill="none" />
<path d="M12 4C7 4 2.73 7.11 1 11.5 2.73 15.89 7 19 12 19s9.27-3.11 11-7.5C21.27 7.11 17 4 12 4zm0 12.5c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z" />
</svg>
);
}
export default FogPreviewOnIcon;