| @@ -17,11 +17,11 @@ type Layer = { | |||
| image?: ImageItem } | |||
| type Line = | |||
| | { mode: Mode.Pen | Mode.Rubber | |||
| | { mode: typeof Mode.Pen | typeof Mode.Rubber | |||
| points: number[] | |||
| stroke: string | |||
| strokeWidth: number } | |||
| | { mode: Mode.Paint | |||
| | { mode: typeof Mode.Paint | |||
| points: number[] | |||
| stroke: string, | |||
| imageSrc: string } | |||
| @@ -29,7 +29,6 @@ type Line = | |||
| const Mode = { | |||
| Pen: 'Pen', | |||
| Rubber: 'Rubber', | |||
| Image: 'Image', | |||
| Paint: 'Paint' } as const | |||
| type Mode = (typeof Mode)[keyof typeof Mode] | |||
| @@ -51,7 +50,7 @@ const floodFill = ( | |||
| const start = getRGBA (data, sx, sy, W) | |||
| const visited = new Uint8Array (W * H) | |||
| if (colourDiffMax (start, [fill.r, flil.g, fill.b, fill.a]) <= tolerance) | |||
| if (colourDiffMax (start, [fill.r, fill.g, fill.b, fill.a]) <= tolerance) | |||
| return | |||
| const stack: [number, number][] = [[sx, sy]] | |||
| @@ -99,7 +98,7 @@ const getRGBA = (data: Uint8ClampedArray, x: number, y: number, w: number) => { | |||
| const hexToRgba = (hex: string, alpha = 255): [number, number, number, number] => { | |||
| const m = /^#?([0-9a-f]{6})$/i.exec (hex) | |||
| const n = parseInt (m[1], 16) | |||
| const n = parseInt (m![1], 16) | |||
| return [(n >> 16) & 255, (n >> 8) & 255, n & 255, alpha] | |||
| } | |||
| @@ -110,21 +109,10 @@ const isLayer = (obj: unknown): obj is Layer => ( | |||
| && typeof (obj as Layer).id === 'string' | |||
| && typeof (obj as Layer).name === 'string' | |||
| && Array.isArray ((obj as Layer).lines) | |||
| && (obj as Layer).lines.every (isLine) | |||
| && Array.isArray ((obj as Layer).history) | |||
| && Array.isArray ((obj as Layer).future)) | |||
| const isLine = (obj: unknown): obj is Line => ( | |||
| typeof obj === 'object' | |||
| && obj !== null | |||
| && Array.isArray ((obj as Line).points) | |||
| && (obj as Line).points.every (n => typeof n === 'number') | |||
| && typeof (obj as Line).stroke === 'string' | |||
| && typeof (obj as Line).strokeWidth === 'number' | |||
| && Object.values (Mode).includes ((obj as Line).mode)) | |||
| export default () => { | |||
| const drawingRef = useRef (false) | |||
| const fileInputRef = useRef<HTMLInputElement> (null) | |||
| @@ -143,7 +131,7 @@ export default () => { | |||
| const activeLayer = layers.find (l => l.id === activeLayerId) | |||
| const handleMouseDown = (ev: Konva.KonvaEventObject<MouseEvent | TouchEvent>) => { | |||
| if (!(activeLayer)) | |||
| if (!(activeLayer) || (mode !== Mode.Pen && mode !== Mode.Rubber)) | |||
| return | |||
| drawingRef.current = true | |||
| @@ -222,7 +210,7 @@ export default () => { | |||
| fileInputRef.current.value = '' | |||
| } | |||
| const handlePaint = async (ev: Konva.KonvaEventObject<MouseEvent | TouchEvent>) => { | |||
| const handlePaint = async () => { | |||
| const layer = activeLayer | |||
| if (!(layer) || !(stageRef.current)) | |||
| return | |||
| @@ -245,14 +233,13 @@ export default () => { | |||
| $off.width = stageWidth | |||
| $off.height = stageHeight | |||
| const ctx = $off.getContext ('2d') | |||
| ctx.drawImage (img, 0, 0, stageWidth, stageHeight) | |||
| ctx!.drawImage (img, 0, 0, stageWidth, stageHeight) | |||
| const imgData = ctx.getImageData (0, 0, stageWidth, stageHeight) | |||
| const imgData = ctx!.getImageData (0, 0, stageWidth, stageHeight) | |||
| const [r, g, b, a] = hexToRgba (colour, 255) | |||
| floodFill (imgData, Math.floor (pos.x), Math.floor (pos.y), { r, g, b, a }) | |||
| ctx.putImageData (imgData, 0, 0) | |||
| ctx!.putImageData (imgData, 0, 0) | |||
| const prevImageSrc = layer.image?.src | |||
| const nextSrc = $off.toDataURL ('image/png') | |||
| updateActiveLayerHistory ([...layer.history, layer.lines]) | |||
| @@ -347,11 +334,7 @@ export default () => { | |||
| if (!(Array.isArray (parsed))) | |||
| throw new Error | |||
| const restored: Layer[] = parsed.filter (isLayer).map (l => ({ | |||
| ...l, lines: l.lines.map (s => ({ | |||
| ...s, mode: (Object.values (Mode).includes (s.mode) | |||
| ? s.mode | |||
| : Mode.Pen) })) })) | |||
| const restored: Layer[] = parsed.filter (isLayer) | |||
| if (restored.length === 0) | |||
| throw new Error | |||