리액트 canvas 사용하기

이성훈·2021년 9월 10일
2
post-thumbnail

파이널 프로젝트때 사용할 canvas 라이브러리 공부한 내용 정리입니다

fabric 받기

fabric은 canvas 위 그림을 객체로 관리하는 라이브러리입니다

// fabric install
$ npm install fabric --save

// 리액트 import
import { fabric } from "fabric";

리액트 상에서 사용법은 다음과 같습니다
1. canvas id 설정

<canvas id='canvas' />
  1. fabric을 저장하고 액세스할 상태 변수 정의
import React, { useState } from 'react';
const [canvas, setCanvas] = useState('');
  1. fabric을 반환하는 함수 생성
const initCanvas = () => (
	new fabric.Canvas('elementId', { style })
)
  1. DOM 초기 렌더링 시 함수 호출
import React, { useState, useEffect } from 'react';
useEffect(() => { 
   setCanvas(initCanvas()); 
}, []);
  1. 최종 코드
import React, { useState, useEffect } from "react";
import { fabric } from "fabric";

function Draw() {
  const [canvas, setCanvas] = useState("");

  const initCanvas = () =>
    new fabric.Canvas("canvas", {
      height: 800,
      width: 800,
      backgroundColor: "gray",
    });

  useEffect(() => {
    setCanvas(initCanvas());
  }, []);

  return <canvas id="canvas" />;
}

export default Draw;

객체

기본적으로 제공하는 도형객체들입니다
fabric.Circle // 동작안됨
fabric.Ellipse // 동작안됨
fabric.Line
fabric.Polygon // 동작안됨
fabric.Polyline
fabric.Rect // 동작됨
fabric.Triangle // 동작됨

// 사용 예시
let circle = new fabric.Circle({
	radius: 20, fill: 'gray', left: 100, top: 200
})

canvas.add(circle); // add로 원하는 도형을 추가해줍니다

내장함수

// 추가
.add(추가할 객체)

// 삭제
.remove()

// 그룹 내 객체 하나에 접근
.item()

// 그룹 내 모든 객체 반환
.getObjects()

// 뒷배경 설정
.setBackgroundImage('이미지링크', canvas.renderAll.bind(canvas), {스타일})

// ??
.onFpsUpdate = () => {}

Image

canvas에 이미지 불러오는 방법
fabric.image.fromURL

멀터를 사용해서 이미지 업로드하는 코드

const handleImage = (e) => {
		const reader = new FileReader();
		const file = e.target.files[0];
		reader.onload = () => {
			new fabric.Image.fromURL(reader.result, (image) => {
				image.scale(0.75);
				canvas.add(image);
				canvas.renderAll();
			});
		};
		reader.readAsDataURL(file);
	};

axios로 서버에 이미지 전송하는방법

// 저장 핸들러
	const saveHandler = async () => {
		if (user.isLogin) {
			const canvasData = JSON.stringify(canvas);
			const sendData = { ...caseInfo, setting: canvasData };
			const imgdata = canvas.toDataURL("image/png", 1.0);
			let file = base64toFile(imgdata);
			let formdata = new FormData();
			formdata.append("picture", file);

			if (review.caseInfo.id) {
				formdata.append("caseId", review.caseInfo.id);
				formdata.append("phondId", review.caseInfo.phoneId);
				formdata.append("price", review.caseInfo.price);
				formdata.append("setting", canvasData);
			} else {
				for (let el in sendData) {
					formdata.append(el, sendData[el]);
				}
			}

			await axios
				.post(`${process.env.REACT_APP_API_URL}locker`, formdata, {
					withCredentials: true,
					header: { "Content-Type": "multipart/form-data" },
				})
				.then((el) => {
					if (el.data.message === "새로운 저장") {
						dispatch(handleAlertModal("새롭게 저장되었습니다"));
					} else if (el.data.message === "ok") {
						dispatch(
							handleConfirmModal(
								"저장이 완료되었습니다",
								0,
								"보관함으로 이동 하시겠습니까?"
							)
						);
					}
				});
		} else {
			dispatch(handleAlertModal("로그인 해주세요"));
			reverseBoo();
		}
	};

색상

hex, RGB, RGBA 다 지원합니다

new fabric.Color('#f55');
new fabric.Color('#123123');
new fabric.Color('356735');
new fabric.Color('rgb(100,0,100)');
new fabric.Color('rgba(10, 20, 30, 0.5)');

문자

기본적인 css정도는 다 제공되는 것 같습니다

Multiline support: 네이티브 텍스트의 경우는 새로운 라인을 지원하지 않았습니다.
Text alignment: Left, Center. right 등 멀티라인 텍스트에 대한 정렬을 제공합니다.
Text background: 백그라운드 역시 텍스트 정렬을 지원합니다.
Text decoration: 텍스트 장식을 지원합니다.
Line height: 멀티라인에 대한 높이 간격 설정에 유용합니다.
Char spacing: 문자 간격을 조정할 수 있습니다.
Subranges: 텍스트 객체의 하위 범주에 대한 색상 및 속성 지정이 가능합니다.
Multibyte: 이모티콘도 지원합니다.
On canvas editing: 대화형 클래스를 통해 캔버스에서 직접 편집이 가능합니다.

const text = new fabric.IText('hello world', { left: 100, top: 100, fontFamily: 'sans-serif' });
canvas.add(text);

Event

캔버스에서 일어나는 다양한 행동들이 발생하는 순간에 활용됩니다

mouse:down 마우스 눌렀을때
object:added 캔버스에 객체가 추가됬을때
after:render 캔버스 전체가 다시 렌더링 됬을때

const canvas = new fabric.Canvas('...');
canvas.on('mouse:down', function(options) {
  console.log(options.e.clientX, options.e.clientY);
});

아래처럼 객체에 직접 붙힐수도 있습니다

rect.on('selected', function() {
  console.log('selected a rectangle');
});

Groups

여러객체들을 단일객체로 묶은 것입니다

const circle = new fabric.Circle({
  radius: 100,
  fill: '#eef',
});

var text = new fabric.Text({
  fontSize: 30,
});

var group = new fabric.Group([ circle, text ], {
  left: 150,
  top: 100,
});

canvas.add(group);

위 처럼 3개의 객체를 하나로 묶고

group.item(0).setFill('red');
group.item(1).set({
  text: 'trololo',
  fill: 'white'
});

item()을 사용하여 개별 객체를 건드릴수있습니다

핸들링

  • canvas.getActiveObjects()
    활성화된 객체의 폰트나, 사이즈, 색상등을 바꿀때 사용하는 함수로 추측됨

    const item = canvas.getActiveObjects();
    
    item.set({ fontSize: e.target.value })
    
    canvas.renderAll();
    setFontSize(e.target.value);

직렬화

만든 캔버스를 이미지로 내보낼 수있는 옵션이 존재하지만 그만큼 성능에 영향을 미치게됩니다
그래서 텍스트로 용량 최적화를 시켜 보내는데 필요한 것이 직렬화/역직렬화입니다

아래는 대표적인 뼈대인 toObject, toJSON를 사용한 간단한 예입니다, toDataURL('png')도 있습니다

const canvas = new fabric.Canvas('c');
JSON.stringify(canvas);  // '{"objects": [], "background": "rgba(0, 0, 0, 0)"}

또한 toSVG로 직렬화하는 방법이 있습니다

canvas.add(new fabric.Rect({
  left: 30,
  top: 30,
  height: 30,
  width: 30,
  fill: 'blue'
}));
console.log(canvas.toSVG());

역직렬화

캔버스에 우리가 직렬화 시킨것을 로드하려면 두가지를 사용할 수 있습니다

// fromJSON
// SG parser

fabric.loadSVGFromString('...', function(objects, options) {
  const obj = fabric.util.groupSVGElements(objects, options);
  canvas.add(obj).renderAll();
});

캔버스 반응형

// 캔버스 반응형 이벤트
		const handleResizeEvent = () => {
			if (document.body.clientWidth <= 768) {
				canvas.setDimensions({
					width: 600,
					height: 600,
				});
			}

			if (document.body.clientWidth <= 425) {
				canvas.setDimensions({
					width: 200,
					height: 400,
				});
			}

			canvas.renderAll();
		};

우클릭, 미들클릭 활성화

캔버스에서 사용

canvas.on('mouse:down', (event) => {
    if(event.button === 1) {
        console.log("left click");
    }
    if(event.button === 2) {
        console.log("middle click");
    }
    if(event.button === 3) {
        console.log("right click");
    }
}

객체에서 사용

object.on('mousedown', (event) => {
    if(event.button === 1) {
        console.log("left click");
    }
    if(event.button === 2) {
        console.log("middle click");
    }
    if(event.button === 3) {
        console.log("right click");
    }
}

pointer 좌표

const pointer = new fabric.Point(
	canvas.getPointer(e.e).x,
	canvas.getPointer(e.e).y
);
console.log(pointer);

참조

fabric github: https://github.com/fabricjs/fabric.js
fabric-history github: https://github.com/lyzerk/fabric-history
JSDoc: http://fabricjs.com/docs/fabric.StaticCanvas.html

fabric 기초 생성: https://aprilescobar.medium.com/part-1-fabric-js-on-react-fabric-canvas-e4094e4d0304
fabric 사용법: https://itinerant.tistory.com/156
fabric all: https://medium.com/@seohyoda/fabric-js-%EC%9E%85%EB%AC%B8-part-1-78dd390536cf
canvas 반응형 관련 정보: https://www.python2.net/questions-1339788.htm

profile
블로그 이전중입니다 => https://kusdsuna.tistory.com/

0개의 댓글