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?
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.
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');
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!
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