みてるぞ 3 days ago
parent
commit
e6eeb88c14
1 changed files with 35 additions and 25 deletions
  1. +35
    -25
      frontend/src/components/threads/ThreadCanvas.tsx

+ 35
- 25
frontend/src/components/threads/ThreadCanvas.tsx View File

@@ -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 = stage.getPointerPosition ()
const pos = ev.target.getStage ()?.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
img.crossOrigin = 'anonymous'
await new Promise<void> (res => {
img.onload = () => res ()
img.src = dataURL
})
if (drawingRef.current)
return

drawingRef.current = true


const $off = document.createElement ('canvas')
$off.width = stageWidth
$off.height = stageHeight
const ctx = $off.getContext ('2d')
ctx!.drawImage (img, 0, 0, stageWidth, stageHeight)
const off = stage.toCanvas ({ pixelRatio: 1 })
const ctx = off.getContext ('2d')!
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)

const nextSrc = $off.toDataURL ('image/png')
ctx.putImageData (imgData, 0, 0)


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 => (
<Layer key={layer.id}>
{layers.map (layer => (!(singleLayer) || layer.id === activeLayerId) && (
<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}


Loading…
Cancel
Save