Improves draw tool

This commit is contained in:
Steve Ruiz 2021-05-27 23:07:27 +01:00
parent bd02ca4fd9
commit 8dfef5c302
9 changed files with 78 additions and 72 deletions

View file

@ -5,7 +5,7 @@ import { useRef } from "react"
import { IconButton } from "components/shared" import { IconButton } from "components/shared"
import { Circle, Trash, X } from "react-feather" import { Circle, Trash, X } from "react-feather"
import { deepCompare, deepCompareArrays, getSelectedShapes } from "utils/utils" import { deepCompare, deepCompareArrays, getSelectedShapes } from "utils/utils"
import { shades, fills, strokes } from "state/data" import { shades, fills, strokes } from "lib/colors"
import ColorPicker from "./color-picker" import ColorPicker from "./color-picker"
import AlignDistribute from "./align-distribute" import AlignDistribute from "./align-distribute"

38
lib/colors.ts Normal file
View file

@ -0,0 +1,38 @@
export const shades = {
transparent: "transparent",
white: "rgba(248, 249, 250, 1.000)",
lightGray: "rgba(224, 226, 230, 1.000)",
gray: "rgba(172, 181, 189, 1.000)",
darkGray: "rgba(52, 58, 64, 1.000)",
black: "rgba(0,0,0, 1.000)",
}
export const strokes = {
lime: "rgba(115, 184, 23, 1.000)",
green: "rgba(54, 178, 77, 1.000)",
teal: "rgba(9, 167, 120, 1.000)",
cyan: "rgba(14, 152, 173, 1.000)",
blue: "rgba(28, 126, 214, 1.000)",
indigo: "rgba(66, 99, 235, 1.000)",
violet: "rgba(112, 72, 232, 1.000)",
grape: "rgba(174, 62, 200, 1.000)",
pink: "rgba(214, 51, 108, 1.000)",
red: "rgba(240, 63, 63, 1.000)",
orange: "rgba(247, 103, 6, 1.000)",
yellow: "rgba(245, 159, 0, 1.000)",
}
export const fills = {
lime: "rgba(217, 245, 162, 1.000)",
green: "rgba(177, 242, 188, 1.000)",
teal: "rgba(149, 242, 215, 1.000)",
cyan: "rgba(153, 233, 242, 1.000)",
blue: "rgba(166, 216, 255, 1.000)",
indigo: "rgba(186, 200, 255, 1.000)",
violet: "rgba(208, 191, 255, 1.000)",
grape: "rgba(237, 190, 250, 1.000)",
pink: "rgba(252, 194, 215, 1.000)",
red: "rgba(255, 201, 201, 1.000)",
orange: "rgba(255, 216, 168, 1.000)",
yellow: "rgba(255, 236, 153, 1.000)",
}

View file

@ -4,8 +4,14 @@ import { DrawShape, ShapeType } from "types"
import { registerShapeUtils } from "./index" import { registerShapeUtils } from "./index"
import { intersectPolylineBounds } from "utils/intersections" import { intersectPolylineBounds } from "utils/intersections"
import { boundsContainPolygon } from "utils/bounds" import { boundsContainPolygon } from "utils/bounds"
import { getBoundsFromPoints, translateBounds } from "utils/utils" import getStroke from "perfect-freehand"
import {
getBoundsFromPoints,
getSvgPathFromStroke,
translateBounds,
} from "utils/utils"
import { DotCircle } from "components/canvas/misc" import { DotCircle } from "components/canvas/misc"
import { shades } from "lib/colors"
const pathCache = new WeakMap<DrawShape, string>([]) const pathCache = new WeakMap<DrawShape, string>([])
@ -29,7 +35,7 @@ const draw = registerShapeUtils<DrawShape>({
strokeLinecap: "round", strokeLinecap: "round",
strokeLinejoin: "round", strokeLinejoin: "round",
...props.style, ...props.style,
fill: "transparent", stroke: "transparent",
}, },
} }
}, },
@ -42,23 +48,7 @@ const draw = registerShapeUtils<DrawShape>({
} }
if (!pathCache.has(shape)) { if (!pathCache.has(shape)) {
pathCache.set( pathCache.set(shape, getSvgPathFromStroke(getStroke(points)))
shape,
points
.reduce(
(acc, [x0, y0], i, arr) => {
if (i === points.length - 1) {
acc.push("L", x0, y0)
} else {
const [x1, y1] = arr[i + 1]
acc.push(x0, y0, (x0 + x1) / 2, (y0 + y1) / 2)
}
return acc
},
["M", ...points[0], "Q"]
)
.join(" ")
)
} }
return <path id={id} d={pathCache.get(shape)} /> return <path id={id} d={pathCache.get(shape)} />

View file

@ -16,7 +16,7 @@
"framer-motion": "^4.1.16", "framer-motion": "^4.1.16",
"ismobilejs": "^1.1.1", "ismobilejs": "^1.1.1",
"next": "10.2.0", "next": "10.2.0",
"perfect-freehand": "^0.4.7", "perfect-freehand": "^0.4.71",
"prettier": "^2.3.0", "prettier": "^2.3.0",
"react": "17.0.2", "react": "17.0.2",
"react-dom": "17.0.2", "react-dom": "17.0.2",

View file

@ -11,7 +11,6 @@ export default function drawCommand(
before: number[][], before: number[][],
after: number[][] after: number[][]
) { ) {
const selectedIds = Array.from(data.selectedIds.values())
const restoreShape = current(getPage(data).shapes[id]) const restoreShape = current(getPage(data).shapes[id])
getShapeUtils(restoreShape).setPoints!(restoreShape, after) getShapeUtils(restoreShape).setPoints!(restoreShape, after)
@ -31,9 +30,6 @@ export default function drawCommand(
undo(data) { undo(data) {
delete getPage(data).shapes[id] delete getPage(data).shapes[id]
data.selectedIds.clear() data.selectedIds.clear()
for (let id of selectedIds) {
data.selectedIds.add(id)
}
}, },
}) })
) )

View file

@ -1,44 +1,6 @@
import { Data, ShapeType } from "types" import { Data, ShapeType } from "types"
import shapeUtils from "lib/shape-utils" import shapeUtils from "lib/shape-utils"
import { shades } from "lib/colors"
export const shades = {
transparent: "transparent",
white: "rgba(248, 249, 250, 1.000)",
lightGray: "rgba(224, 226, 230, 1.000)",
gray: "rgba(172, 181, 189, 1.000)",
darkGray: "rgba(52, 58, 64, 1.000)",
black: "rgba(0,0,0, 1.000)",
}
export const strokes = {
lime: "rgba(115, 184, 23, 1.000)",
green: "rgba(54, 178, 77, 1.000)",
teal: "rgba(9, 167, 120, 1.000)",
cyan: "rgba(14, 152, 173, 1.000)",
blue: "rgba(28, 126, 214, 1.000)",
indigo: "rgba(66, 99, 235, 1.000)",
violet: "rgba(112, 72, 232, 1.000)",
grape: "rgba(174, 62, 200, 1.000)",
pink: "rgba(214, 51, 108, 1.000)",
red: "rgba(240, 63, 63, 1.000)",
orange: "rgba(247, 103, 6, 1.000)",
yellow: "rgba(245, 159, 0, 1.000)",
}
export const fills = {
lime: "rgba(217, 245, 162, 1.000)",
green: "rgba(177, 242, 188, 1.000)",
teal: "rgba(149, 242, 215, 1.000)",
cyan: "rgba(153, 233, 242, 1.000)",
blue: "rgba(166, 216, 255, 1.000)",
indigo: "rgba(186, 200, 255, 1.000)",
violet: "rgba(208, 191, 255, 1.000)",
grape: "rgba(237, 190, 250, 1.000)",
pink: "rgba(252, 194, 215, 1.000)",
red: "rgba(255, 201, 201, 1.000)",
orange: "rgba(255, 216, 168, 1.000)",
yellow: "rgba(255, 236, 153, 1.000)",
}
export const defaultDocument: Data["document"] = { export const defaultDocument: Data["document"] = {
pages: { pages: {

View file

@ -1,7 +1,8 @@
import { createSelectorHook, createState } from "@state-designer/react" import { createSelectorHook, createState } from "@state-designer/react"
import * as vec from "utils/vec" import * as vec from "utils/vec"
import inputs from "./inputs" import inputs from "./inputs"
import { shades, defaultDocument } from "./data" import { defaultDocument } from "./data"
import { shades } from "lib/colors"
import { createShape, getShapeUtils } from "lib/shape-utils" import { createShape, getShapeUtils } from "lib/shape-utils"
import history from "state/history" import history from "state/history"
import * as Sessions from "./sessions" import * as Sessions from "./sessions"
@ -92,6 +93,7 @@ const state = createState({
do: "zoomCameraToSelectionActual", do: "zoomCameraToSelectionActual",
else: "zoomCameraToActual", else: "zoomCameraToActual",
}, },
SELECTED_ALL: { to: "selecting", do: "selectAll" },
}, },
initial: "loading", initial: "loading",
states: { states: {
@ -133,7 +135,6 @@ const state = createState({
states: { states: {
notPointing: { notPointing: {
on: { on: {
SELECTED_ALL: "selectAll",
POINTED_CANVAS: { to: "brushSelecting" }, POINTED_CANVAS: { to: "brushSelecting" },
POINTED_BOUNDS: { to: "pointingBounds" }, POINTED_BOUNDS: { to: "pointingBounds" },
POINTED_BOUNDS_HANDLE: { POINTED_BOUNDS_HANDLE: {
@ -262,7 +263,10 @@ const state = createState({
editing: { editing: {
onEnter: "startDrawSession", onEnter: "startDrawSession",
on: { on: {
STOPPED_POINTING: { do: "completeSession", to: "selecting" }, STOPPED_POINTING: {
do: "completeSession",
to: "draw.creating",
},
CANCELLED: { CANCELLED: {
do: ["cancelSession", "deleteSelectedIds"], do: ["cancelSession", "deleteSelectedIds"],
to: "selecting", to: "selecting",
@ -927,7 +931,7 @@ const state = createState({
}, },
restoreSavedData(data) { restoreSavedData(data) {
// history.load(data) history.load(data)
}, },
clearBoundsRotation(data) { clearBoundsRotation(data) {

View file

@ -1530,3 +1530,19 @@ export function simplify(points: number[][], tolerance = 1) {
return [a, b] return [a, b]
} }
export function getSvgPathFromStroke(stroke: number[][]) {
if (!stroke.length) return ""
const d = stroke.reduce(
(acc, [x0, y0], i, arr) => {
const [x1, y1] = arr[(i + 1) % arr.length]
acc.push(x0, y0, (x0 + x1) / 2, (y0 + y1) / 2)
return acc
},
["M", ...stroke[0], "Q"]
)
d.push("Z")
return d.join(" ")
}

View file

@ -6392,10 +6392,10 @@ pend@~1.2.0:
resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA=
perfect-freehand@^0.4.7: perfect-freehand@^0.4.71:
version "0.4.7" version "0.4.71"
resolved "https://registry.yarnpkg.com/perfect-freehand/-/perfect-freehand-0.4.7.tgz#4d85fd64881ba81b2a4eaa6ac4e8983ccb21dd43" resolved "https://registry.yarnpkg.com/perfect-freehand/-/perfect-freehand-0.4.71.tgz#b98ffc3cbc4e3cd930528e8d74a8849ee77475fb"
integrity sha512-SSSFL8VzXiOHQdUTyNyOb0JC+btVZRy9bi6jos7Nb7PBTI0PHX5jM6RgCTSrubQ8Ul9qOYWmWgJBrwVGHwyJZQ== integrity sha512-bJ3w2E6WcUfZJTXWPlS7DI6FIT9rRIYSCXgDYjvST8sAe/c+zNnJnlfJp3q8Hk1uPt9dH7bFuj8Sico6CKZw7A==
performance-now@^2.1.0: performance-now@^2.1.0:
version "2.1.0" version "2.1.0"