欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

React處理復雜圖片樣式的方法詳解

 更新時間:2024年04月26日 09:59:39   作者:慕仲卿  
這篇文章主要為大家詳細介紹了React處理復雜圖片樣式的方法,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下

正如文章題目所示,本文的目的是為了記錄工作中遇到的,在頁面中處理復雜圖片樣式的解決方案。使用的技術棧有:

  • web Worker
  • fetch
  • createImageBitmap
  • OffscreenCanvas
  • Blob

之所以稱之為“兜底”方案,是因為下文中提到的方法能夠以像素級別操作圖片。而,如果只是這樣,也沒有什么值得記錄的。但是,本文為了防止這種像素級別的操作對頁面渲染性能造成大的沖擊,結合 web worker 異步處理,解決了這個問題。

從本文的思路出發(fā),至少可以收獲兩點技術:

  • web worker 的實際使用案例
  • 前端 PS 的基礎知識

下面讓我們開始吧~

本文分為三個小節(jié),第一小節(jié)簡單的介紹上面提到的技術棧;第二小節(jié)在非工程化 Demo 中演示這個解決方案的流程;第三小節(jié)將此方法分裝成一個 React 組件,以便后面復用和維護。

1. 技術棧介紹

  • Web Worker: Web Worker是瀏覽器提供的一種能在后臺線程中運行JavaScript的技術,它不會阻塞或影響頁面的性能。通過創(chuàng)建一個新的Worker對象,可以將耗時的計算或數(shù)據(jù)處理任務放在后臺執(zhí)行,從而避免UI線程被長時間占用,提高頁面的響應性和用戶體驗。Web Worker通過postMessage和onmessage進行主線程和工作線程之間的通信。
  • Fetch: Fetch是一個現(xiàn)代、強大且靈活的網(wǎng)絡請求API,它提供了一個全局fetch()方法,用于異步獲取網(wǎng)絡資源。與XMLHttpRequest相比,F(xiàn)etch更加簡潔、高效,且支持Promise模式,使得異步處理更加直觀和方便。Fetch還支持跨域請求、請求和響應的攔截、處理HTTP管道等高級功能,是現(xiàn)代Web開發(fā)中網(wǎng)絡請求的首選方式。
  • createImageBitmap: createImageBitmap是一個用于創(chuàng)建位圖圖像的API,它可以直接從各種圖像源(如Blob、ImageData、ImageBitmap、HTMLCanvasElement等)生成一個高效的位圖。這個API是異步的,不會阻塞主線程,因此非常適合用于處理大量圖像數(shù)據(jù)。生成的ImageBitmap對象可以直接用于Canvas的drawImage方法,極大地提高了圖像渲染的性能。
  • OffscreenCanvas: OffscreenCanvas是HTML5 Canvas API的一個擴展,它允許在Web Worker中使用Canvas功能。這意味著圖像處理、渲染等計算密集型任務可以在不阻塞主線程的情況下進行。OffscreenCanvas通過transferControlToOffscreen或getContext('2d', {offscreen: true})等方式創(chuàng)建,并可以在主線程和工作線程之間傳遞,從而實現(xiàn)了真正的后臺渲染。
  • Blob: Blob(Binary Large Object)是一個用于處理二進制數(shù)據(jù)的JavaScript對象。它可以存儲大量的二進制數(shù)據(jù),并允許通過URL.createObjectURL()方法創(chuàng)建一個指向該數(shù)據(jù)的URL,這個URL可以直接用于img、audio、video等元素的src屬性。Blob常用于處理文件上傳、下載以及圖像、音頻、視頻的動態(tài)生成等場景。此外,Blob還支持切片操作,便于大數(shù)據(jù)的處理和傳輸。

這些知識不能說不常見吧,反正是有點高級的。所以這個解決方案還是有點東西的,你可以用來在面試中吹牛

2. 兜底方案流程圖

本文介紹的解決方案可以用下面的流程圖表示:

在一個空白的文件夾下面創(chuàng)建以下文件

  • index.html
  • worker.js

使用到的代碼如下:

<!DOCTYPE html>
<html>

<head>
  <title>Web Worker Image Processing</title>
</head>

<body>
  <img id="originalImage" src="https://images.pexels.com/photos/12196392/pexels-photo-12196392.jpeg?auto=compress&cs=tinysrgb&w=600&lazy=load" alt="Original Image" />
  <img id="processedImage" alt="Processed Image" />

  <script>
    // 創(chuàng)建一個Web Worker實例  
    const worker = new Worker('worker.js');

    // 監(jiān)聽Web Worker的消息  
    worker.onmessage = function (e) {
      const processedImageUrl = e.data;
      
      const _ = document.getElementById('processedImage');
      _.onload = () => {
        worker.terminate();
      }
      _.src = processedImageUrl;
    };

    // 圖片地址,你可以根據(jù)需要修改  
    const imageUrl = 'https://images.pexels.com/photos/12196392/pexels-photo-12196392.jpeg?auto=compress&cs=tinysrgb&w=600&lazy=load';

    // 當原始圖片加載完成后,向Web Worker發(fā)送消息  
    document.getElementById('originalImage').onload = function () {
      worker.postMessage(imageUrl);
    };  
  </script>
</body>

</html>
// worker.js
// 當此Web Worker接收到消息時,會調用此函數(shù)  
self.onmessage = async function (e) {  
  
  // 從接收到的消息中提取出圖片的URL  
  const imageUrl = e.data;  
  
  try {  
    // 使用fetch API從給定的URL異步獲取圖片資源  
    const response = await fetch(imageUrl);  
  
    // 檢查HTTP響應狀態(tài),如果不是200-299之間,則拋出錯誤  
    if (!response.ok) {    
      throw new Error(`HTTP error! status: ${response.status}`);    
    }  
  
    // 將響應體轉換為Blob對象,這通常用于處理二進制數(shù)據(jù)  
    const blob = await response.blob();  
  
    // 使用Blob對象創(chuàng)建一個ImageBitmap,這是一個可以高效繪制到Canvas上的位圖圖像  
    const imgBitmap = createImageBitmap(blob);  
  
    // ImageBitmap對象創(chuàng)建是異步的,所以使用.then()來處理創(chuàng)建成功后的操作  
    imgBitmap.then(function (bitmap) {  
      // 創(chuàng)建一個離屏Canvas,其尺寸與位圖相同  
      const canvas = new OffscreenCanvas(bitmap.width, bitmap.height);  
  
      // 獲取Canvas的2D渲染上下文  
      const ctx = canvas.getContext('2d');  
  
      // 在Canvas上繪制位圖  
      ctx.drawImage(bitmap, 0, 0);  
  
      // 從Canvas上獲取圖像數(shù)據(jù)  
      const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);  
  
      // 獲取圖像數(shù)據(jù)的像素數(shù)組  
      const data = imageData.data;  
  
      // 遍歷每個像素,將其轉換為灰度(通過計算RGB通道的平均值,并將其設置為每個通道的值)  
      for (let i = 0; i < data.length; i += 4) {  
        const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;  
        data[i] = avg; // R  
        data[i + 1] = avg; // G  
        data[i + 2] = avg; // B  
      }  
  
      // 將處理后的圖像數(shù)據(jù)放回Canvas  
      ctx.putImageData(imageData, 0, 0);  
  
      // 將Canvas轉換為Blob對象  
      canvas.convertToBlob().then(blob => {  
        // 創(chuàng)建一個表示該Blob對象的URL  
        const newUrl = URL.createObjectURL(blob);  
  
        // 將新的圖像URL發(fā)送回主線程  
        self.postMessage(newUrl);  
      });  
  
    }).catch(e => {  
      // 如果在處理過程中發(fā)生錯誤,則拋出一個新的錯誤(這里可以添加更詳細的錯誤處理)  
      throw new Error();  
    });  
  
  } catch (e) {  
    // 如果在嘗試獲取或處理圖像時發(fā)生錯誤,則將原始圖像URL發(fā)送回主線程  
    self.postMessage(imageUrl);  
  }   
};

理解上面代碼的邏輯可以結合代碼注釋和流程圖,在此就不過多贅述了。

完成之后使用 live server 啟動 index.html 可以看到如下效果:

左邊是原圖,而右邊是經(jīng)過 web worker 處理之后的圖像。

在這個示例中,你可以簡單的將 web worker 理解成為 PS。它接受原始圖片的地址,返回處理之后的圖片的地址。

需要注意的是,圖片是二進制數(shù)據(jù),所以我們用到了 Blob 將數(shù)據(jù)轉成 URL。

3. 封裝成 React 組件

在 React 中使用 web worker 需要一點技巧,請參考我之前的文章: 在react項目中使用web worker的方法 - 掘金 (juejin.cn)

在你的前端工程目錄中創(chuàng)建 src/component/ImageProcessor 目錄,然后創(chuàng)建下面兩個文件:

文件中的代碼如下所示:

// index.js
// 引入React及其相關hooks
import React, { useEffect, useRef, useState } from 'react';
// 引入Web Worker的腳本
import workerScript from './worker';
// ImageProcessor組件,它接受原始圖片的URL、寬度和高度作為屬性
const ImageProcessor = ({ originSrc, width, height }) => {
  // 使用useState hook來存儲處理后的圖片URL,初始值為原始圖片的URL
  const [src, setSrc] = useState(originSrc);
  // 使用useRef hook來存儲Web Worker的實例
  const workerInstance = useRef(new Worker(workerScript));
  // 使用useEffect hook來處理圖片的URL變化
  useEffect(() => {
    // 當原始圖片URL與處理后的圖片URL不相同時,才進行處理
    if (originSrc === src) {
      // 設置Web Worker的onmessage事件處理函數(shù)
      workerInstance.current.onmessage = function (e) {
        // 接收處理后的圖片URL
        const processedImageUrl = e.data;
        // 更新處理后的圖片URL狀態(tài)
        setSrc(processedImageUrl);
      };
      // 向Web Worker發(fā)送原始圖片的完整URL,以便進行處理
      workerInstance.current.postMessage(window.location.origin + originSrc);
    }
    // 清除函數(shù),在組件卸載或狀態(tài)變化時終止Web Worker
    return () => {
      workerInstance.current.terminate();
    }
    // 當原始圖片URL或處理后的圖片URL發(fā)生變化時,觸發(fā)此hook
  }, [src, originSrc])

  // 圖片加載完成后的事件處理函數(shù)
  const imageLoaded = (event) => {
    // 終止當前的Web Worker
    workerInstance.current.terminate();
  };

  // 返回JSX,表示組件的UI
  return (
    <div>
      {/* 當原始圖片的URL與處理后的圖片URL不相同時,顯示處理后的圖片 */}
      {originSrc !== src && <img
        style={{
          position: 'absolute',
          left: 0,
          top: 0,
          zIndex: 0,
          width: width ?? '100%',// 使用nullish coalescing操作符來提供默認值
          height: height ?? '100%',
          objectFit: 'cover',// 圖片填充方式
        }}
        src={src}// 設置圖片的URL
        alt="Original Image"http:// 圖片的替代文本
        onLoad={imageLoaded}// 圖片加載完成后的事件處理函數(shù)
      />}
    </div>
  );
};

// 導出ImageProcessor組件
export default ImageProcessor;
// worker.js
// 定義一個workerCode函數(shù),這個函數(shù)將作為Web Worker的代碼  
const workerCode = () => {  
  
  // 使用_self變量來引用全局的self對象,以便在Web Worker內部使用  
  // 是用來告訴eslint忽略下一行的代碼檢查,因為這里我們對self進行了重新賦值
  // eslint-disable-next-line   
  const _self = self;  
  
  // 當Web Worker接收到消息時,會調用此函數(shù)  
  _self.onmessage = async function (e) {  
    // 從接收到的消息中提取出圖片的URL  
    const imageUrl = e.data;  
  
    try {  
      // 使用fetch API異步地從給定的URL獲取圖片資源  
      const response = await fetch(imageUrl);  
  
      // 檢查HTTP響應狀態(tài),如果不是200-299之間,則拋出錯誤  
      if (!response.ok) {  
        throw new Error(`HTTP error! status: ${response.status}`);  
      }  
  
      // 將HTTP響應的內容轉換為Blob對象,這通常用于處理二進制數(shù)據(jù)  
      const blob = await response.blob();  
  
      // 使用Blob對象創(chuàng)建一個ImageBitmap,這是一個可以高效繪制到Canvas上的位圖圖像  
      // 注意:createImageBitmap是異步的  
      const imgBitmap = createImageBitmap(blob);  
  
      // ImageBitmap對象創(chuàng)建成功后,會執(zhí)行以下操作  
      imgBitmap.then(function (bitmap) {  
        // 創(chuàng)建一個與位圖尺寸相同的離屏Canvas  
        const canvas = new OffscreenCanvas(bitmap.width, bitmap.height);  
  
        // 獲取這個離屏Canvas的2D渲染上下文  
        const ctx = canvas.getContext('2d');  
  
        // 在Canvas上繪制之前創(chuàng)建的位圖  
        ctx.drawImage(bitmap, 0, 0);  
  
        // 從Canvas上獲取整個圖像的圖像數(shù)據(jù)  
        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);  
  
        // 獲取圖像數(shù)據(jù)的像素數(shù)組  
        const data = imageData.data;  
  
        // 遍歷每個像素,將彩色圖像轉換為灰度圖像  
        // 灰度是通過計算RGB三個通道的平均值,并將其設置為每個通道的值來得到的  
        for (let i = 0; i < data.length; i += 4) {  
          const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;  
          data[i] = avg;     // R  
          data[i + 1] = avg; // G  
          data[i + 2] = avg; // B  
        }  
  
        // 將處理后的灰度圖像數(shù)據(jù)重新放回Canvas  
        ctx.putImageData(imageData, 0, 0);  
  
        // 將Canvas內容轉換為Blob對象  
        canvas.convertToBlob().then(blob => {  
          // 創(chuàng)建一個表示該Blob對象的URL  
          const newUrl = URL.createObjectURL(blob);  
          // 將處理后的灰度圖像的URL發(fā)送回主線程  
          _self.postMessage(newUrl);  
        });  
  
      }).catch(e => {  
        // 如果在處理ImageBitmap或Canvas時出現(xiàn)錯誤,拋出一個新的錯誤  
        // 這里可以添加更詳細的錯誤處理邏輯  
        throw new Error();  
      });  
  
    } catch (e) {  
      // 如果在嘗試獲取或處理圖像時發(fā)生任何錯誤(如網(wǎng)絡錯誤、fetch失敗等)  
      // 則將原始的圖像URL發(fā)送回主線程,表示處理失敗  
      _self.postMessage(imageUrl);  
    }  
  };  
};  
  
// 將workerCode函數(shù)轉換為字符串,并截取大括號內的內容作為Web Worker的代碼  
let code = workerCode.toString();  
code = code.substring(code.indexOf('{') + 1, code.lastIndexOf('}'));  
  
// 創(chuàng)建一個包含處理過的代碼的Blob對象  
const blob = new Blob([code], { type: 'application/javascript' });  
// 為這個Blob對象創(chuàng)建一個URL,這個URL可以被用作Web Worker的腳本源  
const workerScriptURL = URL.createObjectURL(blob);  
  
// 導出這個URL,以便其他模塊可以使用它來創(chuàng)建一個新的Web Worker  
export default workerScriptURL;

這樣組件就封裝完成了,在你需要的地方使用下面的代碼來調用上面的組件

import ImageProcessor from "@/component/ImageProcessor";
<ImageProcessor originSrc = {CompressImageUrl} />

你可以將上述代碼的這部分抽取成一個函數(shù),放到公用工具庫中:

// 遍歷每個像素,將彩色圖像轉換為灰度圖像  
// 灰度是通過計算RGB三個通道的平均值,并將其設置為每個通道的值來得到的  
for (let i = 0; i < data.length; i += 4) {  
  const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;  
  data[i] = avg;     // R  
  data[i + 1] = avg; // G  
  data[i + 2] = avg; // B  
}  

這樣一來,通過替換像素算法就可以實現(xiàn)更加有趣且復雜的圖像處理了。

最后總結一下上面代碼中值得注意的細節(jié):

  • React Hooks的使用useState, useEffect, useRef這三個React Hooks在代碼中都被使用了。useState用于存儲處理后的圖片URL;useEffect用于在組件掛載、更新時執(zhí)行某些操作(如啟動和終止Web Worker);useRef用于存儲Web Worker的實例,并確保其在整個組件生命周期內保持不變。
  • Web Worker的使用:為了提高性能,代碼使用了Web Worker來在后臺處理圖像轉換任務,從而避免阻塞主線程。Web Worker通過postMessageonmessage進行通信。
  • Blob和URL.createObjectURL:在處理圖像后,Web Worker使用BlobURL.createObjectURL創(chuàng)建了一個新的URL,這個URL指向包含處理后圖像數(shù)據(jù)的Blob對象。這樣可以在不將圖像數(shù)據(jù)實際寫入磁盤的情況下,將其作為一個可訪問的資源。
  • createImageBitmap和OffscreenCanvas:為了提高圖像處理性能,代碼中使用了createImageBitmap來高效地將Blob轉換為位圖,并使用OffscreenCanvas進行離屏渲染。這兩個API都允許在不影響頁面渲染的情況下進行高性能的圖像處理。
  • 灰度圖像處理:在Web Worker內部,代碼遍歷了圖像的每個像素,并將其轉換為灰度。這是通過計算RGB通道的平均值,并將這個平均值設置為新的RGB值來實現(xiàn)的。
  • 動態(tài)創(chuàng)建Web Worker腳本worker.js文件最后將自身代碼轉換為字符串,并通過Blob和URL.createObjectURL創(chuàng)建了一個URL,這個URL被用作Web Worker的腳本源。這是一種動態(tài)創(chuàng)建和執(zhí)行JavaScript代碼的技術,允許在不依賴外部文件的情況下運行Web Worker。
  • 錯誤處理和異常捕獲:在Web Worker內部和外部都有錯誤處理和異常捕獲的邏輯。例如,在嘗試獲取或處理圖像時發(fā)生錯誤,會將原始的圖像URL發(fā)送回主線程表示處理失敗。
  • 使用nullish coalescing操作符:在JSX中,width ?? '100%'height ?? '100%'使用了nullish coalescing操作符(??),這是ES2020引入的新特性。它允許在左側操作數(shù)為nullundefined時返回右側的值,否則返回左側的值。這提供了一種簡潔的方式來為變量提供默認值。
  • 圖片加載完成后的處理:在圖片加載完成后,會終止當前的Web Worker。這有助于釋放資源并避免不必要的后臺處理。
  • 代碼組織和模塊導出ImageProcessor組件和workerScriptURL都被導出,以便在其他模塊中使用。這體現(xiàn)了良好的模塊化和代碼復用實踐。

以上就是React處理復雜圖片樣式的方法詳解的詳細內容,更多關于React處理圖片樣式的資料請關注腳本之家其它相關文章!

相關文章

  • react-router-dom?v6?使用詳細示例

    react-router-dom?v6?使用詳細示例

    這篇文章主要介紹了react-router-dom?v6使用詳細示例,文章圍繞主題展開詳細的內容介紹,具有一定的參考價值,感興趣的小伙伴可以參考一下,希望對你的學習有所幫助
    2022-09-09
  • ReactNative 之FlatList使用及踩坑封裝總結

    ReactNative 之FlatList使用及踩坑封裝總結

    本篇文章主要介紹了ReactNative 之FlatList使用及踩坑封裝總結,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-11-11
  • 基于webpack開發(fā)react-cli的詳細步驟

    基于webpack開發(fā)react-cli的詳細步驟

    這篇文章主要介紹了基于webpack開發(fā)react-cli的詳細步驟,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-06-06
  • React啟動時webpack版本沖突報錯的解決辦法

    React啟動時webpack版本沖突報錯的解決辦法

    在啟動React應用時,遇到Webpack版本不匹配導致的運行錯誤,解決方法包括刪除全局及局部的webpack和webpack-cli,然后根據(jù)項目需求安裝特定版本的webpack,本文通過代碼示例給大家介紹的非常詳細,需要的朋友可以參考下
    2024-09-09
  • React三大屬性之Refs的使用詳解

    React三大屬性之Refs的使用詳解

    這篇文章主要介紹了React三大屬性之Refs的使用詳解,幫助大家更好的理解和學習使用React,感興趣的朋友可以了解下
    2021-04-04
  • 詳解React中的setState執(zhí)行機制

    詳解React中的setState執(zhí)行機制

    setState是React組件中用于更新狀態(tài)的方法,是類組件中的方法,用于更新組件的狀態(tài)并重新渲染組件,本文給大家詳細介紹了React中的setState執(zhí)行機制,文中通過代碼示例介紹的非常詳細,需要的朋友可以參考下
    2023-12-12
  • 手挽手帶你學React之React-router4.x的使用

    手挽手帶你學React之React-router4.x的使用

    這篇文章主要介紹了手挽手帶你學React之React-router4.x的使用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-02-02
  • react之組件通信詳解

    react之組件通信詳解

    本篇文章主要介紹了React組件通信詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2021-10-10
  • React中的跨組件通信的幾種常見方法

    React中的跨組件通信的幾種常見方法

    在React中,跨組件通信有幾種常見的方式,每種方式適用于不同的場景,下面是幾種常見的跨組件通信方法,感興趣的朋友一起看看吧
    2025-04-04
  • React+hook實現(xiàn)聯(lián)動模糊搜索

    React+hook實現(xiàn)聯(lián)動模糊搜索

    這篇文章主要為大家詳細介紹了如何利用React+hook+antd實現(xiàn)聯(lián)動模糊搜索功能,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2024-02-02

最新評論