11'use client'
22
33import type { ProgressiveImageProps } from '~/types/props.ts'
4- import { useEffect , useState , useRef } from 'react'
4+ import { useEffect , useState , useRef , Activity } from 'react'
55import { useTranslations } from 'next-intl'
66import { MotionImage } from '~/components/album/motion-image'
77import { useBlurImageDataUrl } from '~/hooks/use-blurhash'
@@ -20,25 +20,25 @@ export default function ProgressiveImage(
2020 props : Readonly < ProgressiveImageProps > ,
2121) {
2222 const t = useTranslations ( )
23-
23+
2424 const [ loadingProgress , setLoadingProgress ] = useState ( 0 )
2525 const [ isLoading , setIsLoading ] = useState ( true )
2626 const [ error , setError ] = useState < string | null > ( null )
2727 const [ highResImageUrl , setHighResImageUrl ] = useState < string | null > ( null )
28- const [ showWebGLViewer , setShowWebGLViewer ] = useState ( Boolean ( props . showLightbox ) )
29- const [ webGLAvailable , setWebGLAvailable ] = useState ( true )
30-
31- const webglViewerRef = useRef < WebGLImageViewerRef | null > ( null )
28+ const [ highResImageLoaded , setHighResImageLoaded ] = useState ( false )
29+ const [ showFullScreenViewer , setShowFullScreenViewer ] = useState ( Boolean ( props . showLightbox ) )
30+ const [ webGLAvailable ] = useState ( ( ) => isWebGLSupported ( ) )
3231
32+ const webglViewerRef = useRef < WebGLImageViewerRef | null > ( null )
3333 useEffect ( ( ) => {
34- setShowWebGLViewer ( Boolean ( props . showLightbox ) )
34+ return ( ) => {
35+ webglViewerRef . current ?. destroy ( )
36+ }
37+ } , [ ] )
38+ useEffect ( ( ) => {
39+ setShowFullScreenViewer ( Boolean ( props . showLightbox ) )
3540 } , [ props . showLightbox ] )
3641
37- // 检测 WebGL 支持
38- useEffect ( ( ) => {
39- setWebGLAvailable ( isWebGLSupported ( ) )
40- } , [ ] )
41-
4242 useEffect ( ( ) => {
4343 loadHighResolutionImage ( )
4444 return ( ) => {
@@ -86,7 +86,7 @@ export default function ProgressiveImage(
8686 const dataURL = useBlurImageDataUrl ( props . blurhash )
8787
8888 const handleCloseViewer = ( ) => {
89- setShowWebGLViewer ( false )
89+ setShowFullScreenViewer ( false )
9090 if ( props . onShowLightboxChange ) {
9191 props . onShowLightboxChange ( false )
9292 }
@@ -95,7 +95,7 @@ export default function ProgressiveImage(
9595 return (
9696 < div className = "relative" >
9797 { /* 预览图 - 在高清图未加载完成时显示 */ }
98- { ! highResImageUrl ? (
98+ < Activity mode = { highResImageLoaded ? 'hidden' : 'visible' } >
9999 < MotionImage
100100 initial = { { opacity : 0 } }
101101 animate = { { opacity : 1 } }
@@ -110,29 +110,49 @@ export default function ProgressiveImage(
110110 height = { props . height }
111111 alt = { props . alt || 'image' }
112112 />
113- ) : (
114- /* 高清图已加载 - 根据状态选择渲染方式 */
113+ { /* 加载进度条 */ }
114+ { isLoading && (
115+ < div className = "absolute bottom-0 left-0 w-full" >
116+ < div
117+ className = "h-1 bg-blue-500"
118+ style = { { width : `${ loadingProgress } %` } }
119+ > </ div >
120+ < div className = "absolute bottom-2 right-2 bg-black/60 text-white text-xs px-2 py-1 rounded" >
121+ { loadingProgress } %
122+ </ div >
123+ </ div >
124+ ) }
125+ { /* 错误提示 */ }
126+ { error && (
127+ < div className = "absolute bottom-0 left-0 w-full" >
128+ < div className = "absolute bottom-2 right-2 text-white bg-black/60 text-xs px-2 py-1 rounded" >
129+ { error }
130+ </ div >
131+ </ div >
132+ ) }
133+ </ Activity >
134+ { highResImageUrl ? (
115135 < >
116- { /* 普通预览模式 - 点击可打开全屏查看 */ }
117- { ! showWebGLViewer && (
136+ < Activity mode = { highResImageLoaded && ! showFullScreenViewer ? 'visible' : 'hidden' } >
118137 < img
119138 className = "object-contain md:max-h-[90vh] cursor-pointer"
120139 src = { highResImageUrl }
121140 width = { props . width }
122141 height = { props . height }
123142 alt = { props . alt || 'image' }
124143 onClick = { ( ) => {
125- setShowWebGLViewer ( true )
144+ setShowFullScreenViewer ( true )
126145 if ( props . onShowLightboxChange ) {
127146 props . onShowLightboxChange ( true )
128147 }
129148 } }
149+ onLoad = { ( ) => {
150+ setHighResImageLoaded ( true )
151+ } }
130152 />
131- ) }
132-
133- { /* WebGL 全屏查看模式 */ }
134- { showWebGLViewer && webGLAvailable && (
135- < div
153+ </ Activity >
154+ < Activity mode = { showFullScreenViewer ? 'visible' : 'hidden' } >
155+ { webGLAvailable ? < div
136156 className = "fixed inset-0 z-[100] bg-black/90 flex items-center justify-center"
137157 onClick = { ( e ) => {
138158 // 点击背景关闭
@@ -163,12 +183,12 @@ export default function ProgressiveImage(
163183 < line x1 = "6" y1 = "6" x2 = "18" y2 = "18" > </ line >
164184 </ svg >
165185 </ button >
166-
186+
167187 { /* 操作提示 */ }
168188 < div className = "absolute bottom-4 left-1/2 -translate-x-1/2 text-white/50 text-sm pointer-events-none" >
169189 { t ( 'Tips.zoomHint' ) }
170190 </ div >
171-
191+
172192 { /* WebGL 图片查看器 */ }
173193 < div className = "w-full h-full" >
174194 < WebGLImageViewer
@@ -181,17 +201,11 @@ export default function ProgressiveImage(
181201 minScale = { 0.5 }
182202 maxScale = { 10 }
183203 limitToBounds = { true }
184- centerOnInit = { true }
185204 smooth = { true }
186205 debug = { process . env . NODE_ENV === 'development' }
187206 />
188207 </ div >
189- </ div >
190- ) }
191-
192- { /* WebGL 不可用时的 Fallback - 使用普通全屏图片 */ }
193- { showWebGLViewer && ! webGLAvailable && (
194- < div
208+ </ div > : < div
195209 className = "fixed inset-0 z-[100] bg-black/90 flex items-center justify-center overflow-auto"
196210 onClick = { ( e ) => {
197211 if ( e . target === e . currentTarget ) {
@@ -220,43 +234,20 @@ export default function ProgressiveImage(
220234 < line x1 = "6" y1 = "6" x2 = "18" y2 = "18" > </ line >
221235 </ svg >
222236 </ button >
223-
224237 { /* WebGL 不可用提示 */ }
225238 < div className = "absolute top-4 left-4 text-white/70 text-sm bg-black/50 px-3 py-1 rounded" >
226239 { t ( 'Tips.webglUnavailable' ) }
227240 </ div >
228-
229241 < img
230242 className = "max-w-full max-h-full object-contain"
231243 src = { highResImageUrl }
232244 alt = { props . alt || 'image' }
233245 />
234- </ div >
235- ) }
246+ </ div > }
247+ </ Activity >
236248 </ >
237- ) }
249+ ) : null }
238250
239- { /* 加载进度条 */ }
240- { isLoading && (
241- < div className = "absolute bottom-0 left-0 w-full" >
242- < div
243- className = "h-1 bg-blue-500"
244- style = { { width : `${ loadingProgress } %` } }
245- > </ div >
246- < div className = "absolute bottom-2 right-2 bg-black/60 text-white text-xs px-2 py-1 rounded" >
247- { loadingProgress } %
248- </ div >
249- </ div >
250- ) }
251-
252- { /* 错误提示 */ }
253- { error && (
254- < div className = "absolute bottom-0 left-0 w-full" >
255- < div className = "absolute bottom-2 right-2 text-white bg-black/60 text-xs px-2 py-1 rounded" >
256- { error }
257- </ div >
258- </ div >
259- ) }
260251 </ div >
261252 )
262- }
253+ }
0 commit comments