|
@@ -3,7 +3,7 @@ import { useEffect, useRef, useState } from 'react' |
|
|
import { FaRedo, FaUndo } from 'react-icons/fa' |
|
|
import { FaRedo, FaUndo } from 'react-icons/fa' |
|
|
import { Layer, Line, Rect, Stage, Image } from 'react-konva' |
|
|
import { Layer, Line, Rect, Stage, Image } from 'react-konva' |
|
|
|
|
|
|
|
|
import type { KonvaEventObject } from 'konva' |
|
|
|
|
|
|
|
|
import type Konva from 'konva' |
|
|
import type { ChangeEventHandler } from 'react' |
|
|
import type { ChangeEventHandler } from 'react' |
|
|
|
|
|
|
|
|
type ImageItem = { src: string } |
|
|
type ImageItem = { src: string } |
|
@@ -23,34 +23,35 @@ type Line = { |
|
|
strokeWidth: number } |
|
|
strokeWidth: number } |
|
|
|
|
|
|
|
|
const Mode = { |
|
|
const Mode = { |
|
|
Pen: Symbol (), |
|
|
|
|
|
Rubber: Symbol (), |
|
|
|
|
|
Image: Symbol () } as const |
|
|
|
|
|
type Mode = keyof Mode |
|
|
|
|
|
|
|
|
Pen: 'Pen', |
|
|
|
|
|
Rubber: 'Rubber', |
|
|
|
|
|
Image: 'Image' } as const |
|
|
|
|
|
type Mode = (typeof Mode)[keyof typeof Mode] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export default () => { |
|
|
export default () => { |
|
|
const drawingRef = useRef (false) |
|
|
const drawingRef = useRef (false) |
|
|
const fileInputRef = useRef<HTMLInputElement> (null) |
|
|
const fileInputRef = useRef<HTMLInputElement> (null) |
|
|
const stageRef = useRef<Stage | null> (null) |
|
|
|
|
|
|
|
|
const stageRef = useRef<Konva.Stage | null> (null) |
|
|
|
|
|
|
|
|
const [activeLayerId, setActiveLayerId] = useState<string | null> (null) |
|
|
const [activeLayerId, setActiveLayerId] = useState<string | null> (null) |
|
|
const [colour, setColour] = useState ('#000000') |
|
|
const [colour, setColour] = useState ('#000000') |
|
|
const [images, setImages] = useState<Record<string, window.Image[]>> ({ }) |
|
|
|
|
|
const [layers, setLayers] = useState<Layer[]> ([]) |
|
|
|
|
|
|
|
|
const [images, setImages] = useState<Record<string, HTMLImageElement>> ({ }) |
|
|
const [layerCnt, setLayerCnt] = useState (0) |
|
|
const [layerCnt, setLayerCnt] = useState (0) |
|
|
|
|
|
const [layers, setLayers] = useState<Layer[]> ([]) |
|
|
const [mode, setMode] = useState<Mode> (Mode.Pen) |
|
|
const [mode, setMode] = useState<Mode> (Mode.Pen) |
|
|
const [pointSize, setPointSize] = useState (3) |
|
|
const [pointSize, setPointSize] = useState (3) |
|
|
const [stageWidth, setStageWidth] = useState (480) |
|
|
|
|
|
const [stageHeight, setStageHeight] = useState (480) |
|
|
const [stageHeight, setStageHeight] = useState (480) |
|
|
|
|
|
const [stageWidth, setStageWidth] = useState (480) |
|
|
|
|
|
|
|
|
const activeLayer = layers.find (l => l.id === activeLayerId) |
|
|
const activeLayer = layers.find (l => l.id === activeLayerId) |
|
|
|
|
|
|
|
|
const handleMouseDown = (ev: KonvaEventObject<MouseEvent>) => { |
|
|
|
|
|
drawingRef.current = true |
|
|
|
|
|
|
|
|
const handleMouseDown = (ev: Konva.KonvaEventObject<MouseEvent | TouchEvent>) => { |
|
|
const pos = ev.target.getStage ()?.getPointerPosition () |
|
|
const pos = ev.target.getStage ()?.getPointerPosition () |
|
|
if (!(pos)) |
|
|
|
|
|
|
|
|
if (!(pos) || !(activeLayer)) |
|
|
return |
|
|
return |
|
|
|
|
|
|
|
|
|
|
|
drawingRef.current = true |
|
|
const lines: Line[] = [ |
|
|
const lines: Line[] = [ |
|
|
...activeLayer.lines, |
|
|
...activeLayer.lines, |
|
|
{ mode, |
|
|
{ mode, |
|
@@ -60,9 +61,10 @@ export default () => { |
|
|
updateActiveLayerLines (lines) |
|
|
updateActiveLayerLines (lines) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const handleMouseMove = (ev: KonvaEventObject<MouseEvent>) => { |
|
|
|
|
|
if (!(drawingRef.current)) |
|
|
|
|
|
|
|
|
const handleMouseMove = (ev: Konva.KonvaEventObject<MouseEvent | TouchEvent>) => { |
|
|
|
|
|
if (!(drawingRef.current) || !(activeLayer)) |
|
|
return |
|
|
return |
|
|
|
|
|
|
|
|
const stage = ev.target.getStage () |
|
|
const stage = ev.target.getStage () |
|
|
const point = stage?.getPointerPosition () |
|
|
const point = stage?.getPointerPosition () |
|
|
if (!(point)) |
|
|
if (!(point)) |
|
@@ -80,17 +82,17 @@ export default () => { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const handleUndo = () => { |
|
|
const handleUndo = () => { |
|
|
if (activeLayer.lines.length === 0) |
|
|
|
|
|
|
|
|
if (!(activeLayer) || activeLayer.lines.length === 0) |
|
|
return |
|
|
return |
|
|
const newLines = [...activeLayer.lines] |
|
|
const newLines = [...activeLayer.lines] |
|
|
const last = newLines.pop()! |
|
|
|
|
|
|
|
|
newLines.pop() |
|
|
updateActiveLayerHistory ([...activeLayer.history, activeLayer.lines]) |
|
|
updateActiveLayerHistory ([...activeLayer.history, activeLayer.lines]) |
|
|
updateActiveLayerFuture ([]) |
|
|
updateActiveLayerFuture ([]) |
|
|
updateActiveLayerLines (newLines) |
|
|
updateActiveLayerLines (newLines) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const handleRedo = () => { |
|
|
const handleRedo = () => { |
|
|
if (activeLayer.history.length === 0) |
|
|
|
|
|
|
|
|
if (!(activeLayer) || activeLayer.history.length === 0) |
|
|
return |
|
|
return |
|
|
const prev = activeLayer.history[activeLayer.history.length - 1] |
|
|
const prev = activeLayer.history[activeLayer.history.length - 1] |
|
|
updateActiveLayerLines (prev) |
|
|
updateActiveLayerLines (prev) |
|
@@ -181,9 +183,14 @@ export default () => { |
|
|
useEffect (() => { |
|
|
useEffect (() => { |
|
|
try |
|
|
try |
|
|
{ |
|
|
{ |
|
|
const paint = JSON.parse (localStorage.getItem ('paint')) |
|
|
|
|
|
|
|
|
const paintJSON = localStorage.getItem ('paint') |
|
|
|
|
|
if (paintJSON == null) |
|
|
|
|
|
throw new Error |
|
|
|
|
|
|
|
|
|
|
|
const paint = JSON.parse (paintJSON) |
|
|
if (!(paint instanceof Array)) |
|
|
if (!(paint instanceof Array)) |
|
|
throw new Error |
|
|
throw new Error |
|
|
|
|
|
|
|
|
setLayers (paint) |
|
|
setLayers (paint) |
|
|
setActiveLayerId (paint[0].id) |
|
|
setActiveLayerId (paint[0].id) |
|
|
} |
|
|
} |
|
@@ -254,7 +261,7 @@ export default () => { |
|
|
min={32} |
|
|
min={32} |
|
|
max={640} |
|
|
max={640} |
|
|
value={stageWidth} |
|
|
value={stageWidth} |
|
|
onChange={ev => setStageWidth (ev.target.value)} /> |
|
|
|
|
|
|
|
|
onChange={ev => setStageWidth (+ev.target.value)} /> |
|
|
</label> |
|
|
</label> |
|
|
<label> |
|
|
<label> |
|
|
高さ: |
|
|
高さ: |
|
@@ -262,7 +269,7 @@ export default () => { |
|
|
min={24} |
|
|
min={24} |
|
|
max={480} |
|
|
max={480} |
|
|
value={stageHeight} |
|
|
value={stageHeight} |
|
|
onChange={ev => setStageHeight (ev.target.value)} /> |
|
|
|
|
|
|
|
|
onChange={ev => setStageHeight (+ev.target.value)} /> |
|
|
</label> |
|
|
</label> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
@@ -363,7 +370,6 @@ export default () => { |
|
|
<div className="border border-black dark:border-white"> |
|
|
<div className="border border-black dark:border-white"> |
|
|
<Stage ref={node => { |
|
|
<Stage ref={node => { |
|
|
stageRef.current = node |
|
|
stageRef.current = node |
|
|
return node |
|
|
|
|
|
}} |
|
|
}} |
|
|
width={stageWidth} |
|
|
width={stageWidth} |
|
|
height={stageHeight} |
|
|
height={stageHeight} |
|
|