В веб-разработке вам может потребоваться повернуть изображение, что легко сделать с помощью CSS. Простой код вроде этого преобразования: Rotate(90deg);. Но что, если мы хотим сделать это на JS?
Рисует изображение на холсте в среде браузера и поворачивает его. Но перед этим нам нужно выполнить некоторые математические вычисления, чтобы сохранить исходное соотношение сторон изображения.
Предполагая, что мы загрузили изображение, вычисление повернутого изображения можно выполнить следующим образом:
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(), которое разрешается после того, как данные изображения готовы.
Ниже приведен пример использования в Интернете:
Если вы нашли это полезным, рассмотрите подписку на мою рассылку новостей чтобы получать больше полезных статей и инструментов о веб-разработке. Спасибо за прочтение!
Отказ от ответственности: Все предоставленные ресурсы частично взяты из Интернета. В случае нарушения ваших авторских прав или других прав и интересов, пожалуйста, объясните подробные причины и предоставьте доказательства авторских прав или прав и интересов, а затем отправьте их по электронной почте: [email protected]. Мы сделаем это за вас как можно скорее.
Copyright© 2022 湘ICP备2022001581号-3