본문 바로가기
프로젝트/한글 게임

5. 선 한 획 씩 삭제하기

by 갱생angel 2024. 4. 11.

<추가사항>

※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