"일꾼이 일을 잘하려면 먼저 도구를 갈고 닦아야 한다." - 공자, 『논어』.
첫 장 > 프로그램 작성 > 어떤 각도에서든 이미지를 회전할 수 있는 React Hook 만들기

어떤 각도에서든 이미지를 회전할 수 있는 React Hook 만들기

2024-11-08에 게시됨
검색:205

Creating a React Hook for Rotating Images at Any Angle

웹 개발 시 이미지 회전이 필요할 수 있는데, 이는 CSS에서 쉽게 수행할 수 있습니다. 다음 변환과 같은 간단한 코드:rotate(90deg);. 하지만 JS에서 하고 싶다면 어떻게 해야 할까요?

TLDR

브라우저 환경에서 캔버스에 이미지를 그려서 회전시킵니다. 하지만 그 전에 원본 이미지 종횡비를 유지하기 위해 몇 가지 계산을 수행해야 합니다.

핵심

이미지를 로드했다고 가정하면 회전된 이미지를 계산하는 방법은 다음과 같습니다.

const { PI, sin, cos, abs } = Math;
const angle = (degree * PI) / 180;
const sinAngle = sin(angle);
const cosAngle = cos(angle);

const rotatedWidth = abs(imageWidth * cosAngle)   abs(imageHeight * sinAngle);

const rotatedHeight = abs(imageWidth * sinAngle)   abs(imageHeight * cosAngle);

다음으로 캔버스 API 중 일부를 사용하여 실제 회전을 수행합니다.

const canvas = document.createElement('canvas');

const { width: canvasWidth, height: canvasHeight } = canvas;
const canvasCtx2D = canvas.getContext('2d');

canvasCtx2D.clearRect(0, 0, canvasWidth, canvasHeight);
canvasCtx2D.translate(canvasWidth / 2, canvasHeight / 2);
canvasCtx2D.rotate(angle);

canvasCtx2D.drawImage(
  image,
  -imageWidth / 2,
  -imageHeight / 2,
  imageWidth,
  imageHeight,
);

return canvas.toDataURL('image/png');

마무리

핵심 코드가 있으면 일부 최적화를 수행하고 이를 사용하기 위한 전용 React 후크를 작성할 수 있습니다.

import { useEffect, useRef, useState } from 'react';

type RotatedImage = {
  src: string;
  width: number;
  height: number;
} | null;

let canvas: HTMLCanvasElement | null = null;
let canvasCtx2D: CanvasRenderingContext2D | null = null;

const getRotatedImage = (
  image: HTMLImageElement | null,
  rotation: number,
): RotatedImage => {
  canvas ??= document.createElement('canvas');
  canvasCtx2D ??= canvas.getContext('2d');

  if (!image || !canvasCtx2D) return null;

  const { width: imageWidth, height: imageHeight, currentSrc } = image;
  const degree = rotation % 360;
  if (!degree) {
    return {
      src: currentSrc,
      width: imageWidth,
      height: imageHeight,
    };
  }

  const { PI, sin, cos, abs } = Math;
  const angle = (degree * PI) / 180;
  const sinAngle = sin(angle);
  const cosAngle = cos(angle);

  canvas.width = abs(imageWidth * cosAngle)   abs(imageHeight * sinAngle);
  canvas.height = abs(imageWidth * sinAngle)   abs(imageHeight * cosAngle);

  // The width and height of the canvas will be automatically rounded.
  const { width: canvasWidth, height: canvasHeight } = canvas;

  canvasCtx2D.clearRect(0, 0, canvasWidth, canvasHeight);
  canvasCtx2D.translate(canvasWidth / 2, canvasHeight / 2);
  canvasCtx2D.rotate(angle);

  canvasCtx2D.drawImage(
    image,
    -imageWidth / 2,
    -imageHeight / 2,
    imageWidth,
    imageHeight,
  );

  const src = canvas.toDataURL('image/png');
  canvas.width = 0;
  canvas.height = 0;

  return {
    src,
    width: canvasWidth,
    height: canvasHeight,
  };
};

const useRotateImage = (imageSrc: string, rotation?: number): RotatedImage => {
  const imageEle = useRef(null);
  const [rotatedImage, setRotatedImage] = useState(null);

  useEffect(() => {
    if (typeof rotation === 'number') {
      let currImage = imageEle.current;

      if (currImage?.currentSrc !== imageSrc) {
        currImage = new Image();
        imageEle.current = currImage;

        currImage.src = imageSrc;
      }

      currImage.decode().then(
        () => setRotatedImage(getRotatedImage(currImage, rotation)),
        () => setRotatedImage(null),
      );
    }
  }, [imageSrc, rotation]);

  return rotatedImage;
};

export default useRotateImage;

여기서는 동일한 캔버스 요소를 재사용하여 반복 생성을 줄입니다. 둘째, 메모리 사용량을 줄이기 위해 각 회전 후에 너비와 높이를 0으로 설정했다는 점에 유의해야 합니다. 그런데, 캔버스를 지우는 작업도 했습니다. 이는 HTML 사양에서 캔버스의 너비와 높이(이전과 동일한지 여부)를 수정하면 canvasCtx2D.clearRect(0, 0, canvasWidth, canvasHeight)와 동일한 캔버스가 지워지기 때문입니다. 최신 브라우저에서 지원됩니다.

useRotateImage에서는 이미지 요소에 대한 참조를 유지하고 이미지 데이터가 준비된 후 해결되는 image.decode() 이후에 회전된 이미지 상태를 설정합니다.

다음은 온라인 사용 사례입니다.


이 정보가 도움이 되었다면 웹 개발에 대한 더 유용한 기사와 도구를 보려면 내 뉴스레터를 구독 해 보세요. 읽어주셔서 감사합니다!

릴리스 선언문 이 글은 https://dev.to/zacharylee/creating-a-react-hook-for-rotating-images-at-any-angle-4nnb?1에서 복제됩니다. 침해 내용이 있는 경우, Study_golang@163으로 문의하시기 바랍니다. .com에서 삭제하세요
최신 튜토리얼 더>

부인 성명: 제공된 모든 리소스는 부분적으로 인터넷에서 가져온 것입니다. 귀하의 저작권이나 기타 권리 및 이익이 침해된 경우 자세한 이유를 설명하고 저작권 또는 권리 및 이익에 대한 증거를 제공한 후 이메일([email protected])로 보내주십시오. 최대한 빨리 처리해 드리겠습니다.

Copyright© 2022 湘ICP备2022001581号-3