Node.js에서 캔버스(Canvas)로 이미지 만들고 저장하는 방법

남양주개발자

·

2021. 1. 4. 09:00

728x90
반응형

Node.js에서 캔버스(Canvas) 사용하는 방법

Node.js에서 캔버스를 사용해서 이미지로 저장하는 방법은 canvas 라이브러리를 사용하면 간단히 구현할 수 있습니다.

Canvas 라이브러리 설치

npm install canvas
// or
yarn add canvas

사용방법

아래 예시는 이름과 디스크립션 그리고 아이콘을 적절하게 배치하여 캔버스 이미지를 만들고 jpg로 이미지를 추출해주는 예시입니다.

전체 코드 구현 예시

const generateETFThumbnail = (etfName) => {
  return new Promise(async (resolve) => {
    const tmpdir = os.tmpdir();
    const filename = `${uuidv4()}.png`;
    const filepath = path.join(tmpdir, filename);
    const { createCanvas, Image } = require("canvas");
    const bgColor = randomColor({
      luminosity: "dark",
    });
    const canvas = createCanvas(580, 340);
    const ctx = canvas.getContext("2d");
    const title = `${etfName} ETF`;
    const description = "미국 ETF 정리";

    // draw background
    ctx.fillStyle = bgColor;
    ctx.fillRect(0, 0, canvas.width, canvas.height);

    // draw title
    ctx.font = "54px Helvetica";
    const {
      actualBoundingBoxLeft,
      actualBoundingBoxRight,
      emHeightAscent: titleHeight,
      actualBoundingBoxAscent,
      actualBoundingBoxDescent,
      emHeightDescent,
    } = ctx.measureText(title);
    const titlePosition = {
      x: canvas.width / 2,
      y: canvas.height / 2 + emHeightDescent,
    };
    // - ((textPaint.descent() + textPaint.ascent()) / 2
    ctx.fillStyle = "#fff";
    ctx.textAlign = "center";
    ctx.fillText(title, titlePosition.x, titlePosition.y);

    // draw thumbnail icon
    const icon = await loadImage("../posts/images/thumbnail_icon.png");
    ctx.drawImage(icon, titlePosition.x - 32, titlePosition.y - 128, 64, 64);

    // draw bottom line
    ctx.strokeStyle = "#fff";
    ctx.lineWidth = 5;
    ctx.beginPath();
    ctx.lineTo(120, titlePosition.y + 40);
    ctx.lineTo(455, titlePosition.y + 40);
    ctx.stroke();

    // draw description
    ctx.fillStyle = "#fff";
    ctx.textAlign = "center";
    ctx.font = "32px Arial";
    ctx.fillText(description, canvas.width / 2, canvas.height - 64);

    const stream = canvas.createPNGStream();

    // 파일시스템에 저장하기 위한 파일 스트림 생성
    const out = fs.createWriteStream(filepath);

    stream.pipe(out);
    out.on("finish", () => {
      resolve(filepath);
    });
  });
};

하나하나 뜯어서 코드를 파악해봅시다. 아래는 os.tmpdir 메서드를 활용해서 임시 디렉토리를 생성하는 구문입니다. uuidv4 메서드를 활용해서 랜덤이름을 부여한 이미지 이름을 만들어줍니다. (임시 저장소에 저장한 이미지는 이미지를 사용한 후 임시 저장소에 저장된 이미지를 제거해줘야 합니다.)

const tmpdir = os.tmpdir();
const filename = `${uuidv4()}.png`;
const filepath = path.join(tmpdir, filename);

캔버스 라이브러리에서 createCanvas를 사용해서 캔버스를 생성합니다. 그리고 캔버스에서 사용할 백그라운드 색상과 타이틀 그리고 디스크립션을 정의합니다.

const { createCanvas, Image } = require("canvas");
const bgColor = randomColor({
  luminosity: "dark",
});
const canvas = createCanvas(580, 340);
const ctx = canvas.getContext("2d");
const title = `${etfName} ETF`;
const description = "미국 ETF 정리";

배경 색상을 추가합니다. 배경 색상은 캔버스의 전체 크기에 적용됩니다.

// draw background
ctx.fillStyle = bgColor;
ctx.fillRect(0, 0, canvas.width, canvas.height);

타이틀 텍스트를 캔버스에 추가합니다. 우선 캔버스 폰트를 정의한 후 타이틀 텍스트의 크기에 관련된 속성들을 가져온 후 타이틀 포지션을 지정해줍니다.

// draw title
ctx.font = "54px Helvetica";
const {
  actualBoundingBoxLeft,
  actualBoundingBoxRight,
  emHeightAscent: titleHeight,
  actualBoundingBoxAscent,
  actualBoundingBoxDescent,
  emHeightDescent,
} = ctx.measureText(title);
const titlePosition = {
  x: canvas.width / 2,
  y: canvas.height / 2 + emHeightDescent,
};
// - ((textPaint.descent() + textPaint.ascent()) / 2
ctx.fillStyle = "#fff";
ctx.textAlign = "center";
ctx.fillText(title, titlePosition.x, titlePosition.y);

미리 준비한 아이콘 이미지를 캔버스에 추가합니다. 원하는 위치(좌표)를 설정해서 아이콘 이미지를 배치해주세요.

// draw thumbnail icon
const icon = await loadImage("../posts/images/thumbnail_icon.png");
ctx.drawImage(icon, titlePosition.x - 32, titlePosition.y - 128, 64, 64);

캔버스에 선(Line)을 한줄 추가합니다. 캔버스에 선을 추가하는 방법은 아래와 같습니다. 캔버스에 선을 긋기 위해서는 기본 속성값을 모두 직접 적용해줘야 합니다. 선의 색상과 굵기 그리고 시작점과 끝점을 지정합니다. 캔버스에 선을 추가했다면, 마지막으로 디스크립션 텍스트를 배치합니다. 배치하는 방법은 타이틀과 동일합니다.

// draw bottom line
ctx.strokeStyle = "#fff";
ctx.lineWidth = 5;
ctx.beginPath();
ctx.lineTo(120, titlePosition.y + 40);
ctx.lineTo(455, titlePosition.y + 40);
ctx.stroke();

// draw description
ctx.fillStyle = "#fff";
ctx.textAlign = "center";
ctx.font = "32px Arial";
ctx.fillText(description, canvas.width / 2, canvas.height - 64);

필자는 PNG 확장자로 이미지를 저장할 것이기 때문에 캔버스의 createPNGStream 메서드를 활용해서 스트림을 추가하겠습니다. 아래 단계를 거치면 PNG 확장자를 가진 이미지가 저장되게 됩니다.

const stream = canvas.createPNGStream();

// 파일시스템에 저장하기 위한 파일 스트림 생성
const out = fs.createWriteStream(filepath);

stream.pipe(out);

out.on("finish", () => {
  resolve(filepath);
});

결과물

캔버스로 생성된 이미지의 결과는 아래와 같습니다.

캔버스 라이브러리로 생성한 이미지 예시

728x90
반응형
그리드형

이 포스팅은 쿠팡파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.

💖 저자에게 암호화폐로 후원하기 💖

아이콘을 클릭하면 지갑 주소가자동으로 복사됩니다