"Se um trabalhador quiser fazer bem o seu trabalho, ele deve primeiro afiar suas ferramentas." - Confúcio, "Os Analectos de Confúcio. Lu Linggong"
Primeira página > Programação > Criando um gancho React para girar imagens em qualquer ângulo

Criando um gancho React para girar imagens em qualquer ângulo

Publicado em 13/09/2024
Navegar:772

Creating a React Hook for Rotating Images at Any Angle

No desenvolvimento web, pode ser necessário girar uma imagem, o que é fácil de fazer em CSS. Código simples como este transform: rotate(90deg);. Mas e se quisermos fazer isso em JS?

TLDR

Desenha a imagem para a tela no ambiente do navegador e a gira. Mas antes disso, precisamos fazer algumas contas para manter a proporção da imagem original.

Essencial

Supondo que carregamos a imagem, o cálculo da imagem girada pode ser feito da seguinte forma:

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);

E a seguir, usamos algumas das APIs de tela para fazer a rotação real:

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');

Concluir

Com o código principal implementado, podemos fazer algumas otimizações e escrever ganchos React dedicados para usá-lo:

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;

Aqui eu reutilizo o mesmo elemento canvas para reduzir a criação repetida. Em segundo lugar, deve-se notar que defini sua largura e altura como 0 após cada rotação para reduzir o uso de memória. Aliás, também fiz a operação de limpeza da tela. Isso ocorre porque na especificação HTML, quando você modifica a largura e a altura da tela (se é a mesma de antes), a tela será limpa, que é igual a canvasCtx2D.clearRect(0, 0, canvasWidth, canvasHeight), que é compatível com navegadores modernos.

Em useRotateImage, mantenho uma referência ao elemento de imagem e defino o estado da imagem girada após image.decode(), que é resolvido depois que os dados da imagem estão prontos.

Abaixo está um caso de uso on-line:


Se você achou isso útil, considere assinar meu boletim informativo para artigos e ferramentas mais úteis sobre desenvolvimento web. Obrigado por ler!

Declaração de lançamento Este artigo foi reproduzido em: https://dev.to/zacharylee/creating-a-react-hook-for-rotating-images-at-any-angle-4nnb?1 Se houver alguma violação, entre em contato com study_golang@163 .com para excluí-lo
Tutorial mais recente Mais>

Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.

Copyright© 2022 湘ICP备2022001581号-3