」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > 建立一個 React Hook 以任意角度旋轉影像

建立一個 React Hook 以任意角度旋轉影像

發佈於2024-11-08
瀏覽:351

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]刪除
最新教學 更多>
  • 如何有效率地將資料從SQL Server 2005遷移到MySQL?
    如何有效率地將資料從SQL Server 2005遷移到MySQL?
    將資料從SQL Server 2005 匯出到MySQL在資料遷移領域,從SQL Server 2005 到MySQL 的過渡可能會帶來挑戰。一項特別艱鉅的任務是提取儲存在近 300 個表中的大量資料並將其轉換為相容的 MySQL 資料庫。 使用 bcp 的局限性,例如無效的 CSV 格式以及需要手...
    程式設計 發佈於2024-12-22
  • 為什麼我無法透過 Ruby on Rails 3 應用程式中的套接字檔案連接到 MySQL 伺服器?
    為什麼我無法透過 Ruby on Rails 3 應用程式中的套接字檔案連接到 MySQL 伺服器?
    使用套接字連接在 Ruby on Rails 3 中建立 MySQL 連線在 macOS 上的 Ruby on Rails 3環境中管理資料庫連線時,使用者嘗試執行遷移時可能會遇到以下錯誤:「無法透過套接字'/tmp/mysql.sock'連接到本機MySQL伺服器」 (2)」。此錯...
    程式設計 發佈於2024-12-22
  • 從程式碼到聊天:幫我免費部署我的 Node.js 和 React 應用程式!
    從程式碼到聊天:幫我免費部署我的 Node.js 和 React 應用程式!
    大家好, 我很高興分享我已經使用 Node.js、React 和 Socket.io 開發了一個聊天應用程式! ?這是一次令人難以置信的學習經歷,我對它的結果感到自豪。該應用程式允許即時訊息傳遞,並且我已經實現了用戶身份驗證和聊天室等功能。 現在我已經建立了它,我希望免費部署它,但我可以使用一些指...
    程式設計 發佈於2024-12-22
  • 為什麼 `mysqli_query()` 回傳「警告:mysqli_query() 期望參數 1 為 MySQLi,給定 null」?
    為什麼 `mysqli_query()` 回傳「警告:mysqli_query() 期望參數 1 為 MySQLi,給定 null」?
    理解「警告:mysqli_query() 期望參數1 為MySQLi,在中給出null」錯誤在您嘗試建立自訂CMS,您遇到以下錯誤訊息:「警告:mysqli_query() 期望參數1 為MySQLi, null Give in"錯誤原因此錯誤表示執行 SQL 查詢的 mysqli_que...
    程式設計 發佈於2024-12-22
  • 為什麼介面對於掌握 Java 中的物件導向程式設計至關重要?
    為什麼介面對於掌握 Java 中的物件導向程式設計至關重要?
    介面:增強 OOP 的橋樑在 Java 世界中,了解介面的原因、內容和方式對於掌握物件導向程式設計。這裡有一個全面的細分:什麼是介面? 介面是純抽象的集合-沒有實作和最終欄位的抽象方法。這意味著介面定義契約而不是提供程式碼片段。 為什麼要使用介面? 介面提供了幾個好處:合約執行: 他們確保實施類別遵...
    程式設計 發佈於2024-12-22
  • 像 V8 和 JavaScriptCore 這樣的 JavaScript 引擎是否使用字串實習?
    像 V8 和 JavaScriptCore 這樣的 JavaScript 引擎是否使用字串實習?
    JavaScript引擎會使用String Interning嗎? 簡介:在程式設計中,string interning是指重複使用現有字串物件的過程而不是為相同的字串建立新的字串。這種優化技術旨在減少記憶體使用並提高效能。問題是,常見的 JavaScript 引擎,包括 V8 和 WebKit 的...
    程式設計 發佈於2024-12-22
  • 如何擷取 Go 範本輸出並將其指派給變數?
    如何擷取 Go 範本輸出並將其指派給變數?
    在Go 中捕獲模板輸出在Go 模板中,捕獲子模板的輸出或直接將其分配給變量是默認不支持。不過,這可以透過註冊自訂函數並使用位元組緩衝區接收模板的結果來實現。 自訂函數註冊要擷取範本輸出,請註冊Template.Funcs() 的函數將範本名稱作為參數並以字串形式傳回範本的輸出:func execTe...
    程式設計 發佈於2024-12-22
  • Go 的短路評估會影響條件語句的效能嗎?
    Go 的短路評估會影響條件語句的效能嗎?
    Go 中的短路求值在程式設計中,短路求值是一種僅在需要確定周圍語句的結果時才對錶達式求值的技術。這通常用在條件語句中,如果先前的條件已經為 false,則無需對多個條件進行求值。 Go 實作邏輯運算子(&& 和 ||)的短路求值,與許多其他程式設計類似語言。這意味著在 if 語句中,解釋器將從左到右...
    程式設計 發佈於2024-12-22
  • HTML 格式標籤
    HTML 格式標籤
    HTML 格式化元素 **HTML Formatting is a process of formatting text for better look and feel. HTML provides us ability to format text without us...
    程式設計 發佈於2024-12-22
  • 我應該使用哪些 g++ 警告標誌來進行全面的 C++ 程式碼分析?
    我應該使用哪些 g++ 警告標誌來進行全面的 C++ 程式碼分析?
    使用g 進行C 編譯的徹底而詳細的警告標誌Gcc 提供了一套全面的警告標誌來幫助開發人員檢測潛在問題他們的代碼。若要在C 中啟用徹底且詳細的警告,請考慮以下建議:基本警告:-迂腐:遵守嚴格C語言標準。 -Wall:啟動所有普遍接受的warnings.-Wextra:將警告範圍擴大到-Wall之外。 ...
    程式設計 發佈於2024-12-22
  • 如何在 PHP 中組合兩個關聯數組,同時保留唯一 ID 並處理重複名稱?
    如何在 PHP 中組合兩個關聯數組,同時保留唯一 ID 並處理重複名稱?
    在 PHP 中組合關聯數組在 PHP 中,將兩個關聯數組組合成一個數組是常見任務。考慮以下請求:問題描述:提供的代碼定義了兩個關聯數組,$array1 和 $array2。目標是建立一個新陣列 $array3,它合併兩個陣列中的所有鍵值對。 此外,提供的陣列具有唯一的 ID,而名稱可能重疊。要求是建...
    程式設計 發佈於2024-12-22
  • Visual Studio 是否完全支援現代 C/C++ 標準?
    Visual Studio 是否完全支援現代 C/C++ 標準?
    Visual Studio 對現代C/C 標準的支援在軟體開發領域,C 和C 程式語言隨著引入C99 和C 11 等新標準,可望帶來一系列有益的功能。然而,問題出現了:這些進步會進入微軟的 Visual Studio IDE(整合開發環境)嗎? 微軟的立場微軟支持新 C 的官方立場/C 標準相當平淡...
    程式設計 發佈於2024-12-22
  • 方法鏈如何增強 Java 程式碼操作?
    方法鏈如何增強 Java 程式碼操作?
    Java 中的方法鏈:實作程式碼操作的流暢性要在Java 中實作方法鏈,請修改方法簽章以傳回“this “ 目的。這允許方法調用的無縫鏈接,如下所示:public Dialog setMessage(String message) { // Message setting logic ...
    程式設計 發佈於2024-12-22
  • 如何從 Matplotlib 圖中刪除科學記數法和偏移量?
    如何從 Matplotlib 圖中刪除科學記數法和偏移量?
    從Matplotlib 圖中刪除科學記數法您有一個帶有科學記數法和軸偏移量的圖,並且您想要消除它們。 停用偏移偏移量與科學記數法分開,並加入軸上顯示的數字。要停用它,請使用:plt.ticklabel_format(useOffset=False)停用科學記數法要阻止科學記數法,請使用: ]plt....
    程式設計 發佈於2024-12-22
  • 如何在 Java 中將位元組數組轉換為字串並返回位元組數組?
    如何在 Java 中將位元組數組轉換為字串並返回位元組數組?
    Java 中位元組數組轉字串並傳回位元組數組假設您有一個初始byte[] 數組,需要將其轉換為字串表示形式。隨後,您想要將該字串轉換回 byte[] 陣列。這種轉換對於各種場景下的資料傳輸和處理至關重要。 要將 byte[] 陣列轉換為字串,可以使用 Arrays.toString() 方法。此方法...
    程式設計 發佈於2024-12-22

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3