portfolio : fix gallery loading and optimize image sizes refs #59
All checks were successful
Deploy / Deploy to Production (push) Successful in 25s

- Remove GalleryAnimation preloading (was downloading full-size originals
  via new Image(), causing 5MB+ redundant downloads on top of WebP srcset)
- Gallery now shows immediately, images load lazily via DOM
- Force eager loading on mockup img when slide becomes active (fixes
  first-project gallery never showing — lazy img off-screen on bg slide)
- Resize all direct ->url() calls to appropriate display dimensions:
  gallery src 600px, mockup src 960px, backgrounds 1920px,
  play lettering 500px, play preview 1000px, team photos 400px

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
isUnknown 2026-04-15 08:12:55 +02:00
parent 3d8d709165
commit 689ec3b138
5 changed files with 21 additions and 26 deletions

View file

@ -18,7 +18,7 @@ $specificData = [
return [
'name' => $file->memberName()->value(),
'role' => $file->role()->value(),
'photo' => $file->url(),
'photo' => $file->resize(400, null, 80)->url(),
'link' => $file->link()->value() ?: null,
];
})->values()

View file

@ -5,13 +5,13 @@ $specificData = [
return [
'title' => $game->title()->value(),
'slug' => $game->slug(),
'lettering' => $game->lettering()->toFile()?->url(),
'lettering' => $game->lettering()->toFile()?->resize(500, null, 80)->url(),
'description' => $game->description()->value(),
'thumbnail' => $game->thumbnail()->toFile()?->url(),
'thumbnailSrcset' => $game->thumbnail()->toFile()?->srcset('thumbnail'),
'thumbnailWebp' => $game->thumbnail()->toFile()?->srcset('thumbnail-webp'),
'backgroundImage' => $game->backgroundImage()->toFile()?->url(),
'preview' => $game->preview()->toFile()?->url(),
'backgroundImage' => $game->backgroundImage()->toFile()?->resize(1920, null, 80)->url(),
'preview' => $game->preview()->toFile()?->resize(1000, null, 80)->url(),
'playLink' => $game->playLink()->value() ?: null,
];
})->values()

View file

@ -3,7 +3,7 @@
$defaultLang = kirby()->defaultLanguage()->code();
$specificData = [
'backgroundImage' => $page->backgroundImage()->toFile()?->url(),
'backgroundImage' => $page->backgroundImage()->toFile()?->resize(1920, null, 80)->url(),
'projects' => $page->children()->listed()->map(function($project) use ($defaultLang) {
$mockupFile = $project->content($defaultLang)->mockup()->toFile();
return [
@ -13,18 +13,18 @@ $specificData = [
'description' => $project->description()->value(),
'thumbnail' => $project->thumbnail()->toFile()?->resize(160, null, 80)->url(),
'imagesGallery' => $project->imagesGallery()->toFiles()->map(fn($f) => [
'src' => $f->url(),
'src' => $f->resize(600, null, 80)->url(),
'srcset' => $f->srcset('gallery'),
'webp' => $f->srcset('gallery-webp'),
])->values(),
'mockup' => $mockupFile?->url(),
'mockup' => $mockupFile?->resize(960, null, 80)->url(),
'mockupSrcset' => $mockupFile?->srcset('mockup'),
'mockupWebp' => $mockupFile?->srcset('mockup-webp'),
'galleryAnimationMode' => $project->galleryAnimationMode()->value() ?: 'vertical',
'mockupPosition' => $project->mockupPosition()->value() ?: 'center',
'secondsPerImage' => $project->secondsPerImage()->isNotEmpty() ? (int) $project->secondsPerImage()->value() : 8,
'galleryBackgroundColor' => $project->galleryBackgroundColor()->value(),
'galleryBackgroundImage' => $project->galleryBackgroundImage()->isNotEmpty() ? $project->galleryBackgroundImage()->toFile()->url() : null,
'galleryBackgroundImage' => $project->galleryBackgroundImage()->isNotEmpty() ? $project->galleryBackgroundImage()->toFile()->resize(1200, null, 80)->url() : null,
'keywords' => $project->keywords()->toStructure()->map(fn($i) => [
'label' => $i->label()->value(),
'text' => $i->text()->value(),

View file

@ -15,25 +15,10 @@
let allLoaded = $state(false)
let needsFade = $state(false)
// Wait for all unique source images to load before revealing
// Show immediately — the DOM images load via lazy loading when visible.
// No explicit preloading: it was forcing full-size originals to download.
onMount(() => {
const uniqueSrcs = new Set(images.map(img => img.src).filter(Boolean))
if (uniqueSrcs.size === 0) { allLoaded = true; return }
let remaining = uniqueSrcs.size
const done = () => { if (--remaining <= 0) allLoaded = true }
for (const src of uniqueSrcs) {
const img = new Image()
img.src = src
if (img.complete) {
done()
} else {
needsFade = true
img.onload = done
img.onerror = done
}
}
allLoaded = true
})
const columns = $derived.by(() => {

View file

@ -145,6 +145,16 @@
return () => img.removeEventListener('load', onLoad)
})
// Force le chargement du mockup quand la slide devient active :
// Si l'image est lazy et a été rendue off-screen (slide en arrière-plan),
// le browser ne la charge pas → mockupReady reste false → galerie jamais affichée.
$effect(() => {
if (!isActive || mockupReady || !mockupEl) return
const img = mockupEl.querySelector('img')
if (!img || img.complete) return
img.loading = 'eager'
})
// --- Effect: reset when slide deactivated ---
// wasActive évite que clearAnchor() s'exécute au montage initial
// (isActive est false avant l'initialisation des slides)