React로 그림판 만들기1

DANO PARK·2022년 7월 19일
18

무작정 따라하기

목록 보기
8/9
post-thumbnail

아! React가 미숙했던 나의 옛날(3달 전)이여!

노마드 코더 바닐라JS로 그림 앱 만들기 강의를 보며 진행했던 프로젝트를 React로 바꿔보고싶었다. 예전에 한 번 시도했었다가 막힌 이후로 다시 한 번 시도해보는 것이다.

국비 과정을 통해 다양한 React 프로젝트를 경험하다보니, 예전에 왜 실패했었는지 단번에 이해가 되었다. 옛날 실패했던 프로젝트 코드를 열어보니, 그때는 JavaScript에만 익숙했기 때문에 React App 위에 JavaScript를 얹혀놓은 괴상한 모습을 하고 있었다.

React Hook 사용하기

바닐라JS와는 다르게, React에서는 canvasgetContext()를 사용하기 위해서는 먼저 useRef()useEffect(), useState()의 도움을 받아야 한다.

  • Vanilla JS
<body>
	<canvas id="canvas"></canvas>
</body>
<script>
	const canvas = document.getElementById("canvas");
    const ctx = canvas.getContext("2d");
</script>
  • React
import React { useEffect, useRef, useState } from "react";

export default function App() {
	const canvasRef = useRef(null);
    const [getCtx, setGetCtx] = useState(null);
    
    useEffect(() => {
    	const canvas = canvasRef.current;
        const ctx = canvas.getContext("2d");
        setGetctx(ctx);
    }, [])
    
	return (
    	<canvas ref={canvasRef}></canvas>
    );
 }

addEventListener말고 Mouse Event 사용하기

canvas에 대한 설정을 해준 다음에는, canvas 범위 내에서 현재 마우스의 위치 정보를 받아와야 한다. 그래야 그 위치에 그림을 그릴 수 있기 때문이다. 바닐라JS에서는 addEventListener()를 사용했지만, React에서는 canvas 태그에 Mouse Event를 사용하자.

  • Vanilla JS
<script>
  canvas.addEventListener("mousemove", onMouseMove);
  canvas.addEventListener("mousedown", startPainting);
  canvas.addEventListener("mouseup", stopPainting);
  canvas.addEventListener("mouseleave", stopPainting);
</script>
  • React
return(
	<canvas
    	ref={canvasRef}
        onMouseDown={() => setPainting(true)}
        onMouseUp={() => setPainting(false)}
        onMouseMove={e => drawFn(e)}
        onMouseLeave={() => setPainting(false)}
    >
    </canvas>
);

마우스 현재 위치 정보 가져와서 그리기

위의 이벤트를 설명하자면, 마우스를 눌렀을 경우 그림을 그릴 수 있고, 마우스를 뗐을 경우나 canvas 범위 내에서 벗어날 때에는 그림을 그릴 수 없다. 그리고 마우스를 canvas 범위 안에서 움직일 때 움직인 위치 정보를 가져다주게 된다.

바닐라 JS는 offsetXoffsetY로 위치 정보를 가져올 수 있는데, React에서는 nativeEvent로 한 번 거쳐서 offsetXoffsetY를 가져와야 한다. clientX나, pageX 등은 canvas 범위를 기준으로 하지 않기 때문에 내가 그림을 그리려는 위치와 실제 그림이 그려지는 위치가 차이가 발생한다. 따라서 React에서는 event.nativeEvent.offsetX, event.nativeEvent.offsetY와 같이 사용해야 한다.

  • Vanilla JS
<script>
	let painting = false;
    
    function stopPainting() {
  		painting = false;
	}

	function startPainting() {
  		painting = true;
	}
    
    function onMouseMove(event) {
  		const x = event.offsetX;
  		const y = event.offsetY;
  		if(!painting) {
    		ctx.beginPath();
    		ctx.moveTo(x, y);
  		} else {
    		ctx.lineTo(x, y);
    		ctx.stroke();
  		}
	}
</script>
  • React
const [painting, setPainting] = useState(false);

const drawFn = e => {
	const mouseX = e.nativeEvent.offsetX;
    const mouseY = e.nativeEvent.offsetY;
    if (!painting) {
      getCtx.beginPath();
      getCtx.moveTo(mouseX, mouseY);
    } else {
      getCtx.lineTo(mouseX, mouseY);
      getCtx.stroke();
    }
}

전체 코드

  • App.jsx
// react
import React, { useRef, useEffect, useState } from "react";
// style
import { CanvasStyle } from "./styles/cavas";

export default function App() {
  // useRef
  const canvasRef = useRef(null);
  // getCtx
  const [getCtx, setGetCtx] = useState(null);
  // painting state
  const [painting, setPainting] = useState(false);

  useEffect(() => {
    // canvas useRef
    const canvas = canvasRef.current;
    canvas.width = 650;
    canvas.height = 540;
    const ctx = canvas.getContext("2d");
    ctx.lineJoin = "round";
    ctx.lineWidth = 2.5;
    ctx.strokeStyle = "#000000";
    setGetCtx(ctx);
  }, []);

  const drawFn = e => {
    // mouse position
    const mouseX = e.nativeEvent.offsetX;
    const mouseY = e.nativeEvent.offsetY;
    // drawing
    if (!painting) {
      getCtx.beginPath();
      getCtx.moveTo(mouseX, mouseY);
    } else {
      getCtx.lineTo(mouseX, mouseY);
      getCtx.stroke();
    }
  }

  return (
    <CanvasStyle>
      <div className="view">
        <div className="canvasWrap">
          <canvas 
            className="canvas"
            ref={canvasRef}
            onMouseDown={() => setPainting(true)}
            onMouseUp={() => setPainting(false)}
            onMouseMove={e => drawFn(e)}
            onMouseLeave={() => setPainting(false)}
          >
          </canvas>
        </div>
      </div>
    </CanvasStyle>
  )
}

결과물

일단 검은 선만 그을 수 있는 상태다. 다음번에는 노마드 코더 바닐라JS로 그림 앱 만들기 강의 내용처럼 색상선택이나 페인트 기능, 저장 기능 등을 구현해야하는데...(귀찮)

시간 날 때마다 계속 작업해보도록 하겠다.

끝.


다음 글 : React로 그림판 만들기2 : 리셋&저장

profile
단오해서 단호박!

1개의 댓글

comment-user-thumbnail
2023년 2월 1일

잘봤습니다

답글 달기