Curseur infini CSS feuilletant les images Polaroid PlatoBlockchain Data Intelligence. Recherche verticale. Aï.

CSS Infinite Slider Feuilletant les images Polaroid

Dans le dernier article, nous avons créé un petit curseur assez sympa (ou "carrousel" si c'est ce que vous préférez) qui tourne dans une direction circulaire. Cette fois, nous allons en créer un qui feuillette une pile d'images Polaroid.

Cool non ? Ne regardez pas encore le code car il y a beaucoup à démêler. Rejoignez-moi, voulez-vous ?

Série de curseurs CSS

La configuration de base

La plupart du code HTML et CSS de ce curseur est similaire à celui circulaire que nous avons créé la dernière fois. En fait, nous utilisons exactement le même balisage :

Et c'est le CSS de base qui définit notre parent .gallery conteneur sous forme de grille où toutes les images sont empilées les unes sur les autres :

.gallery  {
  display: grid;
  width: 220px; /* controls the size */
}
.gallery > img {
  grid-area: 1 / 1;
  width: 100%;
  aspect-ratio: 1;
  object-fit: cover;
  border: 10px solid #f2f2f2;
  box-shadow: 0 0 4px #0007;
}

Rien de complexe pour l'instant. Même pour le style de type Polaroid pour les images, tout ce que j'utilise, c'est border et de box-shadow. Vous pourrez peut-être faire mieux, alors n'hésitez pas à jouer avec ces styles décoratifs ! Nous allons mettre l'accent sur l'animation, qui est la partie la plus délicate.

C'est quoi le truc?

La logique de ce curseur repose sur l'ordre d'empilement des images — alors oui, nous allons jouer avec z-index. Toutes les images commencent par le même z-index valeur (2) qui fera logiquement la dernière image en haut de la pile.

Nous prenons cette dernière image et la faisons glisser vers la droite jusqu'à ce qu'elle révèle l'image suivante dans la pile. Puis on diminue l'image z-index valeur puis nous le glissons dans le pont. Et depuis sa z-index est inférieure au reste des images, elle devient la dernière image de la pile.

Voici une démo simplifiée qui montre l'astuce. Survolez l'image pour activer l'animation :

Maintenant, imaginez la même astuce appliquée à toutes les images. Voici le modèle si nous utilisons le :nth-child() pseudo-sélecteur pour différencier les images :

  • On glisse la dernière image (N). L'image suivante est visible (N - 1).
  • Nous glissons l'image suivante (N - 1). L'image suivante est visible (N - 2)
  • Nous glissons l'image suivante (N - 2). L'image suivante est visible (N - 3)
  • (Nous continuons le même processus jusqu'à ce que nous atteignions la première image)
  • On glisse la première image (1). La dernière image (N) est à nouveau visible.

C'est notre curseur infini !

Disséquer l'animation

Si vous vous souvenez de l'article précédent, j'ai défini une seule animation et joué avec des délais pour contrôler chaque image. Nous ferons la même chose ici. Essayons d'abord de visualiser la chronologie de notre animation. Nous allons commencer avec trois images, puis généraliser plus tard pour n'importe quel nombre (N) d'images.

CSS Infinite Slider Feuilletant les images Polaroid

Notre animation est divisée en trois parties : « slide to right », « slide to left » et « don't move ». On peut facilement identifier le délai entre chaque image. Si l'on considère que la première image commence à 0s, et la durée est égale à 6s, puis le second commencera à -2s et le troisième à -4s.

.gallery > img:nth-child(2) { animation-delay: -2s; } /* -1 * 6s / 3 */
.gallery > img:nth-child(3) { animation-delay: -4s; } /* -2 * 6s / 3 */

Nous pouvons également voir que la partie "ne bougez pas" prend les deux tiers de toute l'animation (2*100%/3) tandis que les parties "glisser vers la droite" et "glisser vers la gauche" en prennent un tiers ensemble - donc, chacune est égale à 100%/6 de l'animation totale.

Nous pouvons écrire nos images clés d'animation comme ceci :

@keyframes slide {
  0%     { transform: translateX(0%); }
  16.67% { transform: translateX(120%); }
  33.34% { transform: translateX(0%); }
  100%   { transform: translateX(0%); } 
}

Ceci 120% est une valeur arbitraire. J'avais besoin de quelque chose de plus grand que 100%. Les images doivent glisser vers la droite loin du reste des images. Pour ce faire, il doit se déplacer d'au moins 100% de sa taille. C'est pourquoi je suis allé 120% — pour gagner de l'espace supplémentaire.

Maintenant, nous devons considérer le z-index. N'oubliez pas que nous devons mettre à jour l'image z-index Plus-value après il glisse à droite de la pile, et before nous le glissons vers le bas de la pile.

@keyframes slide {
  0%     { transform: translateX(0%);   z-index: 2; }
  16.66% { transform: translateX(120%); z-index: 2; }
  16.67% { transform: translateX(120%); z-index: 1; } /* we update the z-order here */
  33.34% { transform: translateX(0%);   z-index: 1; }
  100%   { transform: translateX(0% );  z-index: 1; }  
}

Au lieu de définir un état à la 16.67% (100%/6) point dans la chronologie, nous définissons deux états à des points presque identiques (16.66% et de 16.67%) où le z-index la valeur diminue avant de faire glisser l'image vers le pont.

Voici ce qui se passe lorsque nous rassemblons tout cela :

Hmmm, la partie coulissante semble bien fonctionner, mais l'ordre d'empilement est tout brouillé ! L'animation démarre bien puisque l'image du haut se déplace vers l'arrière… mais les images suivantes ne suivent pas. Si vous le remarquez, la deuxième image de la séquence revient en haut de la pile avant que l'image suivante ne clignote dessus.

Nous devons suivre de près les z-index changements. Initialement, toutes les images ont sont z-index: 2. Cela signifie que l'ordre d'empilement devrait aller…

Our eyes 👀 --> 3rd (2) | 2nd (2) | 1st (2)

Nous glissons la troisième image et mettons à jour son z-index pour obtenir cette commande :

Our eyes 👀 --> 2nd (2) | 1st (2) | 3rd (1)

On fait la même chose avec le second :

Our eyes 👀 --> 1st (2) | 3rd (1) | 2nd (1)

…et le premier :

Our eyes 👀 --> 3rd (1) | 2nd (1) | 1st (1)

Nous le faisons et tout semble aller bien. Mais en réalité, ce n'est pas le cas ! Lorsque la première image est déplacée vers l'arrière, la troisième image commencera une autre itération, ce qui signifie qu'elle revient à z-index: 2:

Our eyes 👀 --> 3rd (2) | 2nd (1) | 1st (1)

Donc, en réalité, nous n'avons jamais eu toutes les images à z-index: 2 du tout! Lorsque les images ne bougent pas (c'est-à-dire la partie « ne bougez pas » de l'animation), le z-index is 1. Si nous faisons glisser la troisième image et mettons à jour son z-index valeur de 2 à 1, il restera au top ! Quand toutes les images ont le même z-index, la dernière dans l'ordre des sources — notre troisième image dans ce cas — est au sommet de la pile. Faire glisser la troisième image donne les résultats suivants :

Our eyes 👀 --> 3rd (1) | 2nd (1) | 1st (1)

La troisième image est toujours en haut et, juste après, nous déplaçons la deuxième image vers le haut lorsque son animation redémarre à z-index: 2:

Our eyes 👀 --> 2nd (2) | 3rd (1) | 1st (1)

Une fois que nous l'avons glissé, nous obtenons:

Our eyes 👀 --> 3rd (1) | 2nd (1) | 1st (1)

Ensuite, la première image sautera en haut :

Our eyes 👀 --> 1st(2) | 3rd (1) | 2nd (1)

Bon, je suis perdu. Toute la logique est fausse alors?

Je sais, c'est déroutant. Mais notre logique n'est pas complètement fausse. Nous n'avons qu'à rectifier un peu l'animation pour que tout fonctionne comme nous le souhaitons. L'astuce consiste à réinitialiser correctement le z-index.

Prenons la situation où la troisième image est en haut :

Our eyes 👀 -->  3rd (2) | 2nd (1) | 1st (1)

Nous avons vu que faire glisser la troisième image et changer son z-index le maintient au top. Ce que nous devons faire, c'est mettre à jour le z-index de la deuxième image. Donc, avant de faire glisser la troisième image hors du jeu, nous mettons à jour le z-index de la deuxième image à 2.

En d'autres termes, nous réinitialisons le z-index de la deuxième image avant la fin de l'animation.

Diagramme des parties de l'animation avec des indicateurs indiquant où l'index z est augmenté ou diminué.
CSS Infinite Slider Feuilletant les images Polaroid

Le symbole plus vert représente l'augmentation z-index à 2, et le symbole moins rouge correspond à z-index: 1. La deuxième image commence par z-index: 2, puis nous le mettons à jour pour 1 lorsqu'il s'éloigne du pont. Mais avant que la première image ne glisse du jeu, nous changeons le z-index de la deuxième image vers 2. Cela garantira que les deux images ont la même z-index, mais encore, le troisième restera en haut car il apparaît plus tard dans le DOM. Mais après la troisième image glisse et son z-index est mis à jour, il se déplace vers le bas.

Ces deux tiers de l'animation, mettons donc à jour nos images clés en conséquence :

@keyframes slide {
  0%     { transform: translateX(0%);   z-index: 2; }
  16.66% { transform: translateX(120%); z-index: 2; }
  16.67% { transform: translateX(120%); z-index: 1; } /* we update the z-order here */
  33.34% { transform: translateX(0%);   z-index: 1; }
  66.33% { transform: translateX(0%);   z-index: 1; }
  66.34% { transform: translateX(0%);   z-index: 2; } /* and also here */
  100%   { transform: translateX(0%);   z-index: 2; }  
}

Un peu mieux, mais toujours pas assez là. Il y a un autre problème…

Oh non, ça ne finira jamais !

Ne vous inquiétez pas, nous n'allons pas modifier à nouveau les images clés car ce problème ne se produit que lorsque la dernière image est impliquée. Nous pouvons créer une animation d'image clé "spéciale" spécifiquement pour la dernière image pour arranger les choses.

Lorsque la première image est en haut, nous avons la situation suivante :

Our eyes 👀 -->  1st (2) | 3rd (1) | 2nd (1)

Compte tenu du réglage précédent que nous avons effectué, la troisième image sautera en haut avant que la première image ne glisse. Cela ne se produit que dans cette situation car la prochaine image qui se déplace après la première image est la dernier image qui a un ordre supérieur dans le DOM. Le reste des images est bien parce que nous avons N, puis N - 1, puis on passe de 3 à 2et une 2 à 1… mais ensuite on passe de 1 à N.

Pour éviter cela, nous utiliserons les images clés suivantes pour la dernière image :

@keyframes slide-last {
  0%     { transform: translateX(0%);   z-index: 2;}
  16.66% { transform: translateX(120%); z-index: 2; }
  16.67% { transform: translateX(120%); z-index: 1; } /* we update the z-order here */
  33.34% { transform: translateX(0%);   z-index: 1; }
  83.33% { transform: translateX(0%);   z-index: 1; }
  83.34% { transform: translateX(0%);   z-index: 2; } /* and also here */
  100%   { transform: translateX(0%);   z-index: 2; }
}

Nous réinitialisons le z-index valeur 5/6 à travers l'animation (au lieu des deux tiers) qui correspond au moment où la première image est sortie de la pile. On ne voit donc aucun saut !

TAD ! Notre curseur infini est maintenant parfait ! Voici notre code final dans toute sa splendeur :

.gallery > img {
  animation: slide 6s infinite;
}
.gallery > img:last-child {
  animation-name: slide-last;
}
.gallery > img:nth-child(2) { animation-delay: -2s; } 
.gallery > img:nth-child(3) { animation-delay: -4s; }

@keyframes slide {
  0% { transform: translateX(0%); z-index: 2; }
  16.66% { transform: translateX(120%); z-index: 2; }
  16.67% { transform: translateX(120%); z-index: 1; } 
  33.34% { transform: translateX(0%); z-index: 1; }
  66.33% { transform: translateX(0%); z-index: 1; }
  66.34% { transform: translateX(0%); z-index: 2; } 
  100% { transform: translateX(0%); z-index: 2; }
}
@keyframes slide-last {
  0% { transform: translateX(0%); z-index: 2; }
  16.66% { transform: translateX(120%); z-index: 2; }
  16.67% { transform: translateX(120%); z-index: 1; }
  33.34% { transform: translateX(0%); z-index: 1; }
  83.33% { transform: translateX(0%); z-index: 1; }
  83.34% { transform: translateX(0%); z-index: 2; } 
  100%  { transform: translateX(0%); z-index: 2; }
}

Prise en charge de n'importe quel nombre d'images

Maintenant que notre animation fonctionne pour trois images, faisons-la fonctionner pour n'importe quel nombre (N) d'images. Mais d'abord, nous pouvons optimiser un peu notre travail en divisant l'animation pour éviter la redondance :

.gallery > img {
  z-index: 2;
  animation: 
    slide 6s infinite,
    z-order 6s infinite steps(1);
}
.gallery > img:last-child {
  animation-name: slide, z-order-last;
}
.gallery > img:nth-child(2) { animation-delay: -2s; } 
.gallery > img:nth-child(3) { animation-delay: -4s; }

@keyframes slide {
  16.67% { transform: translateX(120%); }
  33.33% { transform: translateX(0%); }
}
@keyframes z-order {
  16.67%,
  33.33% { z-index: 1; }
  66.33% { z-index: 2; }
}
@keyframes z-order-last {
  16.67%,
  33.33% { z-index: 1; }
  83.33% { z-index: 2; }
}

Beaucoup moins de code maintenant ! On fait une animation pour la partie coulissante et une autre pour la z-index mises à jour. Notez que nous utilisons steps(1) sur le z-index animation. C'est parce que je veux changer brusquement le z-index valeur, contrairement à l'animation de glissement où nous voulons un mouvement fluide.

Maintenant que le code est plus facile à lire et à maintenir, nous avons une meilleure vue pour comprendre comment prendre en charge n'importe quel nombre d'images. Ce que nous devons faire, c'est mettre à jour les délais d'animation et les pourcentages des images clés. Le délai est facile car nous pouvons utiliser exactement la même boucle que celle que nous avons faite dans le dernier article pour prendre en charge plusieurs images dans le curseur circulaire :

@for $i from 2 to ($n + 1) {
  .gallery > img:nth-child(#{$i}) {
    animation-delay: calc(#{(1 - $i)/$n}*6s);
  }
}

Cela signifie que nous passons du CSS vanille à Sass. Ensuite, nous devons imaginer comment la chronologie évolue avec N images. N'oublions pas que l'animation se déroule en trois phases :

Affichage des trois parties de l'animation dans une série de lignes avec des flèches.
CSS Infinite Slider Feuilletant les images Polaroid

Après "glisser vers la droite" et "glisser vers la gauche", l'image doit rester en place jusqu'à ce que le reste des images passe par la séquence. Ainsi, la partie "ne bougez pas" doit prendre le même temps que (N - 1) comme "glisser vers la droite" et "glisser vers la gauche". Et en une itération, N les images vont glisser. Ainsi, "glisser vers la droite" et "glisser vers la gauche" prennent tous deux 100%/N de la chronologie totale de l'animation. L'image s'éloigne de la pile à (100%/N)/2 et glisse vers l'arrière à 100%/N .

Nous pouvons changer ceci :

@keyframes slide {
  16.67% { transform: translateX(120%); }
  33.33% { transform: translateX(0%); }
}

…pour ça:

@keyframes slide {
  #{50/$n}%  { transform: translateX(120%); }
  #{100/$n}% { transform: translateX(0%); }
}

Si nous remplaçons N comprenant 3, on a 16.67% et de 33.33% quand il y a 3 images dans la pile. C'est la même logique avec l'ordre d'empilement où nous aurons ceci :

@keyframes z-order {
  #{50/$n}%,
  #{100/$n}% { z-index: 1; }
  66.33% { z-index: 2; }
}

Nous devons encore mettre à jour le 66.33% indiquer. C'est censé être là où l'image réinitialise son z-index avant la fin de l'animation. Au même moment, l'image suivante commence à glisser. Étant donné que la partie coulissante prend 100%/N, la réinitialisation devrait avoir lieu à 100% - 100%/N:

@keyframes z-order {
  #{50/$n}%,
  #{100/$n}% { z-index: 1; }
  #{100 - 100/$n}% { z-index: 2; }
}

Mais pour notre z-order-last l'animation fonctionne, cela devrait arriver un peu plus tard dans la séquence. Vous souvenez-vous de la correction que nous avons faite pour la dernière image ? Réinitialiser le z-index la valeur doit se produire lorsque la première image est sortie de la pile et non lorsqu'elle commence à glisser. Nous pouvons utiliser le même raisonnement ici dans nos images clés :

@keyframes z-order-last {
  #{50/$n}%,
  #{100/$n}% { z-index: 1; }
  #{100 - 50/$n}% { z-index: 2; }
}

Nous avons fini! Voici ce que nous obtenons en utilisant cinq images :

Nous pouvons ajouter une touche de rotation pour rendre les choses un peu plus fantaisistes :

Tout ce que j'ai fait est d'ajouter rotate(var(--r)) à la transform propriété. A l'intérieur de la boucle, --r est défini avec un angle aléatoire :

@for $i from 1 to ($n + 1) {
  .gallery > img:nth-child(#{$i}) {
    --r: #{(-20 + random(40))*1deg}; /* a random angle between -20deg and 20deg */
  }
}

La rotation crée de petits problèmes car nous pouvons parfois voir certaines des images sauter à l'arrière de la pile, mais ce n'est pas grave.

Emballage en place

Tout ça z-index le travail était un grand exercice d'équilibre, n'est-ce pas ? Si vous n'étiez pas sûr du fonctionnement de l'ordre d'empilement avant cet exercice, vous avez probablement une bien meilleure idée maintenant ! Si vous avez trouvé certaines des explications difficiles à suivre, je vous recommande fortement de prendre une autre lecture de l'article et cartographier les choses avec un crayon et du papier. Essayez d'illustrer chaque étape de l'animation en utilisant un nombre différent d'images pour mieux comprendre l'astuce.

La dernière fois, nous avons utilisé quelques astuces de géométrie pour créer un curseur circulaire qui revient à la première image après une séquence complète. Cette fois, nous avons accompli une astuce similaire en utilisant z-index. Dans les deux cas, nous n'avons dupliqué aucune des images pour simuler une animation continue, et nous n'avons pas non plus fait appel à JavaScript pour nous aider dans les calculs.

La prochaine fois, nous ferons des curseurs 3D. Restez à l'écoute!

Horodatage:

Plus de Astuces CSS