world-game/src/components/ui/GalleryAnimation.svelte
isUnknown 47be2b4662 Refactor: styles GalleryAnimation migrés dans le composant
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 18:11:31 +01:00

123 lines
3.3 KiB
Svelte
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script>
/**
* GalleryAnimation — animation CSS de galerie en 3 colonnes défilantes.
* @prop {Array<{src: string, srcset: string, webp: string}>} images
* @prop {number} secondsPerImage — durée par image (défaut: 8s)
*/
let { images = [], secondsPerImage = 8 } = $props()
const columns = $derived.by(() => {
const count = images.length
const duration = count * secondsPerImage
const defs = [
{ offset: 0, delay: 0 },
{ offset: Math.floor(count / 3), delay: duration / 4 },
{ offset: 0, delay: duration / 2 },
]
return defs.map(({ offset, delay }) => ({
images: shiftImages(images, offset),
delay,
duration,
}))
})
function shiftImages(imgs, offset) {
if (!offset) return imgs
return [...imgs.slice(offset), ...imgs.slice(0, offset)]
}
</script>
<div
class="gallery-animation gallery-animation--vertical"
style="--gallery-duration: {columns[0]?.duration ?? 24}s"
>
{#each columns as col}
<div class="gallery-animation__column">
<div
class="gallery-animation__track"
style="animation-delay: -{col.delay}s"
>
<!-- Images × 2 pour le défilement infini -->
{#each [col.images, col.images] as set}
{#each set as img}
<picture>
<source type="image/webp" srcset={img.webp} sizes="(max-width: 700px) 33vw, 15vw" />
<img
class="gallery-animation__image"
src={img.src}
srcset={img.srcset}
sizes="(max-width: 700px) 33vw, 15vw"
alt=""
aria-hidden="true"
loading="lazy"
decoding="async"
/>
</picture>
{/each}
{/each}
</div>
</div>
{/each}
</div>
<style>
.gallery-animation {
width: 100%;
height: 100%;
overflow: hidden;
}
/* Vertical mode (Portfolio) */
.gallery-animation--vertical {
display: flex;
flex-direction: row;
gap: 8px;
}
.gallery-animation--vertical :global(.gallery-animation__column) {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
.gallery-animation--vertical :global(.gallery-animation__track) {
display: flex;
flex-direction: column;
animation-timing-function: linear;
animation-iteration-count: infinite;
}
.gallery-animation--vertical :global(.gallery-animation__column:nth-child(odd) .gallery-animation__track) {
animation-name: galleryScrollDown;
animation-duration: var(--gallery-duration);
}
.gallery-animation--vertical :global(.gallery-animation__column:nth-child(even) .gallery-animation__track) {
animation-name: galleryScrollUp;
animation-duration: var(--gallery-duration);
}
:global(.gallery-animation__image) {
width: 100%;
height: auto;
display: block;
object-fit: contain;
}
@keyframes galleryScrollDown {
from { transform: translateY(0); }
to { transform: translateY(-50%); }
}
@keyframes galleryScrollUp {
from { transform: translateY(-50%); }
to { transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) {
:global(.gallery-animation__track) {
animation: none;
}
}
</style>