«Если рабочий хочет хорошо выполнять свою работу, он должен сначала заточить свои инструменты» — Конфуций, «Аналитики Конфуция. Лу Лингун»
титульная страница > программирование > Создание React Hook для поворота изображений под любым углом

Создание React Hook для поворота изображений под любым углом

Опубликовано 8 ноября 2024 г.
Просматривать:793

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