Added brush blending and brush gesture options
This commit is contained in:
parent
36c3e76403
commit
d2474ae198
@ -17,6 +17,7 @@
|
||||
"react-modal": "^3.11.2",
|
||||
"react-router-dom": "^5.1.2",
|
||||
"react-scripts": "3.4.0",
|
||||
"shape-detector": "^0.2.1",
|
||||
"shortid": "^2.2.15",
|
||||
"simple-peer": "^9.6.2",
|
||||
"simplebar-react": "^2.1.0",
|
||||
|
@ -46,6 +46,8 @@ function Map({
|
||||
const [selectedTool, setSelectedTool] = useState("pan");
|
||||
const [brushColor, setBrushColor] = useState("black");
|
||||
const [useBrushGridSnapping, setUseBrushGridSnapping] = useState(false);
|
||||
const [useBrushBlending, setUseBrushBlending] = useState(false);
|
||||
const [useBrushGesture, setUseBrushGesture] = useState(false);
|
||||
|
||||
const [drawnShapes, setDrawnShapes] = useState([]);
|
||||
function handleShapeAdd(shape) {
|
||||
@ -285,6 +287,8 @@ function Map({
|
||||
brushColor={brushColor}
|
||||
useGridSnapping={useBrushGridSnapping}
|
||||
gridSize={gridSizeNormalized}
|
||||
useBrushBlending={useBrushBlending}
|
||||
useBrushGesture={useBrushGesture}
|
||||
/>
|
||||
{mapTokens}
|
||||
</Box>
|
||||
@ -303,6 +307,10 @@ function Map({
|
||||
onEraseAll={handleShapeRemoveAll}
|
||||
useBrushGridSnapping={useBrushGridSnapping}
|
||||
onBrushGridSnappingChange={setUseBrushGridSnapping}
|
||||
useBrushBlending={useBrushBlending}
|
||||
onBrushBlendingChange={setUseBrushBlending}
|
||||
useBrushGesture={useBrushGesture}
|
||||
onBrushGestureChange={setUseBrushGesture}
|
||||
/>
|
||||
</Box>
|
||||
<ProxyToken
|
||||
|
@ -10,6 +10,10 @@ import UndoIcon from "../icons/UndoIcon";
|
||||
import RedoIcon from "../icons/RedoIcon";
|
||||
import GridOnIcon from "../icons/GridOnIcon";
|
||||
import GridOffIcon from "../icons/GridOffIcon";
|
||||
import BlendOnIcon from "../icons/BlendOnIcon";
|
||||
import BlendOffIcon from "../icons/BlendOffIcon";
|
||||
import GestureOnIcon from "../icons/GestureOnIcon";
|
||||
import GestureOffIcon from "../icons/GestureOffIcon";
|
||||
|
||||
import colors, { colorOptions } from "../helpers/colors";
|
||||
|
||||
@ -30,6 +34,10 @@ function MapControls({
|
||||
onEraseAll,
|
||||
useBrushGridSnapping,
|
||||
onBrushGridSnappingChange,
|
||||
useBrushBlending,
|
||||
onBrushBlendingChange,
|
||||
useBrushGesture,
|
||||
onBrushGestureChange,
|
||||
}) {
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
|
||||
@ -71,35 +79,53 @@ function MapControls({
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
<Box>
|
||||
<Label
|
||||
sx={{
|
||||
fontSize: 1,
|
||||
alignItems: "center",
|
||||
":hover": { color: "primary", cursor: "pointer" },
|
||||
":active": { color: "secondary" },
|
||||
}}
|
||||
<Flex sx={{ justifyContent: "space-between" }}>
|
||||
<IconButton
|
||||
aria-label={
|
||||
useBrushGridSnapping
|
||||
? "Disable Brush Grid Snapping"
|
||||
: "Enable Brush Grid Snapping"
|
||||
}
|
||||
title={
|
||||
useBrushGridSnapping
|
||||
? "Disable Brush Grid Snapping"
|
||||
: "Enable Brush Grid Snapping"
|
||||
}
|
||||
onClick={() => onBrushGridSnappingChange(!useBrushGridSnapping)}
|
||||
>
|
||||
{useBrushGridSnapping ? (
|
||||
<IconButton
|
||||
aria-label="Disable Brush Grid Snapping"
|
||||
title="Disable Brush Grid Snapping"
|
||||
onClick={() => onBrushGridSnappingChange(false)}
|
||||
>
|
||||
<GridOnIcon />
|
||||
</IconButton>
|
||||
) : (
|
||||
<IconButton
|
||||
aria-label="Enable Brush Grid Snapping"
|
||||
title="Enable Brush Grid Snapping"
|
||||
onClick={() => onBrushGridSnappingChange(true)}
|
||||
>
|
||||
<GridOffIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
Grid Lock
|
||||
</Label>
|
||||
</Box>
|
||||
{useBrushGridSnapping ? <GridOnIcon /> : <GridOffIcon />}
|
||||
</IconButton>
|
||||
<IconButton
|
||||
aria-label={
|
||||
useBrushBlending
|
||||
? "Disable Brush Blending"
|
||||
: "Enable Brush Blending"
|
||||
}
|
||||
title={
|
||||
useBrushBlending
|
||||
? "Disable Brush Blending"
|
||||
: "Enable Brush Blending"
|
||||
}
|
||||
onClick={() => onBrushBlendingChange(!useBrushBlending)}
|
||||
>
|
||||
{useBrushBlending ? <BlendOnIcon /> : <BlendOffIcon />}
|
||||
</IconButton>
|
||||
<IconButton
|
||||
aria-label={
|
||||
useBrushGesture
|
||||
? "Disable Gesture Detection"
|
||||
: "Enable Gesture Detection"
|
||||
}
|
||||
title={
|
||||
useBrushGesture
|
||||
? "Disable Gesture Detection"
|
||||
: "Enable Gesture Detection"
|
||||
}
|
||||
onClick={() => onBrushGestureChange(!useBrushGesture)}
|
||||
>
|
||||
{useBrushGesture ? <GestureOnIcon /> : <GestureOffIcon />}
|
||||
</IconButton>
|
||||
</Flex>
|
||||
</Box>
|
||||
),
|
||||
erase: (
|
||||
|
@ -5,6 +5,11 @@ import shortid from "shortid";
|
||||
import colors from "../helpers/colors";
|
||||
import { snapPositionToGrid } from "../helpers/shared";
|
||||
|
||||
import {
|
||||
pointsToGesture,
|
||||
convertPointsToGesturePath,
|
||||
} from "../helpers/gestures";
|
||||
|
||||
function MapDrawing({
|
||||
width,
|
||||
height,
|
||||
@ -15,6 +20,8 @@ function MapDrawing({
|
||||
brushColor,
|
||||
useGridSnapping,
|
||||
gridSize,
|
||||
useBrushBlending,
|
||||
useBrushGesture,
|
||||
}) {
|
||||
const canvasRef = useRef();
|
||||
const containerRef = useRef();
|
||||
@ -87,11 +94,17 @@ function MapDrawing({
|
||||
if (selectedTool === "brush") {
|
||||
if (brushPoints.length > 1) {
|
||||
const simplifiedPoints = simplify(brushPoints, 0.001);
|
||||
const gesture = useBrushGesture
|
||||
? pointsToGesture(simplifiedPoints)
|
||||
: "none";
|
||||
onShapeAdd({
|
||||
id: shortid.generate(),
|
||||
points: simplifiedPoints,
|
||||
color: brushColor,
|
||||
gesture,
|
||||
blend: useBrushBlending,
|
||||
});
|
||||
|
||||
setBrushPoints([]);
|
||||
}
|
||||
}
|
||||
@ -112,7 +125,20 @@ function MapDrawing({
|
||||
return path;
|
||||
}
|
||||
|
||||
function drawPath(path, color, context) {
|
||||
function shapeToPath(shape) {
|
||||
return shape.gesture !== "none"
|
||||
? convertPointsToGesturePath(
|
||||
shape.points.map((p) => ({
|
||||
x: p.x * width,
|
||||
y: p.y * height,
|
||||
})),
|
||||
shape.gesture
|
||||
)
|
||||
: pointsToPath(shape.points);
|
||||
}
|
||||
|
||||
function drawPath(path, color, blend, context) {
|
||||
context.globalAlpha = blend ? 0.5 : 1.0;
|
||||
context.fillStyle = color;
|
||||
context.strokeStyle = color;
|
||||
context.stroke(path);
|
||||
@ -126,7 +152,7 @@ function MapDrawing({
|
||||
context.clearRect(0, 0, width, height);
|
||||
let hoveredShape = null;
|
||||
for (let shape of shapes) {
|
||||
const path = pointsToPath(shape.points);
|
||||
const path = shapeToPath(shape);
|
||||
// Detect hover
|
||||
if (selectedTool === "erase") {
|
||||
if (
|
||||
@ -139,15 +165,15 @@ function MapDrawing({
|
||||
hoveredShape = shape;
|
||||
}
|
||||
}
|
||||
drawPath(path, colors[shape.color], context);
|
||||
drawPath(path, colors[shape.color], shape.blend, context);
|
||||
}
|
||||
if (selectedTool === "brush" && brushPoints.length > 0) {
|
||||
const path = pointsToPath(brushPoints);
|
||||
drawPath(path, colors[brushColor], context);
|
||||
drawPath(path, colors[brushColor], useBrushBlending, context);
|
||||
}
|
||||
if (hoveredShape) {
|
||||
const path = pointsToPath(hoveredShape.points);
|
||||
drawPath(path, "#BB99FF", context);
|
||||
const path = shapeToPath(hoveredShape);
|
||||
drawPath(path, "#BB99FF", true, context);
|
||||
}
|
||||
hoveredShapeRef.current = hoveredShape;
|
||||
}
|
||||
@ -160,6 +186,8 @@ function MapDrawing({
|
||||
selectedTool,
|
||||
brushPoints,
|
||||
brushColor,
|
||||
useBrushGesture,
|
||||
useBrushBlending,
|
||||
]);
|
||||
|
||||
return (
|
||||
|
1181
src/helpers/gestures.js
Normal file
1181
src/helpers/gestures.js
Normal file
File diff suppressed because it is too large
Load Diff
18
src/icons/BlendOffIcon.js
Normal file
18
src/icons/BlendOffIcon.js
Normal file
@ -0,0 +1,18 @@
|
||||
import React from "react";
|
||||
|
||||
function BlendOffIcon() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
fill="currentcolor"
|
||||
>
|
||||
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||
<path d="M12 5.1v4.05l7.4 7.4c1.15-2.88.59-6.28-1.75-8.61l-4.94-4.95c-.39-.39-1.02-.39-1.41 0L8.56 5.71l1.41 1.41L12 5.1zm-8.31-.02c-.39.39-.39 1.02 0 1.41l2.08 2.08c-2.54 3.14-2.35 7.75.57 10.68C7.9 20.8 9.95 21.58 12 21.58c1.78 0 3.56-.59 5.02-1.77l2 2c.39.39 1.02.39 1.41 0 .39-.39.39-1.02 0-1.41L5.11 5.08c-.39-.39-1.03-.39-1.42 0zM12 19.59c-1.6 0-3.11-.62-4.24-1.76C6.62 16.69 6 15.19 6 13.59c0-1.32.43-2.56 1.21-3.59L12 14.79v4.8z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default BlendOffIcon;
|
18
src/icons/BlendOnIcon.js
Normal file
18
src/icons/BlendOnIcon.js
Normal file
@ -0,0 +1,18 @@
|
||||
import React from "react";
|
||||
|
||||
function BlendOnIcon() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
fill="currentcolor"
|
||||
>
|
||||
<path d="M24 0H0v24h24V0zm0 0H0v24h24V0zM0 24h24V0H0v24z" fill="none" />
|
||||
<path d="M17.66 8l-4.95-4.94c-.39-.39-1.02-.39-1.41 0L6.34 8C4.78 9.56 4 11.64 4 13.64s.78 4.11 2.34 5.67 3.61 2.35 5.66 2.35 4.1-.79 5.66-2.35S20 15.64 20 13.64 19.22 9.56 17.66 8zM6 14c.01-2 .62-3.27 1.76-4.4L12 5.27l4.24 4.38C17.38 10.77 17.99 12 18 14H6z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default BlendOnIcon;
|
18
src/icons/GestureOffIcon.js
Normal file
18
src/icons/GestureOffIcon.js
Normal file
@ -0,0 +1,18 @@
|
||||
import React from "react";
|
||||
|
||||
function GestureOffIcon() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
fill="currentcolor"
|
||||
>
|
||||
<path fill="none" d="M0 0h24v24H0z" />
|
||||
<path d="M4.222 4.808l16.97 16.97a1 1 0 01-1.414 1.414l-1.3-1.299a4.5 4.5 0 01-5.371-5.372l-2.112-2.112.005.091v6c0 .51-.388.935-.884.993L10 21.5H4c-.51 0-.935-.388-.993-.884L3 20.5v-6c0-.51.388-.935.884-.993L4 13.5h6l.09.005-7.282-7.283a1 1 0 011.414-1.414zM17.5 13a4.5 4.5 0 014.09 6.381l-5.971-5.97A4.483 4.483 0 0117.5 13zm-4.724-9.706l.074.106 3.71 6.08c.39.627-.024 1.434-.735 1.514L15.71 11h-2.503L9.046 6.839 11.15 3.4a.992.992 0 011.626-.106z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default GestureOffIcon;
|
20
src/icons/GestureOnIcon.js
Normal file
20
src/icons/GestureOnIcon.js
Normal file
@ -0,0 +1,20 @@
|
||||
import React from "react";
|
||||
|
||||
function GestureOnIcon() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
fill="currentcolor"
|
||||
>
|
||||
<path d="M0 0h24v24H0V0z" fill="none" />
|
||||
<path d="M11.15 3.4L7.43 9.48c-.41.66.07 1.52.85 1.52h7.43c.78 0 1.26-.86.85-1.52L12.85 3.4c-.39-.64-1.31-.64-1.7 0z" />
|
||||
<circle cx="17.5" cy="17.5" r="4.5" />
|
||||
<path d="M4 21.5h6c.55 0 1-.45 1-1v-6c0-.55-.45-1-1-1H4c-.55 0-1 .45-1 1v6c0 .55.45 1 1 1z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default GestureOnIcon;
|
@ -9879,6 +9879,11 @@ shallow-clone@^3.0.0:
|
||||
dependencies:
|
||||
kind-of "^6.0.2"
|
||||
|
||||
shape-detector@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/shape-detector/-/shape-detector-0.2.1.tgz#d69acf8a5f595100fee08b2d69d6b5c74d887e1e"
|
||||
integrity sha1-1prPil9ZUQD+4Istada1x02Ifh4=
|
||||
|
||||
shebang-command@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
|
||||
|
Loading…
x
Reference in New Issue
Block a user