Retour des tutoriels CSS avec quelque chose d’un peu original pour cet article : un effet parallaxe uniquement en CSS3.
C’est à la suite de l’article de Simon Kern sur Alsacréations que m’est venue l’envie de tenter d’utiliser CSS pour reproduire cet effet initialement conçu avec JavaScript.

L’article zoom sur l’effet parallaxe de Simon est bien conçu, je vous invite à le lire si vous préférez l’utilisation de jQuery, ou si vous souhaitez découvrir une alternative ou un complément à ce tutoriel.
J’en profite pour remercier Simon qui m’a autorisé à reprendre son design ainsi que la base du code qu’il a conçu pour l’article sur Alsacréations.

Démonstration

Place à l’explication !

Concept

Pour réaliser cet effet il nous faut plusieurs éléments qui vont nous permettre de simuler différents plans. Ces plans vont se mouvoir dans le même sens mais à vitesses différées afin de donner l’impression que certains sont en premier plan (mouvements rapides) d’autres sont en arrières plan (mouvements plus lents).

Lorsque un tel effet est mis en place sur un site web, il l’est souvent pour offrir une transition originale entre deux vues, un peut comme lors d’un diaporama pour passer d’une slide à l’autre.

Schématisation

Nous utiliserons trois plans pour réaliser cet effet : un fond imagé en CSS (dernier plan), notre contenu qui se trouve naturellement dans le flot du document (second plan), et des éléments placés ci et là (premier plan) pour ajouter de la profondeur.

L'encadré gris représente un slide, le fond gris une image de fond, le bloc jaune un bloc de contenu, et le personnage blanc un élément de premier plan

Le principe de l’animation étant établi, il nous faut également garder en tête que CSS ne permet pas de positionner les éléments en fonction de la position du contenu dans la page, ou en fonction de la position de la souris.
Nous allons donc utiliser la pseudo classe target afin de déterminer à quelle slide nous nous trouvons, et effectuer les animations en conséquence.

Base HTML

Voici un code allégé qui reprend le contenu du document de démonstration. (je n’ai repris que le contenu de body)

<!-- éléments cachés pour jouer avec :target -->
<span id="s1"></span>
<span id="s2"></span>
<span id="s3"></span>
 
<!-- Le gros conteneur qui va glisser -->
<div id="wrap">
 
	<!-- La navigation -->
	<ul id="nav">
		<li><a href="#s1">Splash</a></li>
		<li><a href="#s2">Lorem Schnapsum</a></li>
		<li><a href="#s3">Credits</a></li>
	</ul>
 
	<!-- La slide 1 -->
	<div id="slide1">
		<div class="slide_inside">
			<!-- Le logo -->
			<h1><img src="images/logo-stop-wars.png" alt="Stop Wars"></h1>
		</div> <!-- /.slide_inside-->	 	
	</div> <!-- /#slide1 -->
 
	<!-- La slide 2 -->
	<div id="slide2">
		<div class="slide_inside">
			<!-- Les deux éléments en 1er plan -->
			<!-- Mettez ici les images que vous voulez 🙂 -->
			<img src="images/bomb-squad-trooper.png" id="trooper" alt="">
			<img src="images/clone.png" id="clone" alt="">
 
			<h2>Star Wars Parallax</h2>
			<p>Lorem Salu bissame !</p>
		</div> <!-- /.slide_inside-->	     	
	</div> <!-- /#slide2 -->
 	
	<!-- La slide 2 -->
	<div id="slide3">
		<div class="slide_inside">			
			<h2>Credits</h2>		
			[…]
		</div> <!-- /.slide_inside-->	    
	</div> <!--  /#slide3 -->
</div>

Pour reprendre la structure de manière schématique, nous avons à peu près cela :

Trois zones, chacune d'elle possède un &quote;.slide_inside&quote;

Nous aurions pu utiliser HTML5 sans aucun problème, le principe est le même.
Maintenant nous devons passer au CSS.

Beaucoup de styles

Dimensionnons nos conteneurs et plaçons nos images de fond. Le code suivant est commenté pour vous aider à le comprendre.

body {
	margin: 0; padding: 0; /* annulation des marges */
	overflow: hidden; /* on cache le débordement de contenu */
	color: #fff;
	background: #000;
}
 
#wrap {
	/* position du conteneur pour le préparer à bouger */
	position: relative;
	top: 0;
	/* on prépare la transition à venir */
	transition: top 1.4s cubic-bezier(.49,.22,.52,1.35);
}
 
#nav {
	/* on fixe le menu en haut */
	position: fixed;
	top: 0; left: 0; right: 0;
	padding: 1em 0;
	margin: 0;
	/* on centre les liens */
	text-align: center;
	background: #000;
}
#nav li {
	/* on aligne les éléments de liste */
	display: inline;
	margin:0;
}
#nav a {
	/* on applique des marges aux liens */
	display: inline-block;
	margin: 0 3em;
	color: #eee;
	/* on applique une petite lueur */
	text-shadow: 0 0 5px #fff;
	/* on prévoit une petite transition (lueur) */
	transition: all 1s;
}

Ça c’était pour l’aspect du cadre, nous allons mettre en styles les différents slides et leur attribuer une image de fond tout en prévoyant les transitions.
Nous allons en profiter pour styler les contenus.

/* slides */
#slide1, #slide2, #slide3 {
	/* height à adapter avec media queries */
	height: 1000px;
	padding-top:100px;
	/* préparation des transitions */
	transition: background-position 1.4s  cubic-bezier(.49,.22,.52,1.35);
}
/* attribution des images de fond */
#slide1 { background:url(images/slide1-bg.jpg) center 0 no-repeat fixed; }
#slide2 { background: url(images/slide2-bg.jpg) center 0 no-repeat fixed; }
#slide3 { background: url(images/slide3-bg.jpg) center 0 no-repeat fixed; }
 
/* Contenus des slides */
.slide_inside {
	width: 770px;
	margin: 0 auto;
	position: relative;
	background-color: rgba(0, 0, 0, .6);
	padding: 50px;
}

#slide1 .slide_inside {
	text-align: center;
	background-color: transparent;
}
#slide2 .slide_inside p {
	width: 500px;
	text-align: justify;
}
#slide3 .slide_inside {
	margin-top: 50px;
}

Attention, Webkit semble ne pas supporter la valeur 1.35 de la fonction cubic-bezier(), il conviendra de corriger le tir en utilisant la syntaxe -webkit-transition: top 1.4s cubic-bezier(.49,.22,.52,1);

Les images de fond et les contenus sont prêts.
On va maintenant positionner et déformer (transform) nos petits personnages en prévoyant également leurs transitions.

/* positionnement des personnages animés */
#trooper, #clone {
	position: absolute;
	left:-180px; top: 300px;
	/* on tourne légèrement les images */
	transform: rotate(10deg);
	/* et on prévoit une transition */
	transition: all 1.4s cubic-bezier(.49,.22,.52,1);
}
/* positionnement différé d'un des 2 personnages */
#clone {
	left: -280px;
	top: 400px;
}

Voilà, tous nos éléments sont positionnés.
Maintenant il faut revoir le positionnement de tous ces éléments en fonction de l’élément ciblé lors d’un clic, c’est là que la pseudo classe target va jouer son rôle.

Animation en fonction de la cible

Le principe va être le même pour chaque élément ciblé par notre menu de navigation.
Je vais donc expliquer le code pour le second élément ciblé (lorsque nous cliquons sur le deuxième élément de menu).

Nous allons utiliser la méthode proposée par Vincent De Oliveira dans son article « Empêcher le scroll avec l’utilisation de target » afin d’éviter qu’un saut dans la page soit effectué lorsque l’on demande à rejoindre une ancre.

/* décalage du conteneur vers le haut */
#s2:target ~ #wrap {
	top:-1000px;
}
 
/* décalage des fonds de chaque slide */
#s2:target ~ #wrap #slide1 { background-position: 50% 36%; }
#s2:target ~ #wrap #slide2 { background-position: 50% 0%; }
#s2:target ~ #wrap #slide3 { background-position: 50% -30%; }
 
/* mouvements d'un des personnages */
#s2:target ~ #wrap #trooper { 
	left: -180px;
	top: -100px;
	transform: rotate(-7deg);
}
 
/* mouvements de l'autre personnage */
#s2:target ~ #wrap #clone {
	left: -350px;
	top: 50px;
	transform: rotate(0deg);
}
 
/* juste pour la déco (lueur sur le lien cliqué) */
#s2:target ~ #wrap #nav li:first-child + li a {
	text-shadow: 0 0 5px #eca603, 0 0 5px #eca603, 0 0 5px #eca603, 0 0 5px #eca603;
}

Pour traduire l’un des sélecteurs CSS avancés en français, on pourrait dire : « Lorsque #s2 est ciblé, je cherche un #wrap directement ou indirectement frère et je style l’élément #clone qui se trouve dedans » (interprétation du sélecteur de la ligne 19).

Le principe est le même lorsque l’on cible les autres slides.
Le plus dur pour vous maintenant, c’est de trouver quels changements appliquer, à quelle vitesse (distance/temps) et dans quelle condition.

Je vous donne le code CSS pour les deux slides restantes. (quand même !)

/* vers Slide 1 */
#s1:target ~ #wrap { top:0px; }
#s1:target ~ #wrap #nav li:first-child a { text-shadow: 0 0 5px #eca603, 0 0 5px #eca603, 0 0 5px #eca603, 0 0 5px #eca603; }
#s1:target ~ #wrap #slide1 { background-position: 50% 0%; }
#s1:target ~ #wrap #slide2 { background-position: 50% -35%; }
#s1:target ~ #wrap #slide3 { background-position: 50% -60%; }
#s1:target ~ #wrap #trooper { left: -180px; top: 300px; transform: rotate(10deg); }
#s1:target ~ #wrap #clone { left: -280px; top: 300px; transform: rotate(10deg); }
 
/* vers Slide 3 */
#s3:target ~ #wrap { top:-2150px; }
#s3:target ~ #wrap #nav li:last-child a { text-shadow: 0 0 5px #eca603, 0 0 5px #eca603, 0 0 5px #eca603, 0 0 5px #eca603; }
#s3:target ~ #wrap #slide1 { background-position: 50% 60%; }
#s3:target ~ #wrap #slide2 { background-position: 50% 35%; }
#s3:target ~ #wrap #slide3 { background-position: 50% 0%; }
#s3:target ~ #wrap #trooper { left:-1500px;top:-1000px; transform: rotate(-15deg); }
#s3:target ~ #wrap #clone { left:-1000px;top: -800px; transform: rotate(-15deg); }

Le code CSS complet se trouve ici : CSS de la démo

Vous aurez noté que les éléments ciblés sont nos span du tout début du code HTML. À partir de ce type de cible, nous pouvons parcourir tout le DOM afin de sélectionner l’élément à styler.

N’hésitez pas si vous avez des questions 😉

Démonstration