[Canvas] 이미지 에디터 만들기 1: 캔버스에 이미지 띄우기

profile
마릴린벅시
2024. 8. 26. 15:38프론트엔드

 

 

 

 

 

⭐️ konva.js 설치

react프로젝트에 konva.js를 설치해준다.

- npm install konva

 

⭐️ 빈 div 엘리먼트 준비

export default function Canvas() {
  return (
    <>
      <div id='container'></div>
    </>
  );
}

 

⭐️ Stage, layer 준비

Konva.js를 이용해 캔버스 위에 이미지를 띄우기 위해서는

기본적으로 Stage와 Layer가 필요하다.

Stage는 캔버스이고,

Layer는 포토샵의 레이어와 마찬가지로 캔버스 위에 레이어를 하나 까는 것이다.

필요에 따라 이 Layer는 여러겹 올릴 수 있다.

일단 기본적으로 하나의 레이어가 필수로 필요하다.

initialize함수를 추가한다.

여기서 Stage를 초기화 할 때 container에는 div의 id값을 전달해주고,

캔버스 사이즈인 width, height를 전달한다. 여기까지 하면 div엘리먼트에 캔버스가 주입된다.

그 다음은 layer를 생성하여 stage에 올려준다. (stage.add)

import { useCallback, useEffect, useRef } from 'react';
import Konva from 'konva';
import { Stage } from 'konva/lib/Stage';
import { Layer } from 'konva/lib/Layer';

const CANVAS_SIZE = 512;

export default function Canvas2() {
  const source = '/images/bottle.jpeg';
  const stageRef = useRef<Stage | null>(null);
  const layerRef = useRef<Layer | null>(null);

  /* 1. Stage와 Layer를 생성. 계속 재사용 될 객체를 ref에 넣어준다. */
  const initialize = useCallback(() => {
    const stage = new Konva.Stage({
      container: 'container', // <div id='container'></div>의 id와 연결
      width: CANVAS_SIZE,
      height: CANVAS_SIZE,
    });
    stageRef.current = stage;

    const layer = new Konva.Layer();
    layerRef.current = layer;
    stage.add(layer);
  }, []);


  useEffect(() => {
    if (!source) return;
    initialize();
  }, [initialize]);

  return <div id='container'></div>;
}

 

⭐️ 이미지 띄우기 (최종코드)

drawImage함수를 추가한다.

미리 준비해둔 이미지(source)를 layer에 추가해주는 함수이다.

이미지가 로드되지 않을 경우 사용자에게 alert를 보여주기 위해서 try-catch문을 사용했다.

초기화할 이미지 정보를 imageOpt에 정의한 뒤

new Konva.Iamge 생성자 함수에 인자로 전달해준다.

width, height는 이미지 크기이고 image는 load된 이미지 객체, id는 전달하지 않아도 상관없다.

여기서 draggable에 true값을 할당하면 캔버스 위의 이미지를 마우스로 여기저기 움직일 수 있게 된다.

이미지의 width, height가 캔버스의 크기보다 크면

당연히 이미지가 잘리기 때문에 이 부분은

추후 캔버스 사이즈에 맞게 이미지를 축소하는 함수를 추가해 줄 예정이다.

new Konva.Image로 생성 된 이미지 객체를 레이어에 추가해준다. (layerRef.current.add)

그리고 추후 이미지 객체를 조작해야 하기 때문에

이미지 객체도 ref에 저장해둔다. (imageRef.current = defaultImage)

import { useCallback, useEffect, useRef } from 'react';
import Konva from 'konva';
import { Stage } from 'konva/lib/Stage';
import { Layer } from 'konva/lib/Layer';
import { Node } from 'konva/lib/Node';

const CANVAS_SIZE = 512;

export default function Canvas2() {
  const source = '/images/bottle.jpeg';
  const stageRef = useRef<Stage | null>(null);
  const layerRef = useRef<Layer | null>(null);
  const imageRef = useRef<Node | null>(null);

  /* 1. Stage와 Layer를 생성 */
  const initialize = useCallback(() => {
    const stage = new Konva.Stage({
      container: 'container', // <div id='container'></div>의 id와 연결
      width: CANVAS_SIZE,
      height: CANVAS_SIZE,
    });
    stageRef.current = stage;

    const layer = new Konva.Layer();
    layerRef.current = layer;
    stage.add(layer);
  }, []);

  /* 2. 위에서 생성한 Layer위에 이미지 객체를 올려주기. */
  const drawImage = useCallback(async () => {
    if (layerRef.current) {
      const imageObj = new Image();
      imageObj.src = source;
      try {
        await new Promise((resolve, reject) => {
          imageObj.onload = resolve;
          imageObj.onerror = reject;
        });

        const imageOpt = {
          width: CANVAS_SIZE,
          height: CANVAS_SIZE,
          image: imageObj,
          id: 'imageId',
          draggable: true, //true로 설정 시 캔버스 내에서 이미지를 여기저기 옮길 수 있다.
        };

        const defaultImage = new Konva.Image(imageOpt);

        imageRef.current = defaultImage;
        layerRef?.current?.add(defaultImage);
      } catch (error) {
        alert('이미지 로드에 실패했다!');
      }
    }
  }, []);

  /* 3. DOM이 렌더 된 뒤에 실행되어야 하므로 useEffect안에서 initialize, drawImage를 순서대로 실행해준다. */
  useEffect(() => {
    if (!source) return;
    initialize();
    drawImage();
  }, [initialize, drawImage]);

  return <div id='container'></div>;
}
 

 

 

⭐️ 최종 코드 깃헙에서 보기

아래의 레포를 clone받은 뒤 9733d88커밋으로 체크아웃 하면 여기까지의 코드를 볼 수 있다.

https://github.com/seoulsaram/canvas-search

 

GitHub - seoulsaram/canvas-search

Contribute to seoulsaram/canvas-search development by creating an account on GitHub.

github.com

 

반응형