2 분 소요

React로 Canvas 사용하기

js를 사용 할 경우 getElementById를 사용하여 캔버스 태그에 접근하지만 react에서는 useRef를 사용하여 접근해야 한다.

1. canvasRef 설정

function Canvas(){
	const canvasRef = useRef(null);

return(
    <div>
	<h2>캔버스</h2>
	<canvas ref={canvasRef} width="500" height="500"></canvas>
    </div>
    )
}

useRef()를 사용하여 Ref객체를 만들고, <canvas>의 ref로 속성을 지정한다. 이렇게 하면 canvasRef.current 의 값은 <canvas ref={canavasRef}> 를 가르키게 된다.

2. useEffect 사용

function Canvas(){
	const canvasRef = useRef(null);
	const contextRef = useRef(null);
	const [ctx, setCtx] = useState(null);

	useEffect(()=>{
		const canvas = canvasRef.current;
		canvas.width = 500;
		canvas.height = 500;
		const context = canvas.getContext("2d");

		contextRef.current = context;
		
		setCtx(contextRef.current);

		
		},[])

1.<canvas> 컴포넌트가 렌더링 된 시점(mount)을 보장하면서(=mount된 이후) canvascontext를 가져오기 위해 useEffect를 사용한다.

참고 : useEffect() : 컴포넌트의 렌더링 결과가 실제 돔에 반영된 뒤(componentDidMount)와 컴포넌트가 업데이트된 뒤(componentDidUpdate), 컴포넌트가 사라지기 직전(componentWillUnmount)에 호출된다.

2.useRef()가 반환한 canvasRef의 current속성 값을 const변수canvas에 정의한다. 이제 const변수 canvas는 조작하려는 <canvas>를 가리키게 된다.

3.캔버스에 그림을 그리기 위해서 getContext("2d")메서드로 2d그림을 그릴 수 있게 context를 정의 한다.

4.contextRef를 useRef를 사용하여 정의한다. contextRef의 current 어트리뷰트에 3번에서 정의한 context를 대입한다. 이제 contextRef.current를 기준으로 그림을 그리게 된다.

참고 : 어느 분의 블로그에서 봤는데 useState를 사용하지 않고, useRef를 사용한 드로잉 컨텍스트를 참조하여 작동하였더니 render가 될 때 참조하는 값이 가끔 null 이란 오류가 발생한다고 한다. 이 오류를 수정하기 위해 useState를 사용하여 ctx의 state에 드로잉컨텍스트를 할당하여 해결한다.

5.위와 같은 오류를 해결하기위해 useState를 사용하여 ctx에 contextRef.current를 넣어준다.

3. draw함수 정의

function Canvas(){
	const canvasRef = useRef(null);
	const contextRef = useRef(null);
	const [ctx, setCtx] = useState(null);

	useEffect(()=>{
		const canvas = canvasRef.current;
		canvas.width = 500;
		canvas.height = 500;
		const context = canvas.getContext("2d");

		contextRef.current = context;
		
		setCtx(contextRef.current);
		
		},[])
		
		function draw(){
			ctx.arc(100,100,50,0,Math.PI*2,false);
      		        ctx.fill();
		}

ctx를 사용하여 (100,100)을 중심으로 하고 반지름은 50, 0을 startAngle, Math.PI*2를 endAngle로 두는 호를 그린다. ctx.fill()를 사용하여 경로의 내부가 채워진 도형을 그리게 된다.

참고 : arc함수에 각도는 라디안 값을 사용한다. radian = (Math.PI/180)*degrees

4. draw()함수 호출

function Canvas(){
	const canvasRef = useRef(null);
	const contextRef = useRef(null);
	const [ctx, setCtx] = useState(null);

	useEffect(()=>{
		const canvas = canvasRef.current;
		canvas.width = 500;
		canvas.height = 500;
		const context = canvas.getContext("2d");

		contextRef.current = context;
		
		setCtx(contextRef.current);
		
		},[])
		
		function draw(){
		  ctx.arc(100,100,50,0,Math.PI*2,false);
                  ctx.fill();
		}

		if(ctx!=null){
	           draw();
		}

draw함수를 if문으로 둘러싸지 않고 실행시키면

Canvas.js:23 Uncaught TypeError: Cannot read properties of null (reading 'arc')

와 같이 null의 properties를 읽을 수 없다고 하면서 에러가 발생한다.
이는 ctx가 정의되는 useEffect가 실행되기 전에 draw를 호출해서 발생하는 것이다.

위에서 말했다시피 useEffect() 는 컴포넌트의 렌더링 결과가 실제 돔에 반영된 뒤(componentDidMount)와 컴포넌트가 업데이트된 뒤(componentDidUpdate), 컴포넌트가 사라지기 직전(componentWillUnmount)에 호출되기 때문이다.

따라서 if(ctx!=null) 로 조건을 주고 draw함수를 호출해야 한다.

전체코드

function Canvas(){
	const canvasRef = useRef(null);
	const contextRef = useRef(null);
	const [ctx, setCtx] = useState(null);

	useEffect(()=>{
		const canvas = canvasRef.current;
		canvas.width = 500;
		canvas.height = 500;
		const context = canvas.getContext("2d");

		contextRef.current = context;
		
		setCtx(contextRef.current);
		
		},[])
		
		function draw(){
		  ctx.arc(100,100,50,0,Math.PI*2,false);
                  ctx.fill();
		}

		if(ctx!=null){
		  draw();
		}
	
    return(
        <div>
	    <h2>캔버스</h2>
	    <canvas className="canvas" ref={canvasRef} width="500" height="500"></canvas>
        </div>
    )
}

export default Canvas

카테고리:

업데이트:

댓글남기기