decent drawing tools

This commit is contained in:
caranicas 2022-09-17 11:58:21 -04:00
parent 8b48ad77e8
commit 346d1dddba
5 changed files with 237 additions and 76 deletions

View File

@ -2,19 +2,16 @@ import { style, globalStyle } from "@vanilla-extract/css";
export const DrawImageMain = style({
position: "relative",
width: "512px",
height: "512px",
});
globalStyle(`${DrawImageMain} > canvas`, {
position: "absolute",
top: "0",
left: "0",
opacity: "0.5",
opacity: ".5",
});
globalStyle(`${DrawImageMain} > img`, {
position: "absolute",
top: "0",
left: "0",
});

View File

@ -1,22 +1,27 @@
// @ts-nocheck
import React, { useRef, useState } from "react";
import React, { useRef, useState, useCallback, useEffect } from "react";
// https://github.com/embiem/react-canvas-draw
type DrawImageProps = {
imageData: string;
brushSize: string;
brushShape: string;
brushColor: string;
isErasing: boolean;
};
import {
DrawImageMain, //@ts-ignore
} from "./drawImage.css.ts";
export default function DrawImage({ imageData }: DrawImageProps) {
export default function DrawImage({ imageData, brushSize, brushShape, brushColor, isErasing }: DrawImageProps) {
const drawingRef = useRef<HTMLCanvasElement>(null);
const cursorRef = useRef<HTMLCanvasElement>(null);
const [isDrawing, setIsDrawing] = useState(false);
const [isUpdating, setIsUpdating] = useState(false);
const _handleMouseDown = (
e: React.MouseEvent<HTMLCanvasElement, MouseEvent>
@ -27,95 +32,100 @@ export default function DrawImage({ imageData }: DrawImageProps) {
nativeEvent: { offsetX, offsetY },
} = e;
setIsDrawing(true);
setIsUpdating(true);
};
const _handleMouseUp = (
e: React.MouseEvent<HTMLCanvasElement, MouseEvent>
) => {
setIsDrawing(false);
setIsUpdating(false);
const canvas = drawingRef.current;
if (canvas) {
const data = canvas.toDataURL();
console.log("data", data);
// TODO: SEND THIS TO THE STATE
}
};
const _handleMouseMove = (
e: React.MouseEvent<HTMLCanvasElement, MouseEvent>
) => {
if (isDrawing) {
const canvas = drawingRef.current;
if (canvas) {
const ctx = canvas.getContext("2d");
ctx.strokeStyle = "red";
const {
nativeEvent: { offsetX, offsetY },
} = e;
const _drawCanvas = (x, y, brushSize, brushShape, brushColor) => {
const canvas = drawingRef.current;
if (canvas) {
const ctx = canvas.getContext("2d");
if (isErasing) {
// stack overflow https://stackoverflow.com/questions/10396991/clearing-circular-regions-from-html5-canvas
const offset = brushSize / 2;
ctx.clearRect(x - offset, y - offset, brushSize, brushSize);
} else {
ctx.beginPath();
ctx.lineWidth = 20;
// Sets the end of the lines drawn
// to a round shape.
ctx.lineCap = "round";
ctx.strokeStyle = "white";
// The cursor to start drawing
// moves to this coordinate
ctx.moveTo(offsetX, offsetY);
// A line is traced from start
// coordinate to this coordinate
ctx.lineTo(offsetX, offsetY);
// Draws the line.
ctx.lineWidth = brushSize;
ctx.lineCap = brushShape;
ctx.strokeStyle = brushColor;
ctx.moveTo(x, y);
ctx.lineTo(x, y);
ctx.stroke();
}
}
};
const _handleCursorMove = (
e: React.MouseEvent<HTMLCanvasElement, MouseEvent>
) => {
console.log("cursor move");
const _drawCursor = (x, y, brushSize, brushShape, brushColor) => {
const canvas = cursorRef.current;
if (canvas) {
const ctx = canvas.getContext("2d");
ctx.strokeStyle = "red";
const {
nativeEvent: { offsetX, offsetY },
} = e;
ctx.beginPath();
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.lineWidth = 20;
if (isErasing) {
const offset = brushSize / 2;
// draw a quare outline
ctx.lineWidth = 2;
ctx.lineCap = 'butt';
ctx.strokeStyle = brushColor;
ctx.moveTo(x - offset, y - offset);
ctx.lineTo(x + offset, y - offset);
ctx.lineTo(x + offset, y + offset);
ctx.lineTo(x - offset, y + offset);
ctx.lineTo(x - offset, y - offset);
ctx.stroke();
// Sets the end of the lines drawn
// to a round shape.
ctx.lineCap = "round";
} else {
ctx.strokeStyle = "white";
// The cursor to start drawing
// moves to this coordinate
ctx.moveTo(offsetX, offsetY);
ctx.lineWidth = brushSize;
ctx.lineCap = brushShape;
ctx.strokeStyle = brushColor;
ctx.moveTo(x, y);
ctx.lineTo(x, y);
ctx.stroke();
}
}
// A line is traced from start
// coordinate to this coordinate
ctx.lineTo(offsetX, offsetY);
};
// Draws the line.
ctx.stroke();
const _handleMouseMove = (
e: React.MouseEvent<HTMLCanvasElement, MouseEvent>
) => {
const {
nativeEvent: { offsetX: x, offsetY: y },
} = e;
_drawCursor(x, y, brushSize, brushShape, brushColor);
if (isUpdating) {
_drawCanvas(x, y, brushSize, brushShape, brushColor);
}
};
// function for external use
const fillCanvas = () => {
const canvas = drawingRef.current;
if (canvas) {
const ctx = canvas.getContext("2d");
ctx.fillStyle = brushColor;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
};
return (
<div className={DrawImageMain}>
@ -124,17 +134,15 @@ export default function DrawImage({ imageData }: DrawImageProps) {
ref={drawingRef}
width={512}
height={512}
onMouseDown={_handleMouseDown}
onMouseMove={_handleMouseMove}
onMouseUp={_handleMouseUp}
></canvas>
<canvas
ref={cursorRef}
width={512}
height={512}
onMouseMove={_handleCursorMove}
onMouseDown={_handleMouseDown}
onMouseUp={_handleMouseUp}
onMouseMove={_handleMouseMove}
></canvas>
</div>
);
}

View File

@ -12,8 +12,6 @@ export const InpaintingSlider = style({
position: "absolute",
top: "10px",
left: "400px",
width: "200px",
height: "20px",
zIndex: 1,
backgroundColor: "rgba(0, 0, 0, 0.5)",
});

View File

@ -1,19 +1,148 @@
import React from "react";
import React, { useRef, useState, ChangeEvent } from "react";
import DrawImage from "../../../molecules/drawImage";
import { useImageCreate } from "../../../../stores/imageCreateStore";
import {
InpaintingPanelMain,
InpaintingControls,
InpaintingControlRow,
} from // @ts-ignore
"./inpaintingPanel.css.ts";
export default function InpaintingPanel() {
// no idea if this is the right typing
const drawingRef = useRef(null);
const [brushSize, setBrushSize] = useState('20');
const [brushShape, setBrushShape] = useState("round");
const [brushColor, setBrushColor] = useState("#fff");
const [isErasing, setIsErasing] = useState(false);
const init_image = useImageCreate((state) =>
state.getValueForRequestKey("init_image")
);
const _handleBrushMask = () => {
setIsErasing(false);
};
const _handleBrushErase = () => {
setIsErasing(true);
};
const _handleFillMask = () => {
console.log("fill mask!!", drawingRef);
// drawingRef.current?.fillCanvas();
};
const _handleClearAll = () => {
console.log("clear all");
};
const _handleBrushSize = (e: ChangeEvent<HTMLInputElement>) => {
setBrushSize(e.target.value);
};
// const _handleBrushShape = (e: ) => {
// console.log("brush shape", e.target.value);
// setBrushShape(e.target.value);
// };
const _handleBrushShape = (e: ChangeEvent<HTMLInputElement>) => {
console.log("brush shape", e.target.value);
setBrushShape(e.target.value);
};
const _handleBrushColor = (e: ChangeEvent<HTMLInputElement>) => {
console.log("brush color", e.target.value);
setBrushColor(e.target.value);
};
return (
<div>
<DrawImage imageData={init_image} />
<div className={InpaintingPanelMain}>
<DrawImage
// ref={drawingRef}
imageData={init_image}
brushSize={brushSize}
brushShape={brushShape}
brushColor={brushColor}
isErasing={isErasing}
/>
<div className={InpaintingControls}>
<div className={InpaintingControlRow}>
<button
onClick={_handleBrushMask}
>
Mask
</button>
<button
onClick={_handleBrushErase}
>
Erase
</button>
<button
disabled
onClick={_handleFillMask}
>
Fill
</button>
<button
disabled
onClick={_handleClearAll}
>
Clear
</button>
<label
>
Brush Size
<input
type="range"
min="1"
max="100"
value={brushSize}
onChange={_handleBrushSize}
>
</input>
</label>
</div>
<div className={InpaintingControlRow}>
<button
value={"round"}
onClick={_handleBrushShape}
>
Cirle Brush
</button>
<button
value={"square"}
onClick={_handleBrushShape}
>
Square Brush
</button>
<button
value={"#000"}
onClick={_handleBrushColor}
>
Dark Brush
</button>
<button
value={"#fff"}
onClick={_handleBrushColor}
>
Light Brush
</button>
</div>
</div>
</div>
);
};

View File

@ -0,0 +1,29 @@
import { style } from '@vanilla-extract/css'
export const InpaintingPanelMain = style({
position: 'relative',
width: '100%',
height: '100%',
padding: '10px 10px',
});
export const InpaintingControls = style({
display: 'flex',
flexDirection: 'row',
width: '100%',
flexWrap: 'wrap',
});
export const InpaintingControlRow = style({
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-evenly',
alignItems: 'center',
width: '100%',
':first-of-type': {
margin: '10px 0',
},
});