[DAY60] TIL

1nxeo·2023년 4월 6일

항해99

목록 보기
57/63
post-thumbnail

react typescript 로 그림판 만들기

  1. 문제
    이번에 구현해야 하는 기능 중에서 그림판이 있었다..
    HTML에 canvas Element가 있는데, 그걸 한번도 사용해본 적이 없었고 어떤 흐름으로 사용되는지 몰랐음.
    DOM에 직접적으로 접근해야하고 mouse 이벤트를 사용해야함.

  2. 시도
    일단 canvas 쓰고 본다. 그리고 getElementById 대신 react의 훅인 useRef를 사용하여 캔버스를 조작해준다.
    최대한 addEventListener도 안쓰고 싶은 마음에 어떻게할지 고민하다가, react에서는 canvas 태그에 onMouseUp 등등을 사용할 수 있다는 사실을 알게되었다.

  3. 해결

 const canvasRef = useRef<HTMLCanvasElement>(null);

// return 이하
 			<canvas
                ref={canvasRef}
                height={700}
                width={700}
                onMouseDown={mouseDownHandler}
                onMouseMove={mouseMoveHandler}
                onMouseUp={mouseUpHandler}
                onMouseLeave={mouseLeaveHandler}
              />

마우스 핸들링 함수들

 // useEffect + AddEventListener 대체 함수
  const mouseDownHandler = (
    event: React.MouseEvent<HTMLCanvasElement>
  )=> {
      startPaint(event);
  };

  const mouseMoveHandler = (
    event: React.MouseEvent<HTMLCanvasElement>
  )=> {
      paint(event);
  };

  const mouseUpHandler = (event: React.MouseEvent<HTMLCanvasElement>)=>
  {
      exitPaint();
  };
  const mouseLeaveHandler = (
    event: React.MouseEvent<HTMLCanvasElement>
  )=> {
      exitPaint();
    } 
  };

마우스 위치를 구하는 함수

  // 좌표 함수
  const getCoordinates = (
    event: React.MouseEvent<HTMLCanvasElement>
  ): Coordinate | undefined => {
    if (!canvasRef.current) {
      return;
    }
    const canvas: HTMLCanvasElement = canvasRef.current;
    return {
      x: event.pageX - canvas.offsetLeft,
      y: event.pageY - canvas.offsetTop,
    };
  };

그림그릴때 사용되는 함수들 (custom hook으로 빼줬다)

// canvas에 선긋는 함수
  const drawLine = (
    originalMousePosition: Coordinate,
    newMousePosition: Coordinate,
    color: string
  ) => {
    if (!ref.current) {
      return;
    }
    const canvas: HTMLCanvasElement = ref.current;
    const context = canvas.getContext("2d");

    if (context) {
      context.strokeStyle = black;
      context.lineJoin = "round";
      context.lineWidth = 5;

      context.beginPath();
      context.moveTo(originalMousePosition.x, originalMousePosition.y);
      context.lineTo(newMousePosition.x, newMousePosition.y);
      context.closePath();

      context.stroke();
    }
  };

// mouse event 에 따른 처리 함수
  const startPaint = useCallback(
    (event: React.MouseEvent<HTMLCanvasElement>) => {
      const coordinates = action(event);
      if (coordinates) {
        setIsPainting(true);
        setMousePosition(coordinates);
      }
    },
    []
  );

  const paint = useCallback(
    (event: React.MouseEvent<HTMLCanvasElement>)=> {
      event.preventDefault(); // prevent drag
      event.stopPropagation(); // prevent drag

      if (isPainting) {
        const newMousePosition = action(event);
        if (mousePosition && newMousePosition) {
          drawLine(mousePosition, newMousePosition, color);
          setMousePosition(newMousePosition);
        }
      }
    },
    [isPainting, mousePosition]
  );

  const exitPaint = useCallback(() => {
    setIsPainting(false);
  }, []);
  1. 알게된 점
    useRef를 사용하여 DOM에 직접적으로 접근이 가능하다.
    useEffect와 addEventListener, removeEventListener를 사용하지 않고 이벤트에 대한 처리가 가능하다. 실제로는 지우개모드와 펜모드 두 가지의 모드가 있어서 따로 처리해주는 것이 편하긴 했지만, 만약 그리기만 가능한 그림판을 만든다면 코드 자체는 useEffect와 eventListener를 사용하는게 나아 보이기도 한다. 더 고민해봐야할듯.
profile
항상 피곤한 인서의 개발블로그

0개의 댓글