mirror of
https://github.com/OpenListTeam/OpenList-Frontend.git
synced 2026-03-15 03:20:25 +00:00
add(preview): HEIF HEIC AVIF VVC AVC HEIF-JPG
This commit is contained in:
5962
public/static/libheif/libheif.js
Normal file
5962
public/static/libheif/libheif.js
Normal file
File diff suppressed because it is too large
Load Diff
BIN
public/static/libheif/libheif.wasm
Normal file
BIN
public/static/libheif/libheif.wasm
Normal file
Binary file not shown.
187
src/pages/home/previews/heic.tsx
Normal file
187
src/pages/home/previews/heic.tsx
Normal file
@@ -0,0 +1,187 @@
|
||||
import { Error, FullLoading } from "~/components"
|
||||
import { useRouter, useT } from "~/hooks"
|
||||
import { objStore } from "~/store"
|
||||
import { onCleanup, onMount, createSignal, Show } from "solid-js"
|
||||
|
||||
const Preview = () => {
|
||||
const t = useT()
|
||||
const { replace } = useRouter()
|
||||
const [loading, setLoading] = createSignal(true)
|
||||
const [error, setError] = createSignal(false)
|
||||
|
||||
// 获取当前目录下所有HEIC文件
|
||||
let heicFiles = objStore.objs.filter((obj) =>
|
||||
/\.(heic|heif|avif|vvc|avc|jpeg|jpg)$/i.test(obj.name),
|
||||
)
|
||||
|
||||
if (heicFiles.length === 0) {
|
||||
heicFiles = [objStore.obj]
|
||||
}
|
||||
|
||||
// 键盘导航功能:左右箭头切换文件
|
||||
const onKeydown = (e: KeyboardEvent) => {
|
||||
const index = heicFiles.findIndex((f) => f.name === objStore.obj.name)
|
||||
if (e.key === "ArrowLeft" && index > 0) {
|
||||
replace(heicFiles[index - 1].name)
|
||||
} else if (e.key === "ArrowRight" && index < heicFiles.length - 1) {
|
||||
replace(heicFiles[index + 1].name)
|
||||
}
|
||||
}
|
||||
|
||||
let libheif: any
|
||||
let decoder: any
|
||||
let canvas: HTMLCanvasElement | null = null
|
||||
|
||||
onMount(() => {
|
||||
window.addEventListener("keydown", onKeydown)
|
||||
initLibheif()
|
||||
})
|
||||
|
||||
onCleanup(() => {
|
||||
window.removeEventListener("keydown", onKeydown)
|
||||
if (libheif && decoder) {
|
||||
decoder.free()
|
||||
decoder = null
|
||||
}
|
||||
libheif = null
|
||||
})
|
||||
|
||||
// 初始化libheif库
|
||||
const initLibheif = async () => {
|
||||
setLoading(true)
|
||||
setError(false)
|
||||
|
||||
try {
|
||||
// 动态加载libheif脚本
|
||||
if (!window.libheif) {
|
||||
await loadScript("/static/libheif/libheif.js", "libheif-script")
|
||||
}
|
||||
|
||||
// 加载WASM文件
|
||||
const wasmBinary = await fetchWasm("/static/libheif/libheif.wasm")
|
||||
|
||||
// 初始化libheif
|
||||
libheif = window.libheif({ wasmBinary })
|
||||
decoder = new libheif.HeifDecoder()
|
||||
|
||||
// 加载并解码当前HEIC文件
|
||||
await loadAndDecode(objStore.raw_url)
|
||||
} catch (e) {
|
||||
console.error("HEIC初始化失败:", e)
|
||||
setError(true)
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
// 加载脚本
|
||||
const loadScript = (src: string, id: string) => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const script = document.createElement("script")
|
||||
script.src = src
|
||||
script.id = id
|
||||
script.onload = () => resolve()
|
||||
script.onerror = () => reject(new Error(`脚本加载失败: ${src}`))
|
||||
document.head.appendChild(script)
|
||||
})
|
||||
}
|
||||
|
||||
// 获取WASM文件
|
||||
const fetchWasm = async (url: string) => {
|
||||
const response = await fetch(url)
|
||||
if (!response.ok) throw new Error(`WASM加载失败: ${url}`)
|
||||
return await response.arrayBuffer()
|
||||
}
|
||||
|
||||
// 加载并解码HEIC文件
|
||||
const loadAndDecode = async (url: string) => {
|
||||
try {
|
||||
setLoading(true)
|
||||
setError(false)
|
||||
|
||||
// 获取HEIC文件
|
||||
const response = await fetch(url)
|
||||
if (!response.ok) throw new Error("文件获取失败")
|
||||
const buffer = await response.arrayBuffer()
|
||||
|
||||
// 解码HEIC文件
|
||||
const images = decoder.decode(buffer)
|
||||
if (!images || images.length === 0) {
|
||||
throw new Error("没有可解码的图像")
|
||||
}
|
||||
|
||||
// 显示第一张图像
|
||||
const image = images[0]
|
||||
await displayImage(image)
|
||||
setLoading(false)
|
||||
} catch (e) {
|
||||
console.error("HEIC解码失败:", e)
|
||||
setError(true)
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
// 在canvas上显示图像
|
||||
const displayImage = (image: any) => {
|
||||
return new Promise<void>((resolve) => {
|
||||
if (!canvas) return resolve()
|
||||
|
||||
const width = image.get_width()
|
||||
const height = image.get_height()
|
||||
|
||||
// 调整canvas尺寸
|
||||
canvas.width = width
|
||||
canvas.height = height
|
||||
|
||||
// 创建ImageData对象
|
||||
const imageData = new ImageData(width, height)
|
||||
|
||||
// 显示图像
|
||||
image.display(imageData, (displayData: ImageData | null) => {
|
||||
if (!displayData || !canvas) return resolve()
|
||||
|
||||
const ctx = canvas.getContext("2d")
|
||||
if (!ctx) return resolve()
|
||||
|
||||
ctx.putImageData(displayData, 0, 0)
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
id="heic-container"
|
||||
style={{
|
||||
position: "relative",
|
||||
width: "100%",
|
||||
height: "75vh",
|
||||
display: "flex",
|
||||
"justify-content": "center",
|
||||
"align-items": "center",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
<canvas
|
||||
ref={canvas}
|
||||
style={{
|
||||
"max-width": "100%",
|
||||
"max-height": "100%",
|
||||
"object-fit": "contain",
|
||||
display: loading() || error() ? "none" : "block",
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 加载状态 */}
|
||||
<Show when={loading()}>
|
||||
<FullLoading />
|
||||
</Show>
|
||||
|
||||
{/* 错误状态 */}
|
||||
<Show when={error()}>
|
||||
<Error msg={t("preview.failed_load_heic")} h="75vh" />
|
||||
</Show>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Preview
|
||||
@@ -65,6 +65,12 @@ const previews: Preview[] = [
|
||||
component: lazy(() => import("./flash")),
|
||||
prior: true,
|
||||
},
|
||||
{
|
||||
name: "HEIC",
|
||||
exts: ["heic", "heif", "avif", "vvc", "avc", "jpeg", "jpg"],
|
||||
component: lazy(() => import("./heic")),
|
||||
prior: true,
|
||||
},
|
||||
{
|
||||
name: "Markdown with word wrap",
|
||||
type: ObjType.TEXT,
|
||||
|
||||
Reference in New Issue
Block a user