Canvas.tsx 2.85 KB
import React, { useCallback, useEffect, useRef, useState } from 'react';
import { Vector } from './types';

// 참고 : https://basketdeveloper.tistory.com/79

export const Canvas: React.FC = () => {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  
  const [mousePosition, setMousePosition] = useState<Vector>({ x:0, y:0 });
  const [isPainting, setIsPainting] = useState(false);

  const getCoordinates = useCallback((event: MouseEvent): Vector | undefined => {
    if (!canvasRef.current) {
      return;
    } else {
      return {
        x: event.pageX - canvasRef.current.offsetLeft,
        y: event.pageY - canvasRef.current.offsetTop
      };
    }
  }, []);

  const drawLine = useCallback((prev: Vector, current: Vector) => {
    if (canvasRef.current) {
      const context = canvasRef.current!.getContext('2d');
      if (context) {
        context.strokeStyle = 'black';
        context.lineJoin = 'round';
        context.lineWidth = 5;

        context.beginPath();
        context.moveTo(prev.x, prev.y);
        context.lineTo(current.x, current.y);
        context.closePath();

        context.stroke();
      }
    }
  }, []);

  const clearCanvas = useCallback(() => {
    if (canvasRef.current) {
      const context = canvasRef.current.getContext('2d');
      if (context) {
        context.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height);
      }
    }
  }, []);

  const startPaint = useCallback((event: MouseEvent) => {
    const coordinates = getCoordinates(event);
    if (coordinates) {
      setIsPainting(true);
      setMousePosition(coordinates);
    }
  }, []);

  const paint = useCallback(
    (event: MouseEvent) => {
      // 드래그 방지
      event.preventDefault();
      event.stopPropagation();

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

  const exitPaint = useCallback(() => {
    setIsPainting(false);
  }, []);

  useEffect(() => {
    if (canvasRef.current) {
      const canvas: HTMLCanvasElement = canvasRef.current;

      canvas.addEventListener('mousedown', startPaint);
      canvas.addEventListener('mousemove', paint);
      canvas.addEventListener('mouseup', exitPaint);
      canvas.addEventListener('mouseleave', exitPaint);
  
      return () => {
        canvas.removeEventListener('mousedown', startPaint);
        canvas.removeEventListener('mousemove', paint);
        canvas.removeEventListener('mouseup', exitPaint);
        canvas.removeEventListener('mouseleave', exitPaint);
      };
    }
  }, [startPaint, paint, exitPaint]);

  return (
    <div className='mx-3 px-2 py-1 rounded shadow'>
      <canvas ref={canvasRef} width='512' height='384' />
    </div>
  );
}