@@ -12,17 +12,14 @@ useEcharts([RadarChart, RadarComponent, TooltipComponent, LegendComponent, Canva
1212
1313const SLIDE_TRANSITION_MS = 820 ;
1414
15- const artPhotoModules = import . meta. glob ( "../public/art/**/*.webp" , {
16- eager : true ,
17- import : "default" ,
18- } ) ;
15+ const artPhotoModules = import . meta. glob ( "../public/art/**/*.webp" ) ;
1916
20- const artPhotoCategoryMap = Object . entries ( artPhotoModules ) . reduce ( ( accumulator , [ path , src ] ) => {
17+ const artPhotoCategoryMap = Object . keys ( artPhotoModules ) . reduce ( ( accumulator , path ) => {
2118 const matched = path . match ( / \/ a r t \/ ( [ ^ / ] + ) \/ [ ^ / ] + $ / ) ;
2219 if ( ! matched ) return accumulator ;
2320 const category = decodeURIComponent ( matched [ 1 ] ) ;
2421 if ( ! accumulator [ category ] ) accumulator [ category ] = [ ] ;
25- accumulator [ category ] . push ( src ) ;
22+ accumulator [ category ] . push ( path . replace ( "../public" , "" ) ) ;
2623 return accumulator ;
2724} , { } ) ;
2825
@@ -398,6 +395,7 @@ export default function App() {
398395 const [ form , setForm ] = useState ( { name : "" , email : "" , subject : contactConfig . defaultSubject , message : "" } ) ;
399396 const touchStartY = useRef ( null ) ;
400397 const touchStartSection = useRef ( null ) ;
398+ const artHoverRafRef = useRef ( null ) ;
401399 const toolboxRef = useRef ( null ) ;
402400 const unlockTimerRef = useRef ( null ) ;
403401 const transitionTimerRef = useRef ( null ) ;
@@ -565,6 +563,7 @@ export default function App() {
565563 useEffect ( ( ) => ( ) => {
566564 window . clearTimeout ( unlockTimerRef . current ) ;
567565 window . clearTimeout ( transitionTimerRef . current ) ;
566+ if ( artHoverRafRef . current ) window . cancelAnimationFrame ( artHoverRafRef . current ) ;
568567 } , [ ] ) ;
569568
570569 useEffect ( ( ) => {
@@ -577,24 +576,35 @@ export default function App() {
577576 } , [ artCategoryIndex , portfolioMode ] ) ;
578577
579578 useEffect ( ( ) => {
580- if ( portfolioMode !== "art" || ! artPhotoCategories . length ) return undefined ;
579+ if ( portfolioMode !== "art" || ! artPhotoCategories . length || activeIndex !== portfolioSectionIndex ) return undefined ;
580+ if ( window . matchMedia ( "(prefers-reduced-motion: reduce)" ) . matches ) return undefined ;
581+ if ( ( navigator . hardwareConcurrency ?? 8 ) <= 4 ) return undefined ;
581582
582583 const safeCategoryIndex = ( ( artCategoryIndex % artPhotoCategories . length ) + artPhotoCategories . length ) % artPhotoCategories . length ;
583584 const currentPhotos = artPhotoCategories [ safeCategoryIndex ] ?. photos ?? [ ] ;
584585 if ( currentPhotos . length <= 1 ) return undefined ;
585586
586587 const timer = window . setInterval ( ( ) => {
588+ if ( document . hidden ) return ;
587589 setArtPhotoIndex ( ( current ) => ( current + 1 ) % currentPhotos . length ) ;
588- } , 3500 ) ;
590+ } , 5500 ) ;
589591
590592 return ( ) => window . clearInterval ( timer ) ;
591- } , [ artCategoryIndex , portfolioMode ] ) ;
593+ } , [ activeIndex , artCategoryIndex , portfolioMode , portfolioSectionIndex ] ) ;
592594
593595 useEffect ( ( ) => {
594596 if ( portfolioMode !== "art" ) return ;
595597 const randomIndex = Math . floor ( Math . random ( ) * artCopyPool . length ) ;
596598 setArtCopy ( artCopyPool [ randomIndex ] ) ;
597- } , [ portfolioMode , artCategoryIndex , artPhotoIndex ] ) ;
599+ } , [ portfolioMode , artCategoryIndex ] ) ;
600+
601+ const handleArtTileHover = ( index ) => {
602+ if ( index === artPhotoIndex || artHoverRafRef . current ) return ;
603+ artHoverRafRef . current = window . requestAnimationFrame ( ( ) => {
604+ setArtPhotoIndex ( index ) ;
605+ artHoverRafRef . current = null ;
606+ } ) ;
607+ } ;
598608
599609 const preloadAround = ( index ) => {
600610 setLoadedIndexes ( ( current ) => {
@@ -1235,7 +1245,7 @@ export default function App() {
12351245 < Card className = "glass-card portfolio-card" >
12361246 < Grid container >
12371247 < Grid size = { { xs : 12 , md : 7 } } >
1238- < img src = { work . image } alt = { work . title } loading = "lazy" className = "portfolio-image portfolio-image-large" />
1248+ < img src = { work . image } alt = { work . title } loading = "lazy" decoding = "async" className = "portfolio-image portfolio-image-large" />
12391249 </ Grid >
12401250 < Grid size = { { xs : 12 , md : 5 } } >
12411251 < CardContent className = "portfolio-content" >
@@ -1285,7 +1295,7 @@ export default function App() {
12851295 < Card className = "glass-card portfolio-card art-matrix-shell" >
12861296 < Grid container >
12871297 < Grid size = { { xs : 12 , md : 7 } } >
1288- { artLeadPhoto ? < img src = { artLeadPhoto } alt = { normalizeArtCategoryName ( activeArtCategory . name ) } loading = "lazy" className = "portfolio-image portfolio-image-large art-lead-image" /> : null }
1298+ { artLeadPhoto ? < img src = { artLeadPhoto } alt = { normalizeArtCategoryName ( activeArtCategory . name ) } loading = "lazy" decoding = "async" className = "portfolio-image portfolio-image-large art-lead-image" /> : null }
12891299 </ Grid >
12901300 < Grid size = { { xs : 12 , md : 5 } } >
12911301 < CardContent className = "portfolio-content" >
@@ -1321,11 +1331,11 @@ export default function App() {
13211331 animate = { { opacity : 1 , y : 0 } }
13221332 transition = { { duration : 0.35 , delay : Math . min ( index * 0.03 , 0.28 ) , ease : "easeOut" } }
13231333 whileHover = { { y : - 8 , scale : 1.02 } }
1324- onMouseEnter = { ( ) => setArtPhotoIndex ( index ) }
1325- onFocus = { ( ) => setArtPhotoIndex ( index ) }
1334+ onMouseEnter = { ( ) => handleArtTileHover ( index ) }
1335+ onFocus = { ( ) => handleArtTileHover ( index ) }
13261336 onClick = { ( ) => setArtPhotoIndex ( index ) }
13271337 >
1328- < img src = { photo } alt = { `${ normalizeArtCategoryName ( activeArtCategory . name ) } -${ index + 1 } ` } loading = "lazy" className = "art-photo-image" />
1338+ < img src = { photo } alt = { `${ normalizeArtCategoryName ( activeArtCategory . name ) } -${ index + 1 } ` } loading = "lazy" decoding = "async" className = "art-photo-image" />
13291339 < Box component = "figcaption" className = "art-photo-caption" > FRAME { String ( index + 1 ) . padStart ( 2 , "0" ) } </ Box >
13301340 </ motion . figure >
13311341 ) ;
0 commit comments