<추가사항>
※returnCurrentLine 함수 추가 - 가장 최근에 쓴 선 삭제
※redrawCanvas 함수 추가 - 선 하나 삭제 시 paths 배열에 저장된 선들을 다시 그려서 캔버스에 복구
※path / setPath 추가 - 현재 그리는 선의 좌표들을 저장하는 배열의 변수 / 함수
※paths / setPaths 추가 - path들의 집합을 저장하는 배열의 변수 / 함수
※css 파일 다시 추가
가장 최근에 쓴 선을 삭제하는 기능
-forEach() : 배열을 순회해서 처리하는 데 사용되는 메서드로, 배열의 각 요소에 대해 주어진 함수를 순서대로 한 번씩 실행, map() 함수와 비슷한 기능을 가짐
page - <Canvas.js>
import React, { useEffect, useRef, useState } from "react";
import "../../css/canvas.css";
function Canvas() {
const canvasRef = useRef(null);
const [isDrawing, setIsDrawing] = useState(false);
const [lastX, setLastX] = useState(0);
const [lastY, setLastY] = useState(0);
const [outputImageSrc, setOutputImageSrc] = useState(null);
const [path, setPath] = useState([]); //현재 그리는 선의 좌표들을 저장하는 배열.
const [paths, setPaths] = useState([]); //그려진 모든 선들의 좌표들을(path들의 집합) 저장하는 배열.
useEffect(() => {
const canvas = canvasRef.current;
const ctx = canvas.getContext("2d");
const drawing = (e) => {
if (!isDrawing) return;
const rect = canvas.getBoundingClientRect();
const offsetX = e.clientX - rect.left;
const offsetY = e.clientY - rect.top;
ctx.beginPath();
ctx.moveTo(lastX, lastY);
ctx.lineTo(offsetX, offsetY);
ctx.strokeStyle = "black";
ctx.lineWidth = 2;
ctx.stroke();
setLastX(offsetX);
setLastY(offsetY);
setPath((prevPath) => [...prevPath, { x: offsetX, y: offsetY }]); //x, y 좌표를 배열에 저장
};
canvas.addEventListener("mousemove", drawing);
return () => {
canvas.removeEventListener("mousemove", drawing);
};
}, [isDrawing, lastX, lastY]);
const drawingCanvas = (e) => {
setIsDrawing(true);
const rect = e.target.getBoundingClientRect();
setLastX(e.clientX - rect.left);
setLastY(e.clientY - rect.top);
setPath([{ x: e.clientX - rect.left, y: e.clientY - rect.top }]); //캔버스 내 마우스 커서의 좌표를 배열에 저장
};
const stopDrawing = () => {
if (isDrawing) {
setPaths((prevPaths) => [...prevPaths, path]); //path 좌표들을 하나로 모아 집합으로 저장
setPath([]); //path 배열 초기화
}
setIsDrawing(false);
};
const canvasOut = () => {
setIsDrawing(false);
};
const outputCanvasImage = () => {
const canvas = canvasRef.current;
setOutputImageSrc(canvas.toDataURL());
};
const clearCanvas = () => {
const canvas = canvasRef.current;
const ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
setOutputImageSrc(null);
setPaths([]); //paths 배열 비우기
};
//한 획 지우기 함수
const returnCurrentLine = () => {
setPaths((prevPaths) => prevPaths.slice(0, -1)); //가장 최근 선 한 개를 삭제
};
//선 하나 삭제 시 paths 배열에 저장된 선들을 다시 그려서 캔버스에 복구
const redrawCanvas = () => {
const canvas = canvasRef.current;
const ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
paths.forEach((p) => { //paths 배열을 순회하면서 배열에 남아있는 선(p)을 다시 캔버스에 보여줌
ctx.beginPath(); //새로운 선을 그릴 경로 시작
ctx.moveTo(p[0].x, p[0].y); //현재 경로의 시작점을 설정
for (let i = 1; i < p.length; i++) {//좌표들을 순회하면서 선을 그림
ctx.lineTo(p[i].x, p[i].y);
}
ctx.stroke(); //실제 선을 그려서 새로운 선을 추가
});
};
//paths 배열에 저장된 모든 선들을 캔버스에 다시 그려주는 역할을 수행
//선이 추가되거나 삭제될 때 캔버스를 업데이트하고 다시 렌더링하는 데 사용
useEffect(redrawCanvas, [paths]); //렌더링 될 때마다 redrawCanvas 실행
return (
<div className="canvas">
<canvas
ref={canvasRef}
width={500}
height={500}
style={{ border: "1px solid black" }}
onMouseDown={drawingCanvas}
onMouseUp={stopDrawing}
onMouseOut={canvasOut}
/>
{outputImageSrc && <img src={outputImageSrc} alt="분석된 이미지" />}
<div>
<button onClick={outputCanvasImage}>추출</button>
<button onClick={clearCanvas}>다시 쓰기</button>
<button onClick={returnCurrentLine}>한 획 지우기</button> <!--한 획 지우기 버튼-->
</div>
</div>
);
}
export default Canvas;
canvas.css
.canvas button {
width: 100px;
height: 30px;
font-size: 15px;
font-weight: bold;
border: 1px solid black;
background-color: white;
}
.canvas button:hover {
color: white;
background-color: black;
}
'프로젝트 > 한글 게임' 카테고리의 다른 글
7. 이미지 텍스트 인식 (0) | 2024.04.14 |
---|---|
6. Google Vision API (0) | 2024.04.14 |
4. Canvas 다시 쓰기 (0) | 2024.04.11 |
3. Canvas 이미지 추출 (0) | 2024.04.11 |
2. 선 그리기 (0) | 2024.04.11 |