portfolio : animation horizontale — toggle panel + composant
All checks were successful
Deploy / Deploy to Production (push) Successful in 19s
All checks were successful
Deploy / Deploy to Production (push) Successful in 19s
Ajout du mode horizontal dans GalleryAnimation (5 rangées, scrollLeft/scrollRight) avec toggle vertical/horizontal dans le panel projet. refs #21 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
347ccd33eb
commit
133651c35d
5 changed files with 116 additions and 21 deletions
|
|
@ -1,19 +1,32 @@
|
|||
<script>
|
||||
/**
|
||||
* GalleryAnimation — animation CSS de galerie en 3 colonnes défilantes.
|
||||
* GalleryAnimation — animation CSS de galerie défilante.
|
||||
* Mode vertical : 3 colonnes (haut/bas)
|
||||
* Mode horizontal : 5 rangées (gauche/droite)
|
||||
* @prop {Array<{src: string, srcset: string, webp: string}>} images
|
||||
* @prop {number} secondsPerImage — durée par image (défaut: 8s)
|
||||
* @prop {'vertical'|'horizontal'} mode — direction du défilement
|
||||
*/
|
||||
let { images = [], secondsPerImage = 8, backgroundColor = null, backgroundImage = null } = $props()
|
||||
let { images = [], secondsPerImage = 8, backgroundColor = null, backgroundImage = null, mode = 'vertical' } = $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 },
|
||||
]
|
||||
|
||||
const defs = mode === 'horizontal'
|
||||
? [
|
||||
{ offset: 0, delay: 0 },
|
||||
{ offset: Math.floor(count / 5), delay: duration / 5 },
|
||||
{ offset: Math.floor(2 * count / 5), delay: 2 * duration / 5 },
|
||||
{ offset: Math.floor(count / 5), delay: 3 * duration / 5 },
|
||||
{ offset: 0, delay: 4 * duration / 5 },
|
||||
]
|
||||
: [
|
||||
{ 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,
|
||||
|
|
@ -28,7 +41,7 @@
|
|||
</script>
|
||||
|
||||
<div
|
||||
class="gallery-animation gallery-animation--vertical"
|
||||
class="gallery-animation gallery-animation--{mode}"
|
||||
style="--gallery-duration: {columns[0]?.duration ?? 24}s{backgroundColor ? `; --background-color: ${backgroundColor}` : ''}{backgroundImage ? `; --gallery-background-image: url(${backgroundImage})` : ''}"
|
||||
>
|
||||
<div class="gallery-animation__overlay"></div>
|
||||
|
|
@ -72,7 +85,22 @@
|
|||
opacity: .8;
|
||||
}
|
||||
|
||||
/* Vertical mode (Portfolio) */
|
||||
.gallery-animation__overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-color: #000;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
:global(.gallery-animation__image) {
|
||||
display: block;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
MODE VERTICAL — 3 colonnes
|
||||
========================================================================== */
|
||||
|
||||
.gallery-animation--vertical {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
|
@ -105,18 +133,9 @@
|
|||
animation-duration: var(--gallery-duration);
|
||||
}
|
||||
|
||||
.gallery-animation__overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-color: #000;
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
:global(.gallery-animation__image) {
|
||||
.gallery-animation--vertical :global(.gallery-animation__image) {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
@keyframes galleryScrollDown {
|
||||
|
|
@ -129,6 +148,65 @@
|
|||
to { transform: translateY(0); }
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
MODE HORIZONTAL — 5 rangées
|
||||
========================================================================== */
|
||||
|
||||
.gallery-animation--horizontal {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
.gallery-animation--horizontal :global(.gallery-animation__column) {
|
||||
--inner-height: 100vh;
|
||||
height: clamp(calc(var(--inner-height) / 5), 20%, calc(var(--inner-height) / 3));
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: stretch;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.gallery-animation--horizontal :global(.gallery-animation__track) {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: stretch;
|
||||
height: 100%;
|
||||
column-gap: 1rem;
|
||||
animation-timing-function: linear;
|
||||
animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
.gallery-animation--horizontal :global(.gallery-animation__column:nth-child(odd) .gallery-animation__track) {
|
||||
animation-name: galleryScrollRight;
|
||||
animation-duration: var(--gallery-duration);
|
||||
}
|
||||
|
||||
.gallery-animation--horizontal :global(.gallery-animation__column:nth-child(even) .gallery-animation__track) {
|
||||
animation-name: galleryScrollLeft;
|
||||
animation-duration: var(--gallery-duration);
|
||||
}
|
||||
|
||||
.gallery-animation--horizontal :global(.gallery-animation__image) {
|
||||
height: 100%;
|
||||
width: auto;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@keyframes galleryScrollRight {
|
||||
from { transform: translateX(-50%); }
|
||||
to { transform: translateX(0); }
|
||||
}
|
||||
|
||||
@keyframes galleryScrollLeft {
|
||||
from { transform: translateX(0); }
|
||||
to { transform: translateX(-50%); }
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
REDUCED MOTION
|
||||
========================================================================== */
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
:global(.gallery-animation__track) {
|
||||
animation: none;
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@
|
|||
</script>
|
||||
|
||||
<div class="portfolio-gallery mobile-only" aria-hidden="true">
|
||||
<GalleryAnimation images={currentProject.imagesGallery} backgroundColor={currentProject.galleryBackgroundColor} backgroundImage={currentProject.galleryBackgroundImage} />
|
||||
<GalleryAnimation images={currentProject.imagesGallery} backgroundColor={currentProject.galleryBackgroundColor} backgroundImage={currentProject.galleryBackgroundImage} mode={currentProject.galleryAnimationMode} />
|
||||
</div>
|
||||
|
||||
<section
|
||||
|
|
@ -99,7 +99,7 @@
|
|||
{#if currentProject}
|
||||
<!-- Galerie animation (gauche desktop / plein écran mobile) -->
|
||||
<div class="portfolio-gallery desktop-only" aria-hidden="true">
|
||||
<GalleryAnimation images={currentProject.imagesGallery} backgroundColor={currentProject.galleryBackgroundColor} backgroundImage={currentProject.galleryBackgroundImage} />
|
||||
<GalleryAnimation images={currentProject.imagesGallery} backgroundColor={currentProject.galleryBackgroundColor} backgroundImage={currentProject.galleryBackgroundImage} mode={currentProject.galleryAnimationMode} />
|
||||
</div>
|
||||
|
||||
<!-- Mockup device (centre) -->
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue