Improves draw tool
This commit is contained in:
parent
bd02ca4fd9
commit
8dfef5c302
9 changed files with 78 additions and 72 deletions
|
@ -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
38
lib/colors.ts
Normal 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)",
|
||||||
|
}
|
|
@ -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)} />
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
|
@ -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: {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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(" ")
|
||||||
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Reference in a new issue