This commit is contained in:
@@ -116,6 +116,7 @@ const isLayer = (obj: unknown): obj is Layer => (
|
|||||||
export default () => {
|
export default () => {
|
||||||
const drawingRef = useRef (false)
|
const drawingRef = useRef (false)
|
||||||
const fileInputRef = useRef<HTMLInputElement> (null)
|
const fileInputRef = useRef<HTMLInputElement> (null)
|
||||||
|
const layersRef = useRef<Record<string, Konva.Layer>> ({ })
|
||||||
const stageRef = useRef<Konva.Stage | null> (null)
|
const stageRef = useRef<Konva.Stage | null> (null)
|
||||||
|
|
||||||
const [activeLayerId, setActiveLayerId] = useState<string | null> (null)
|
const [activeLayerId, setActiveLayerId] = useState<string | null> (null)
|
||||||
@@ -125,6 +126,7 @@ export default () => {
|
|||||||
const [layers, setLayers] = useState<Layer[]> ([])
|
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 [singleLayer, setSingleLayer] = useState (false)
|
||||||
const [stageHeight, setStageHeight] = useState (480)
|
const [stageHeight, setStageHeight] = useState (480)
|
||||||
const [stageWidth, setStageWidth] = useState (480)
|
const [stageWidth, setStageWidth] = useState (480)
|
||||||
|
|
||||||
@@ -210,37 +212,31 @@ export default () => {
|
|||||||
fileInputRef.current.value = ''
|
fileInputRef.current.value = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlePaint = async () => {
|
const handlePaint = (ev: Konva.KonvaEventObject<PointerEvent | MouseEvent | TouchEvent>) => {
|
||||||
const layer = activeLayer
|
const layer = activeLayer
|
||||||
if (!(layer) || !(stageRef.current))
|
if (!(layer) || !(layersRef.current?.[layer.id]))
|
||||||
return
|
return
|
||||||
|
|
||||||
const stage = stageRef.current
|
const pos = ev.target.getStage ()?.getPointerPosition ()
|
||||||
const pos = stage.getPointerPosition ()
|
|
||||||
if (!(pos))
|
if (!(pos))
|
||||||
return
|
return
|
||||||
|
|
||||||
const dataURL = stage.toDataURL ({ mimeType: 'image/png', pixelRatio: 1 })
|
const stage = layersRef.current[layer.id]
|
||||||
|
if (!(stage))
|
||||||
|
return
|
||||||
|
|
||||||
const img = new window.Image
|
if (drawingRef.current)
|
||||||
img.crossOrigin = 'anonymous'
|
return
|
||||||
await new Promise<void> (res => {
|
|
||||||
img.onload = () => res ()
|
|
||||||
img.src = dataURL
|
|
||||||
})
|
|
||||||
|
|
||||||
const $off = document.createElement ('canvas')
|
drawingRef.current = true
|
||||||
$off.width = stageWidth
|
|
||||||
$off.height = stageHeight
|
const off = stage.toCanvas ({ pixelRatio: 1 })
|
||||||
const ctx = $off.getContext ('2d')
|
const ctx = off.getContext ('2d')!
|
||||||
ctx!.drawImage (img, 0, 0, stageWidth, stageHeight)
|
const imgData = ctx.getImageData (0, 0, off.width, off.height)
|
||||||
|
|
||||||
const imgData = ctx!.getImageData (0, 0, stageWidth, stageHeight)
|
|
||||||
const [r, g, b, a] = hexToRgba (colour, 255)
|
const [r, g, b, a] = hexToRgba (colour, 255)
|
||||||
floodFill (imgData, Math.floor (pos.x), Math.floor (pos.y), { r, g, b, a })
|
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 nextSrc = $off.toDataURL ('image/png')
|
|
||||||
|
|
||||||
updateActiveLayerHistory ([...layer.history, layer.lines])
|
updateActiveLayerHistory ([...layer.history, layer.lines])
|
||||||
updateActiveLayerFuture ([])
|
updateActiveLayerFuture ([])
|
||||||
@@ -251,7 +247,9 @@ export default () => {
|
|||||||
{ mode: Mode.Paint,
|
{ mode: Mode.Paint,
|
||||||
points: [pos.x, pos.y],
|
points: [pos.x, pos.y],
|
||||||
stroke: colour,
|
stroke: colour,
|
||||||
imageSrc: nextSrc }] }) : l))
|
imageSrc: off.toDataURL ('image/png') }] }) : l))
|
||||||
|
|
||||||
|
drawingRef.current = false
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateActiveLayerLines = (lines: Line[]) => {
|
const updateActiveLayerLines = (lines: Line[]) => {
|
||||||
@@ -537,11 +535,19 @@ export default () => {
|
|||||||
追加
|
追加
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<input type="checkbox"
|
||||||
|
checked={singleLayer}
|
||||||
|
onChange={ev => setSingleLayer (ev.target.checked)} />
|
||||||
|
レイアを分けて表示
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="w-full flex items-center justify-center mb-16">
|
<div className="w-full flex items-center justify-center mb-16">
|
||||||
<div className="border border-black dark:border-white">
|
<div className="border border-black dark:border-white">
|
||||||
<Stage ref={node => {
|
<Stage className="touch-none"
|
||||||
|
ref={node => {
|
||||||
stageRef.current = node
|
stageRef.current = node
|
||||||
}}
|
}}
|
||||||
width={stageWidth}
|
width={stageWidth}
|
||||||
@@ -561,8 +567,11 @@ export default () => {
|
|||||||
listening={false} />
|
listening={false} />
|
||||||
</Layer>
|
</Layer>
|
||||||
|
|
||||||
{layers.map (layer => (
|
{layers.map (layer => (!(singleLayer) || layer.id === activeLayerId) && (
|
||||||
<Layer key={layer.id}>
|
<Layer key={layer.id} ref={node => {
|
||||||
|
if (node)
|
||||||
|
layersRef.current = { ...layersRef.current, [layer.id]: node }
|
||||||
|
}}>
|
||||||
{images[layer.id] && (
|
{images[layer.id] && (
|
||||||
<Image image={images[layer.id]}
|
<Image image={images[layer.id]}
|
||||||
x={0}
|
x={0}
|
||||||
@@ -587,7 +596,8 @@ export default () => {
|
|||||||
: 'source-over'} />)
|
: 'source-over'} />)
|
||||||
case Mode.Paint:
|
case Mode.Paint:
|
||||||
return (
|
return (
|
||||||
<Image image={images[line.imageSrc]}
|
<Image key={i}
|
||||||
|
image={images[line.imageSrc]}
|
||||||
x={0}
|
x={0}
|
||||||
y={0}
|
y={0}
|
||||||
scaleX={1}
|
scaleX={1}
|
||||||
|
|||||||
Reference in New Issue
Block a user