Adds support for solid / dotted / dashed draw lines
This commit is contained in:
parent
3b553e32fe
commit
8a747fe82f
5 changed files with 127 additions and 38 deletions
|
@ -2,6 +2,7 @@ import * as DropdownMenu from '@radix-ui/react-dropdown-menu'
|
|||
import * as RadioGroup from '@radix-ui/react-radio-group'
|
||||
import * as Panel from './panel'
|
||||
import styled from 'styles'
|
||||
import { forwardRef } from 'react'
|
||||
|
||||
export const breakpoints: any = { '@initial': 'mobile', '@sm': 'small' }
|
||||
|
||||
|
@ -367,9 +368,11 @@ export function BoxIcon({
|
|||
)
|
||||
}
|
||||
|
||||
export function IsFilledFillIcon(): JSX.Element {
|
||||
export const IsFilledFillIcon = forwardRef<SVGSVGElement, { color?: string }>(
|
||||
(props, ref) => {
|
||||
return (
|
||||
<svg
|
||||
ref={ref}
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
|
@ -386,7 +389,8 @@ export function IsFilledFillIcon(): JSX.Element {
|
|||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
export const ButtonsRow = styled('div', {
|
||||
position: 'relative',
|
||||
|
|
|
@ -182,7 +182,7 @@ class Inputs {
|
|||
|
||||
return (
|
||||
Date.now() - this.pointerUpTime < DOUBLE_CLICK_DURATION &&
|
||||
vec.dist(origin, point) < 8
|
||||
vec.dist(origin, point) < 4
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import { registerShapeUtils } from './register'
|
|||
|
||||
const rotatedCache = new WeakMap<DrawShape, number[][]>([])
|
||||
const pathCache = new WeakMap<DrawShape['points'], string>([])
|
||||
const simplePathCache = new WeakMap<DrawShape['points'], string>([])
|
||||
const polygonCache = new WeakMap<DrawShape['points'], string>([])
|
||||
|
||||
const draw = registerShapeUtils<DrawShape>({
|
||||
|
@ -47,6 +48,8 @@ const draw = registerShapeUtils<DrawShape>({
|
|||
|
||||
const styles = getShapeStyle(style)
|
||||
|
||||
const strokeWidth = +styles.strokeWidth
|
||||
|
||||
if (!pathCache.has(points)) {
|
||||
renderPath(shape, style)
|
||||
}
|
||||
|
@ -54,7 +57,7 @@ const draw = registerShapeUtils<DrawShape>({
|
|||
if (points.length > 0 && points.length < 3) {
|
||||
return (
|
||||
<g id={id}>
|
||||
<circle r={+styles.strokeWidth * 0.618} fill={styles.stroke} />
|
||||
<circle r={strokeWidth * 0.618} fill={styles.stroke} />
|
||||
</g>
|
||||
)
|
||||
}
|
||||
|
@ -63,6 +66,7 @@ const draw = registerShapeUtils<DrawShape>({
|
|||
points.length > 3 &&
|
||||
vec.dist(points[0], points[points.length - 1]) < +styles.strokeWidth * 2
|
||||
|
||||
if (shape.style.dash === DashStyle.Draw) {
|
||||
if (shouldFill && !polygonCache.has(points)) {
|
||||
renderFill(shape, style)
|
||||
}
|
||||
|
@ -80,6 +84,47 @@ const draw = registerShapeUtils<DrawShape>({
|
|||
<path d={pathCache.get(points)} fill={styles.stroke} />
|
||||
</g>
|
||||
)
|
||||
}
|
||||
|
||||
// For solid, dash and dotted lines, draw a regular stroke path
|
||||
|
||||
const strokeDasharray = {
|
||||
[DashStyle.Dotted]: `${strokeWidth / 10} ${strokeWidth * 3}`,
|
||||
[DashStyle.Dashed]: `${strokeWidth * 3} ${strokeWidth * 3}`,
|
||||
[DashStyle.Solid]: `none`,
|
||||
}[style.dash]
|
||||
|
||||
const strokeDashoffset = {
|
||||
[DashStyle.Dotted]: `-${strokeWidth / 20}`,
|
||||
[DashStyle.Dashed]: `-${strokeWidth}`,
|
||||
[DashStyle.Solid]: `none`,
|
||||
}[style.dash]
|
||||
|
||||
if (!simplePathCache.has(points)) {
|
||||
simplePathCache.set(points, getSolidStrokePath(points))
|
||||
}
|
||||
|
||||
const path = simplePathCache.get(points)
|
||||
|
||||
return (
|
||||
<g id={id}>
|
||||
{style.dash !== DashStyle.Solid && (
|
||||
<path
|
||||
d={path}
|
||||
strokeWidth={strokeWidth * 2}
|
||||
fill="transparent"
|
||||
stroke="transparent"
|
||||
/>
|
||||
)}
|
||||
<path
|
||||
d={path}
|
||||
strokeWidth={strokeWidth * 1.618}
|
||||
fill={shouldFill ? styles.fill : 'none'}
|
||||
strokeDasharray={strokeDasharray}
|
||||
strokeDashoffset={strokeDashoffset}
|
||||
/>
|
||||
</g>
|
||||
)
|
||||
},
|
||||
|
||||
getBounds(shape) {
|
||||
|
@ -168,12 +213,12 @@ const draw = registerShapeUtils<DrawShape>({
|
|||
return this
|
||||
},
|
||||
|
||||
applyStyles(shape, style) {
|
||||
const styles = { ...shape.style, ...style }
|
||||
styles.dash = DashStyle.Solid
|
||||
shape.style = styles
|
||||
return this
|
||||
},
|
||||
// applyStyles(shape, style) {
|
||||
// const styles = { ...shape.style, ...style }
|
||||
// styles.dash = DashStyle.Solid
|
||||
// shape.style = styles
|
||||
// return this
|
||||
// },
|
||||
|
||||
onSessionComplete(shape) {
|
||||
const bounds = this.getBounds(shape)
|
||||
|
@ -243,3 +288,43 @@ function renderFill(shape: DrawShape, style: ShapeStyles) {
|
|||
)
|
||||
)
|
||||
}
|
||||
|
||||
function getSolidStrokePath(stroke: number[][]) {
|
||||
let len = stroke.length
|
||||
|
||||
if (len === 0) return 'M 0 0 L 0 0'
|
||||
if (len < 3) return `M ${stroke[0][0]} ${stroke[0][1]}`
|
||||
|
||||
// Remove duplicates from points
|
||||
stroke = stroke.reduce<number[][]>((acc, pt, i) => {
|
||||
if (i === 0 || !vec.isEqual(pt, acc[i - 1])) {
|
||||
acc.push(pt)
|
||||
}
|
||||
return acc
|
||||
}, [])
|
||||
|
||||
len = stroke.length
|
||||
|
||||
const d = stroke.reduce(
|
||||
(acc, [x0, y0], i, arr) => {
|
||||
if (i === stroke.length - 1) {
|
||||
acc.push('L', x0, y0)
|
||||
return acc
|
||||
}
|
||||
|
||||
const [x1, y1] = arr[i + 1]
|
||||
acc.push(
|
||||
x0.toFixed(2),
|
||||
y0.toFixed(2),
|
||||
((x0 + x1) / 2).toFixed(2),
|
||||
((y0 + y1) / 2).toFixed(2)
|
||||
)
|
||||
return acc
|
||||
},
|
||||
['M', stroke[0][0], stroke[0][1], 'Q']
|
||||
)
|
||||
|
||||
const path = d.join(' ').replaceAll(/(\s[0-9]*\.[0-9]{2})([0-9]*)\b/g, '$1')
|
||||
|
||||
return path
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ const initialData: Data = {
|
|||
currentStyle: {
|
||||
size: SizeStyle.Medium,
|
||||
color: ColorStyle.Black,
|
||||
dash: DashStyle.Solid,
|
||||
dash: DashStyle.Draw,
|
||||
isFilled: false,
|
||||
},
|
||||
activeTool: 'select',
|
||||
|
|
|
@ -351,7 +351,7 @@ export default class Vec {
|
|||
}
|
||||
|
||||
static round = (a: number[], d = 5): number[] => {
|
||||
return a.map((v) => Number(v.toPrecision(d)))
|
||||
return a.map((v) => +v.toPrecision(d))
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue