Stores vénitiens 3D en CSS

Les variations sur cette transition ont longtemps été un pilier de jQuery. Toujours partant pour de nouveaux défis, Dudley Storey a imaginé une superbe solution 3D responsive en pur CSS.

Par

Les variations sur cette transition ont longtemps été un pilier des carrousels jQuery. Je me suis demandé si on pouvait obtenir le même effet en pur CSS, avec la condition supplémentaire que le résultat devait être responsif — ce qui n’est pas le cas de la plupart des solutions JavaScript.

See the Pen 3D Responsive Image Venetian Blind Transition Effect In Pure CSS by Dudley Storey (@dudleystorey) on CodePen.

C’est certainement faisable, bien qu’avec pas mal de markup : la solution requiert autant de copies de l’image que nous voulons de lamelles de store. Dans notre cas, je voulais 10 lamelles verticales pour des images qui auraient une largeur maximale de 1000 pixels. Donc 10 copies de chaque photo, à l’intérieur d’un conteneur <figure>. En même temps, j’avais besoin de 10 copies de l’autre image pour l’autre côté de la lamelle. Chaque image aurait une classe différente :

<figure id=blinds>
  <img src="autumn-face.jpg" alt class="first">
  <img src="autumn-face.jpg" alt class="first">
  …
  <img src="autumn-face.jpg" alt class="first">
  <img src="julia.jpg" alt class="second">
  <img src="julia.jpg" alt class="second">
  …
  <img src="julia.jpg" alt class="second">
</figure>

En réalité, seules deux images seront chargées par le navigateur, qui s’occupe ensuite de gérer les copies. Toutes les images ont exactement la même taille, et la même position absolue. L’élément <figure> occupera 100% de la largeur de son conteneur, avec la bonne hauteur obtenue grâce à la même astuce que celle utilisée pour la vidéo responsive :

#blinds {
  margin: 0; position: relative; padding-bottom: 56.5%;
  perspective: 1800px; height: transform-style: preserve-3d;
  max-width: 1000px;
}
#blinds img { top: 0; left: 0; position: absolute; transition: 1s; }

Chaque copie de la deuxième image est pivotée à 180 degrés et placée derrière les autres en utilisant une infime valeur de translate :

#blinds img.first { transform: rotateY(0deg); }
#blinds img.second { transform: rotateY(-180deg) translateZ(1px); }

Pour créer le découpage, j’utilise clip, associé au sélecteur nth-child. Comme vous pouvez le voir, les valeurs de gauche et de droite du clip sont augmentées de 100px à chaque itération :

#blinds img:nth-child(1), #blinds img:nth-child(11) {
    clip: rect(0px, 100px, 840px, 0px); }
#blinds img:nth-child(2), #blinds img:nth-child(12) {
    clip: rect(0px, 200px, 840px, 100px); }
#blinds img:nth-child(3), #blinds img:nth-child(13) {
    clip: rect(0px, 300px, 840px, 200px); }
…
#blinds img:nth-child(10n) {
    clip: rect(0px, 1000px, 840px, 900px); }

Même après qu’on ait utilisé clip, le transform-origin de l’image — c’est à dire le point à partir duquel l’image sera transformée — demeure le centre de l’élément. Pour remédier à cela, on règle le transform-origin pour chaque lamelle et on le fixe à une valeur x située au milieu de la lamelle. On peut le voir comme un axe central autour duquel pivote la lamelle :

image
La position logique de transform-origin pour les images découpées du “store vénitien”.

En CSS :

#blinds img:nth-child(1), #blinds img:nth-child(11) {
    clip: rect(0px, 100px, 840px, 0px);
    transform-origin: 50px 0px; }
#blinds img:nth-child(2), #blinds img:nth-child(12) {
  clip: rect(0px, 200px, 840px, 100px);
  transform-origin: 150px 0px; }
#blinds img:nth-child(3), #blinds img:nth-child(13) {
  clip: rect(0px, 300px, 840px, 200px);
  transform-origin: 250px 0px; }
…
#blinds img:nth-child(10n) {
  clip: rect(0px, 1000px, 840px, 900px);
  transform-origin: 950px 0px; }

Puis nous pouvons pivoter nos deux images de 180 degrés au :hover, chacune selon sa classe :

#blinds:hover img.first { transform: rotateY(180deg); }
#blinds:hover img.second { transform: rotateY(0deg) translateZ(1px); }

Tel quel, tous les stores vont pivoter d’un seul coup. Pour créer un effet de “vague”, nous ajoutons un retard sur la transition de chaque lamelle :

#blinds img:nth-child(1), #blinds img:nth-child(11) {
  clip: rect(0px, 100px, 840px, 0px);
  transform-origin: 50px 0px; }
#blinds img:nth-child(2), #blinds img:nth-child(12) {
  clip: rect(0px, 200px, 840px, 100px);
  transform-origin: 150px 0px;
  transition-delay: 100ms; }
#blinds img:nth-child(3), #blinds img:nth-child(13) {
  clip: rect(0px, 300px, 840px, 200px);
  transform-origin: 250px 0px;
  transition-delay: 200ms; }
…
#blinds img:nth-child(10n) {
  clip: rect(0px, 1000px, 840px, 900px);
  transform-origin: 950px 0px;
  transition-delay: 900ms; }

L’un des avantages d’utiliser clip est que le résultat est automatiquement responsif : si la taille de l’image se réduit le nombre de lamelles est également réduit. Essayez de réduire la fenêtre de votre navigateur : si le viewport a une largeur de 500px, les images animées fonctionnent comme s’il y avait cinq lamelles.

Approches alternatives

La nature répétitive du code appelle une approche plus efficace : soit un préprocesseur, soit JavaScript pour créer automatiquement le CSS. Je m’occuperai de cela prochainement. Pour l’instant, disons que c’est un bon début, même s’il n’est pas parfait : en raison de problèmes de rendu navigateur, il pourra arriver que des images soient légèrement superposées pendant un bref instant lors de la transition.


Intéressé par CSS ? Sur la Cascade, retrouvez des articles et ressources.


original paru le dans le blog de Dudley Storey, Demosthenes.info.

Sur l’auteur : est enseignant, auteur, artisan et cycliste, il vit à Calgary, dans l’État de l’Alberta au Canada. Passionné par le web et le développement (mobile) qu'il enseigne depuis 15 ans, il est l’auteur de Pro CSS3 Animation, et publie régulièrement dans son blog demosthenes.info des articles sur HTML, CSS et SVG. On peut le suivre sur Twitter ou Google+.

Traduit avec l’aimable autorisation de l’auteur.
Copyright Dudley Storey © 2014.