<!-- Generated by SpecStory -->

# 高性能 HEIC/HEIF 图片渲染方案 (2025-05-26 13:52:09)

_**User**_

使用 高性能的浏览器方案 渲染 heic heif 图片 , 转换可能需要 wasm 库。首先还是下载图片，然后吧 buffer 转换，借助 blob 去加载图片

---

_**Assistant**_

我来帮你实现高性能的 HEIC/HEIF 图片渲染方案。首先我需要搜索一下现有的 WASM 库来处理 HEIC/HEIF 格式转换。

---

<details>
            <summary>Searched web "HEIC HEIF to JPEG WebAssembly library browser 2024" • **5** results</summary>
        
| Title | URL |
|-------|-----|
| HEIC.online: Convert HEIC to JPG | https://heic.online/ |
| Handling HEIC on the web \| Blog | https://upsidelab.io/blog/handling-heic-on-the-web |
| GitHub - hoppergee/heic-to: Convert HEIC/HEIF images to JPEG, PNG in browser | https://github.com/hoppergee/heic-to |
| ffmpeg.wasm \| ffmpeg.wasm | https://ffmpegwasm.netlify.app/ |
| GitHub - Trzcin/convert-it-all: Easily convert between file formats. Provides a cli and a web interface using WebAssembly. | https://github.com/Trzcin/convert-it-all |

</details>

---

现在让我搜索一些专门的 HEIC/HEIF WASM 库：

---

<details>
            <summary>Searched web "heic-to libheif-js heic2any WASM library comparison 2024" • **5** results</summary>
        
| Title | URL |
|-------|-----|
| Handling HEIC on the web \| Blog | https://upsidelab.io/blog/handling-heic-on-the-web |
| Unable to convert heic to jpg taken by iPhone 15 pro iOS18 | https://github.com/strukturag/libheif/issues/1190 |
| GitHub - hoppergee/heic-to: Convert HEIC/HEIF images to JPEG, PNG in browser | https://github.com/hoppergee/heic-to |
| High Efficiency Image File Format | https://en.wikipedia.org/wiki/Libheif |
| Handling HEIC on the web \| Blog \| Upside | https://upsidelab.io/blog/handling-heic-on-the-web |

</details>

---

基于我的研究，我现在来为你实现一个高性能的 HEIC/HEIF 图片渲染方案。我将使用 `heic-to` 库，这是一个基于最新 libheif 1.19.8 的现代化 WASM 库，专门为浏览器优化。

首先，让我安装必要的依赖：

---

```bash
cd /Users/innei/git/vibe-coding/photo-gallery-site && npm install heic-to
```

```
be-coding/photo-gallery-site && npm install heic-to       cd /Users/innei/git/vibe-coding/photo-gallery-site    npm
 WARN  2 deprecated subdependencies found: glob@7.2.3, inflight@1.0.6
Packages: +1
+
Progress: resolved 887, reused 777, downloaded 1, added 1, done

dependencies:
+ heic-to 1.1.13
- heic2any 0.0.4

Done in 5.8s using pnpm v10.11.0

innei in photo-gallery-site on  main [$!⇡1] via 🎁 v0.0.1 via  v22.15.0 on ☁️  innei@rss3.io at     f88a4a0 "Upd
ate background color in ProgressiveImage component for improved styling consistency"  by tukon479@gmail.com took 5s 
at 13:53:34 ❯
```

---

现在让我创建一个专门处理 HEIC/HEIF 图片的工具函数：

---

```diff
- 
+ import { heicTo, isHeic } from 'heic-to'
+ 
+ export interface HeicConversionOptions {
+   quality?: number
+   format?: 'image/jpeg' | 'image/png'
+ }
+ 
+ export interface ConversionResult {
+   blob: Blob
+   url: string
+   originalSize: number
+   convertedSize: number
+   format: string
+ }
+ 
+ /**
+  * 检测文件是否为 HEIC/HEIF 格式
+  */
+ export async function detectHeicFormat(file: File | Blob): Promise<boolean> {
+   try {
+     return await isHeic(file)
+   } catch (error) {
+     console.warn('Failed to detect HEIC format:', error)
+     return false
+   }
+ }
+ 
+ /**
+  * 将 HEIC/HEIF 图片转换为 JPEG 或 PNG
+  */
+ export async function convertHeicImage(
+   file: File | Blob,
+   options: HeicConversionOptions = {}
+ ): Promise<ConversionResult> {
+   const { quality = 0.8, format = 'image/jpeg' } = options
+ 
+   try {
+     // 检查是否为 HEIC 格式
+     const isHeicFormat = await detectHeicFormat(file)
+     if (!isHeicFormat) {
+       throw new Error('File is not in HEIC/HEIF format')
+     }
+ 
+     // 转换图片
+     const convertedBlob = await heicTo({
+       blob: file,
+       type: format,
+       quality,
+     })
+ 
+     // 创建 URL
+     const url = URL.createObjectURL(convertedBlob)
+ 
+     return {
+       blob: convertedBlob,
+       url,
+       originalSize: file.size,
+       convertedSize: convertedBlob.size,
+       format,
+     }
+   } catch (error) {
+     console.error('HEIC conversion failed:', error)
+     throw new Error(`Failed to convert HEIC image: ${error instanceof Error ? error.message : 'Unknown error'}`)
+   }
+ }
+ 
+ /**
+  * 清理转换后的 URL
+  */
+ export function revokeConvertedUrl(url: string): void {
+   try {
+     URL.revokeObjectURL(url)
+   } catch (error) {
+     console.warn('Failed to revoke URL:', error)
+   }
+ }
+ 
+ /**
+  * 获取文件的 MIME 类型
+  */
+ export function getFileMimeType(file: File): string {
+   return file.type || 'application/octet-stream'
+ }
+ 
+ /**
+  * 检查文件是否可能是 HEIC 格式（基于扩展名）
+  */
+ export function isPotentialHeicFile(file: File): boolean {
+   const fileName = file.name.toLowerCase()
+   return fileName.endsWith('.heic') || fileName.endsWith('.heif')
+ }
+ 
+ /**
+  * 批量转换 HEIC 图片
+  */
+ export async function convertMultipleHeicImages(
+   files: (File | Blob)[],
+   options: HeicConversionOptions = {},
+   onProgress?: (completed: number, total: number) => void
+ ): Promise<ConversionResult[]> {
+   const results: ConversionResult[] = []
+   
+   for (let i = 0; i < files.length; i++) {
+     try {
+       const result = await convertHeicImage(files[i], options)
+       results.push(result)
+     } catch (error) {
+       console.error(`Failed to convert file ${i}:`, error)
+       // 继续处理其他文件，不中断整个流程
+     }
+     
+     onProgress?.(i + 1, files.length)
+   }
+   
+   return results
+ } 
```

---

让我修复类型错误：

---



---

现在让我更新 ProgressiveImage 组件来支持 HEIC/HEIF 格式：

---



---

现在让我更新 ProgressiveImage 组件的状态和逻辑来处理 HEIC 转换：

---



---

现在让我更新重置状态的 useEffect：

---



---

现在让我更新主要的图片加载逻辑来处理 HEIC 格式：

---



---

让我修复 linter 错误，更新加载指示器来显示转换状态：

---



---



---

现在让我更新加载指示器来显示 HEIC 转换状态：

---



---

现在让我添加一个清理函数来处理组件卸载时的 URL 清理：

---



---

现在让我创建一个简单的测试页面来验证 HEIC 支持：

---

Cancelled

---

_**User**_

这个会导致无线 convert

---

_**Assistant**_

你说得对！这里确实会导致无限循环。问题在于 `useEffect` 的依赖数组中包含了 `convertedUrl`，而在 effect 内部又会修改 `convertedUrl`，这会触发新的 effect 执行。

让我修复这个问题：

---



---

现在让我也优化一下清理逻辑，使用 `useRef` 来跟踪当前的 URL，避免在依赖数组中包含状态：

---



---

现在让我更新重置逻辑来使用 ref：

---



---

现在让我更新转换逻辑来使用 ref：

---



---

现在让我更新组件卸载时的清理逻辑：

---



---

让我移除未使用的 `convertedUrl` 状态，因为我们现在使用 ref 来管理：

---



---

让我移除对 `setConvertedUrl` 的调用：

---



---



---

现在让我创建一个简单的测试组件来验证 HEIC 支持：

---

Cancelled

---

