<aside> 💡 캔버스는 HTML 요소 중 하나로 스크립트 언어로 그림을 그리는데 사용한다.
</aside>
canvas를 사용하기 위해선 캔버스 context에 DOM으로 접근해야 한다. React에선 useRef()
를 사용해서 Ref 객체를 만들고 접근하고 싶은 DOM의 ref 값으로 설정해주면 된다. 그럼 Ref 객체의 .current
값은 해당 DOM을 가리킨다.
import React, { useRef, useEffect, useState } from 'react';
import styled from 'styled-components';
const Canvas = () => {
const canvasRef = useRef(null);
// 생략
return <DrawingArea ref={canvasRef} />
}
/* ----- 스타일 영역 ----- */
const DrawingArea = styled.canvas`
// 생략
`
canvasRef의 기본값은 null
이기 때문에 컴포넌트가 마운트 된 후 코드를 실행하도록 useEffect 안에다 코드를 작성한다.
아직 경험하진 못했지만 컴포넌트가 렌더되고 가끔 useRef 참조값이 null
인 경우가 있다고. useState를 활용해 드로잉 컨텍스트를 상태로 저장하면 이런 경우를 방지할 수 있다고 함.
const Canvas = () => {
const canvasRef = useRef(null);
const [ctx, setCtx] = useState();
useEffect(() => {
// 캔버스 사이즈 설정
const canvas = canvasRef.current;
canvas.width = window.innerHeight; // 혹은 원하는 사이즈 px없이 숫자만 입력
canvas.height = window.innerWidth; // 혹은 원하는 사이즈 px없이 숫자만 입력
// 캔버스 context 접근 후 기본 설정
const context = canvas.getContext('2d') // "그리기 메서드와 속성을 갖는" 2차원 드로잉 컨텍스트 참조
context.strokeStyle = 'black'; // line 색
context.lineWidth = 3; // line 굵기
context.lineJoin = 'round'; // 선 연결 모양(기본 값 miter 일반 모양)
context.lineCap = 'round'; // 선 끝 모양
context.save(); // 드로잉 컨텍스트 설정 저장
setCtx(context) // 설정한 드로잉 컨텍스트를 상태로 저장
}, [])
}
<aside> 💡 offset 좌표는 이벤트가 걸려 있는 DOM 객체를 기준으로 좌표를 출력한다. canvas 요소에 이벤트를 걸었다면 브라우저 화면이 아닌 canvas 요소가 기준이 된다. offset 좌표의 기본값은 왼쪽 상단 (0, 0)
</aside>
캔버스에서 마우스로 그림을 그릴 때 이벤트 대상의 x(가로), y(세로) 좌표를 반환하는 offsetX
offsetY
가 필요하다. 리액트에선 성능 최적화를 위해 래핑된 합성 이벤트(Synthetic Event)를 전달하며 재사용된다.
하지만 이 합성 이벤트에선 offset 좌표를 사용할 수 없다. 핸들러에서 이벤트 객체를 콘솔로 찍어보면 offsetX, offsetY를 찾을 수 없다. — 참고 글
리액트에서 offset 좌표를 사용하려면 event.nativeEvent
를 통해 브라우저의 고유 이벤트에 접근해야 한다. 매개변수 구조분해를 이용해 ({ nativeEvent })
이런식으로 명시하면 사용하기 편하다.
const Canvas = () => {
const [isDrawing, setIsDrawing] = useState(false); // 그리기 상태 설정
const [path, setPath2D] = useState(new Path2D()); // path 객체 생성
const startDrawing = ({ nativeEvent }) => {}
const drawing = ({ nativeEvent }) => {}
const finishDrawing = ({ nativeEvent }) => {}
return (
<DrawingArea
ref={canvasRef}
onMouseDown={startDrawing}
onMouseUp={finishDrawing}
onMouseMove={drawing}
// onMouseLeave={finishDrawing} // 마우스가 이벤트 영역 벗어났을 때
/>
);
};
ctx.moveTo(x, y)
true
로 변경하고ctx.lineTo(x, y)
ctx.stroke(x, y)