”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 创建一个 React Hook 以任意角度旋转图像

创建一个 React Hook 以任意角度旋转图像

发布于2024-09-13
浏览:952

Creating a React Hook for Rotating Images at Any Angle

在Web开发中,您可能需要旋转图像,这在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 hook 来使用它:

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;

这里我复用了同一个canvas元素,以减少重复创建。其次,需要注意的是,我在每次旋转后将其宽度和高度设置为0,以减少内存使用。对了,我还做了清理画布的操作。这是因为在HTML规范中当你修改画布的宽度和高度时(无论是否与之前相同)都会清除画布,这与canvasCtx2D.clearRect(0, 0, canvasWidth, canvasHeight)相同,这现代浏览器支持。

在useRotateImage中,我保留对图像元素的引用,并在image.decode()之后设置旋转图像状态,该状态在图像数据准备好后解析。

以下是一个在线用例:


如果您觉得这有帮助,请考虑 订阅我的时事通讯 以获取更多有关 Web 开发的有用文章和工具。感谢您的阅读!

版本声明 本文转载于:https://dev.to/zacharylee/creating-a-react-hook-for-rotating-images-at-any-angle-4nnb?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 如何将图标放置在图像或视频上并在点击时触发下载?
    如何将图标放置在图像或视频上并在点击时触发下载?
    在图像或视频上放置图标问题:您需要在图像或视频上放置图标图像或视频,将其与左下角对齐。单击时,该图标应触发图像的下载提示。解决方案:要实现所需的定位,需要在图像周围创建一个相对容器。然后,将图标的位置设置为绝对位置。这是一个代码示例:.container { position: relative...
    编程 发布于2024-11-07
  • 使用 Jsoup 将 HTML 转换为纯文本时如何保留换行符?
    使用 Jsoup 将 HTML 转换为纯文本时如何保留换行符?
    使用 Jsoup 的 Html 到纯文本转换保留换行符Jsoup 提供了强大的 HTML 操作工具,但其默认从 HTML 到纯文本的转换文本可以合并换行符,将它们呈现为连续文本。要保留这些换行符,请按以下方式使用 Jsoup:用于保留换行符的自定义函数:提供的 Java 代码片段引入了一个自定义函数...
    编程 发布于2024-11-07
  • 如何在 C++ 中创建自定义输入流以从非标准源读取数据?
    如何在 C++ 中创建自定义输入流以从非标准源读取数据?
    在 C 中创建自定义输入流 C 中的自定义输入流提供了从非标准源读取数据的强大机制。虽然直接扩展 istream 类似乎是一个可行的选择,但建议探索其他方法以确保高效可靠的实现。从streambuf派生首选方法在 C 中创建自定义流的方法是从 std::streambuf 基类派生自定义的 Stre...
    编程 发布于2024-11-07
  • SharpAPI Laravel 集成指南
    SharpAPI Laravel 集成指南
    Welcome to the SharpAPI Laravel Integration Guide! This repository provides a comprehensive, step-by-step tutorial on how to integrate SharpAPI into y...
    编程 发布于2024-11-07
  • 如何将 Firebase 与 Laravel 集成
    如何将 Firebase 与 Laravel 集成
    Laravel and Firebase are two powerful tools that can significantly enhance the development of modern web applications. Laravel, a popular PHP framewor...
    编程 发布于2024-11-07
  • Expo with Redux Toolkit、文件系统和 Redux Persist:综合指南
    Expo with Redux Toolkit、文件系统和 Redux Persist:综合指南
    Redux Toolkit 是一个流行的库,它通过提供一组实用程序和约定来简化 Redux 开发。它包括一个减速器和动作创建模式,可以简化编写 Redux 逻辑的过程。将 Redux Persist 与 Redux Toolkit 相结合可以显着提高 React Native 应用程序中状态管理的效...
    编程 发布于2024-11-07
  • 如何处理非嵌套 Lambda 闭包中的变量作用域问题?
    如何处理非嵌套 Lambda 闭包中的变量作用域问题?
    Python Lambda 闭包作用域问题将变量封装在闭包中以将其从函数签名中删除是一种常用于高效代码结构的技术。但是,在非嵌套 lambda 的情况下,闭包保留变量的最终值,从而在尝试基于迭代变量访问特定值时导致问题。考虑提供的代码片段:names = ['a', 'b', 'c'] def te...
    编程 发布于2024-11-07
  • 如何使用现代 CSS 将按钮无缝集成到输入字段中?
    如何使用现代 CSS 将按钮无缝集成到输入字段中?
    如何使用现代 CSS 将按钮集成到输入中问题:创建视觉元素其中按钮无缝集成在输入字段中,允许正常的用户交互、保留文本可见性并保持可访问性和屏幕阅读器兼容性。解决方案:Flexbox 和表单边框最佳方法是使用弹性盒布局以及包含元素(表单)上的边框:定位:设置具有水平行布局的弹性盒,允许输入扩展以填充可...
    编程 发布于2024-11-07
  • 内核开发中的 C++:综合指南
    内核开发中的 C++:综合指南
    介绍 由于直接硬件访问和最小的运行时开销,内核开发传统上是 C 的领域。然而,由于其面向对象的特性,C 在内核编程中找到了自己的位置,这可以带来更干净、更易于维护的代码。本指南将逐步介绍如何使用 C 进行内核开发,重点是设置环境、构建项目以及使用 C 功能编写内核代码,同时牢记内核...
    编程 发布于2024-11-07
  • 在 React 项目中实现 CSS 模块
    在 React 项目中实现 CSS 模块
    React 中的 CSS 模块是一种通过自动生成唯一类名来确定 CSS 范围的方法。这可以防止大型应用程序中的类名冲突并允许模块化样式。以下是如何在 React 项目中使用 CSS 模块: 1. 设置 默认情况下,React 支持 CSS 模块。您只需使用扩展名 .module.cs...
    编程 发布于2024-11-07
  • 有哪些资源可用于实现彗星模式?
    有哪些资源可用于实现彗星模式?
    Comet:服务器推送模式服务器推送是一种在服务器和 Web 客户端之间实现双向通信的技术,已经获得了显着的成果最近的兴趣。 Comet 设计模式作为在 JavaScript 应用程序中实现服务器推送的一种有前途的方法而出现。本问题探讨了 Comet 模式的 jQuery 实现和通用资源的可用性。j...
    编程 发布于2024-11-07
  • 探索心理健康门诊项目的类型
    探索心理健康门诊项目的类型
    门诊心理健康治疗方法是一种不强调在医疗机构过夜的方案。这种疗法主要在医生办公室、医院或诊所提供,在那里人们可以进行定期治疗,以进行高度结构化的定期治疗。 在 COVID-19 大流行期间,全球约有 2.75 亿吸毒者。比前几十年高出近 22%。吸毒成瘾的增加导致全美吸毒过量人数创历史新高。好消息是门...
    编程 发布于2024-11-07
  • 如何在 C++ Builder 中初始化 OpenGL 帧:分步指南
    如何在 C++ Builder 中初始化 OpenGL 帧:分步指南
    如何在 C Builder 中初始化 OpenGL 帧在 C Builder 中的窗体内初始化 OpenGL 帧可能是一项具有挑战性的任务。在尝试调整现有 OpenGL 代码(例如问题中提供的示例)时,您可能会遇到困难。要正确创建和渲染 OpenGL 帧,请按照下列步骤操作:使用 TForm::Ha...
    编程 发布于2024-11-07
  • 利用这些罕见的 HTML 属性增强您的 Web 开发技能
    利用这些罕见的 HTML 属性增强您的 Web 开发技能
    Introduction HTML attributes are most often referred to as the overlooked heroes of web development, playing a crucial role in shaping the st...
    编程 发布于2024-11-07
  • 如何在 Python 中将字符串转换为二进制:ASCII 与 Unicode?
    如何在 Python 中将字符串转换为二进制:ASCII 与 Unicode?
    在Python中将字符串转换为二进制在Python中,您可能会遇到需要将字符串表示为二进制数字序列的情况。这对于多种原因都很有用,例如数据加密或二进制文件操作。使用 bin() 函数将字符串转换为二进制的最简单方法就是使用bin()函数。该函数接受一个字符串作为输入,并将其二进制表示形式返回为字符串...
    编程 发布于2024-11-07

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3