En el desarrollo web, es posible que necesites rotar una imagen, lo cual es fácil de hacer en CSS. Código simple como esta transformación: rotar (90 grados); ¿Pero qué pasa si queremos hacerlo en JS?
Dibuja la imagen en el lienzo en el entorno del navegador y la gira. Pero antes de eso, necesitamos hacer algunos cálculos para mantener la relación de aspecto original de la imagen.
Suponiendo que hemos cargado la imagen, el cálculo de la imagen rotada se puede hacer de la siguiente manera:
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);
Y a continuación, utilizamos algunas de las API del lienzo para realizar la rotación 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');
Con el código central implementado, podemos hacer algunas optimizaciones y escribir ganchos de React dedicados para usarlo:
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;
Aquí reutilizo el mismo elemento de lienzo para reducir la creación repetida. En segundo lugar, cabe señalar que establecí su ancho y alto en 0 después de cada rotación para reducir el uso de memoria. Por cierto, también hice la operación de limpiar el lienzo. Esto se debe a que en la especificación HTML, cuando modifica el ancho y el alto del lienzo (si es el mismo que antes), se borrará el lienzo, que es lo mismo que canvasCtx2D.clearRect(0, 0, canvasWidth, canvasHeight), que es compatible con los navegadores modernos.
En useRotateImage, mantengo una referencia al elemento de imagen y configuro el estado de la imagen girada después de image.decode(), que se resuelve una vez que los datos de la imagen están listos.
A continuación se muestra un caso de uso en línea:
Si esto te resultó útil, considera suscribirte a mi boletín informativo para obtener más artículos y herramientas útiles sobre desarrollo web. ¡Gracias por leer!
Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.
Copyright© 2022 湘ICP备2022001581号-3