[Fabric.js] 2. Fabric.js에서 동적으로 속성 값을 설정하는 방법

chaevivi·2024년 2월 19일
0

Fabric.js

목록 보기
2/3
post-thumbnail

이전 포스트에서는 React, TypeScript 환경에서 Fabric.js 캔버스를 생성하는 방법에 대해서 알아보았습니다. 이번 포스트에서는 캔버스의 다양한 속성 값을 동적으로 설정하는 방법에 대해 알아보겠습니다.



1. 캔버스 드로잉 모드 (isDrawingMode)

캔버스를 생성할 때 드로잉 모드를 미리 true로 작성하지 않은 이상 캔버스의 드로잉 모드 기본값은 false입니다.


const newCanvas = new fabric.Canvas('canvas', {
  width: 500,
  height: 500,
  isDrawingMode: true
});

위의 코드처럼 isDrawingMode의 값을 truefalse(isDrawingMode 값을 작성하지 않으면 기본으로 false)로 캔버스를 생성할 수 있습니다. 만약 isDrawingModetrue로 초기에 설정되어 있다면 캔버스에 그림을 그릴 수 있는 브러쉬가 자동으로 생성됩니다.


하지만 드로잉 모드를 동적으로 변경하고 싶을 때가 있습니다.

제 프로젝트를 예로 들면, 브러쉬 패널을 클릭했을 때만 드로잉 모드가 true이고 다른 패널에서는 드로잉 모드가 false여야 합니다. 왜냐하면 모든 패널에서 드로잉 모드가 true라면 배경을 바꾸는데 불필요하게 그림이 그려질 수도 있기 때문입니다.


이럴 때는 useEffect를 활용해야 한다는 것을 잘 알고 계실 겁니다. 그렇다면 코드를 작성해 봅시다.

useEffect(() => {
    if (canvas) {
      canvas.isDrawingMode = nowPanel === 'brush';
    }
}, [canvas, nowPanel]);
  • canvas가 null이나 undefined 일수도 있기 때문에 꼭 확인하는 과정이 있어야 합니다.
  • nowPanel은 현재 클릭된 패널을 의미합니다.
  • nowPanelbrush일 때만 isDrawingModetrue라고 설정하였습니다.

하지만 이렇게 작성해도 제대로 작동하지 않는다는 것을 확인할 수 있습니다. 😫😥😭 왜 이런 일이 발생할까요?


이유는 바로 Fabric은 자동으로 캔버스를 렌더링하지 않기 때문입니다. 이는 성능과 관련이 있습니다. Fabric은 캔버스에 다양한 개체와 애니메이션 등을 추가할 수 있습니다. 하지만 캔버스의 모든 개체와 애니메이션이 리렌더링을 하려고 한다면 성능에 매우 안 좋은 영향을 끼칠 수 있습니다. 그래서 Fabric은 수동으로 렌더링을 할 수 있는 메서드 renderAll을 제공합니다.

공식 문서에서 확인할 수 있듯이 renderAll 메서드는 캔버스와 캔버스 안에 있는 컨테이너를 모두 렌더링합니다. 그래서 캔버스에 도형, 이미지를 추가하거나 속성 값을 바꿀 때 마지막에 꼭 renderAll 메서드를 작성해 주어야 변경 사항이 제대로 반영이 됩니다.


이제 코드를 고쳐봅시다.

useEffect(() => {
    if (canvas) {
      canvas.isDrawingMode = nowPanel === 'brush';
      canvas.renderAll();
    }
}, [canvas, nowPanel]);
  • canvas의 드로잉 모드를 true로 변경하고 renderAll 메서드를 실행합니다.

위처럼 코드를 변경하면 아래와 같이 브러쉬 패널에서 정상적으로 브러쉬 기능이 작동하는 것을 확인할 수 있습니다.



2. 객체 선택 여부 (selectable)

해당 프로젝트에서는 스티커 패널에서 원하는 스티커를 선택하면 캔버스에 스티커가 추가되고 스티커를 이동, 크기와 방향 조절, 삭제 등을 할 수 있습니다. 브러쉬 기능과 마찬가지로 스티커 기능도 다른 패널에서는 스티커 조작을 하지 못하도록 하려면 어떻게 해야 할까요? 바로 selectable 속성을 사용하면 됩니다.


isDrawingMode처럼 useEffect에서 selectablefalse로 바꿔주면 되지 않나요?

라고 하실 수도 있습니다. 비슷하지만 조금 다릅니다.


브러쉬 기능의 isDrawingMode는 캔버스에 그림을 그릴 수 있는 모드를 활성화 하냐 안하냐를 선택하는 속성입니다. 때문에 조금은 쉽게 isDrawingMode 속성을 동적으로 조작할 수 있습니다.

반면, 스티커 기능은 스티커를 누를 때마다 캔버스에 이미지를 추가합니다. 이때(이미지를 추가할 때), selectable 속성을 truefalse로 할 수 있습니다.

기본값은 false이고 true라고 하면 이미지의 크기와 방향을 조절하고 이동할 수 있는 선택 영역이 자동으로 생깁니다. (위 이미지에서 파란색 부분)

코드로 자세히 살펴봅시다.

fabric.Image.fromURL(url, (img) => {
    const newImg = img.set({ selectable: true });
    canvas.add(newImg);
    canvas.renderAll();
});
  • 이미지의 url을 전달하여 캔버스에 이미지를 추가할 수 있습니다.
  • 이때 이미지의 selectable 여부를 결정할 수 있습니다.

이미지의 selectable 여부를 동적으로 결정하고 싶을 때는 어떻게 해야할까요? 위에서 언급했던 것처럼 단순히 useEffect 에서 selectabletruefalse로 변경하면 제대로 동작하지 않습니다.

왜냐하면 위의 이미지를 추가하는 코드는 여러 개의 이미지 중 하나를 선택해 선택한 하나의 이미지만을 캔버스에 추가하는 코드이기 때문입니다. 다시 말해, 위의 코드는 이미지 하나만을 관리하는 코드입니다. 때문에 여러 개의 스티커를 추가한 상황에서 다른 패널로 이동하면 생각한대로 동작하지 않습니다.


방법은 여러 개의 스티커의 selectable 속성을 한 번에 처리해야 합니다. 여기서 fabric에서 제공하는 다양한 메서드를 사용할 수 있습니다.

캔버스에 이미지를 추가하면 해당 이미지는 객체로 관리됩니다. fabric의 getObjects() 메서드를 사용하면 캔버스의 모든 객체를 불러올 수 있습니다.

공식 문서를 보면 객체의 배열을 반환한다고 나와있습니다. 때문에 배열을 돌면서 개별 객체의 selectable 속성을 변경해주면 됩니다.


코드로 확인해 봅시다.

useEffect(() => {
  if (canvas) {
    const objects = canvas.getObjects();
  
    for (const obj of objects) {
      obj.selectable = nowPanel === 'sticker';
    }
    canvas.renderAll();
  }
}, [canvas, nowPanel]);
  • objects 변수에 캔버스의 모든 객체 배열을 담습니다.
  • for ... ofobjects 배열을 돌면서 객체의 selectable을 현재 패널이 'sticker'일때만 true가 되도록 하였습니다.

위처럼 코드를 작성하면 스티커 패널일 때만 스티커를 조작할 수 있습니다.



다음 포스트에서는

그림을 그린 후 실행 취소와 다시 실행을 할 수 있는 버튼을 만들어 보겠습니다. 감사합니다.




출처
🔗 Fabric.js 공식 홈페이지: http://fabricjs.com/

profile
직접 만드는 게 좋은 프론트엔드 개발자

0개의 댓글