Parallaxe sur background et sprite avec un plugin jQuery
Ce plugin a été développé il y a plusieurs années. Il ne gère pas les problématiques actuelles liées au responsive, je ne recommande donc pas son utilisation en production.
Mais il peut toujours vous apprendre quelque chose en JavaScript…
Récemment confronté à la mise en place du fameux « Parallax effect », j’ai été surpris de constater que malgré la masse de plugins existants, aucun ne correspondait réellement à mes besoins. Je me suis donc lancé dans le développement de mon propre plugin, que je partage dans cet article.
Démos
En démo intégrée au corps de la page, ça donne ça :
Et en combinant le plugin avec les techniques vu précédemment (Single-page layout et Smooth scroll), on peut arriver à faire quelque chose comme ça :
Caractéristiques
- Pas de contrainte de structure, le plugin s’adapte à n’importe quel type de gabarit (et non l’inverse),
- L’effet parallaxe peut s’appliquer sur un background, mais également sur n’importe quel type d’élément (que j’appellerai « sprite » par la suite),
- Au chargement de la page (scrollée ou non, cas d’un lien vers une ancre par exemple), les éléments en parallaxe s’affichent au bon endroit (en prenant en compte les coefficients de déplacement),
- Pas de positionnement hasardeux, on peut définir proprement les positions initiales des éléments (horizontalement et verticalement),
- Léger et performant, 1ko dans sa version minifiée et une gestion intelligente des déplacements (on ne bouge que ce qui se trouve dans le champ de vision).
Fonctionnement
La parallaxe est l’effet du changement de position de l’observateur sur ce qu’il perçoitWikipédia, Parallaxe
Appliqué au web, le changement de position de l’observateur correspond au défilement de la page, et donc au mouvement de la scroll du navigateur (événement .scroll()
). L’effet repose donc sur la modification du positionnement d’un élément en fonction de la distance qui a été parcourue ($(window).scrollTop()
) lors de la navigation. Un coefficient de déplacement (paramètre coeff
) permettra de réduire ou d’amplifier le mouvement de l’objet.
Pour modifier la position d’un élément, en théorie il suffit de jouer avec ses valeurs CSS (.css()
ou .animate()
). Malheureusement, l’exécution de l’événement .scroll()
varie selon le navigateur lors d’un défilement avec molette, et le rendu devient affreux sur les browsers dont le défilement n’est pas smooth (page de test ici, à comparer entre Firefox et Chrome).
Pour remédier au problème, j’ai identifié deux solutions :
- Soit on utilise un script pour normaliser le scroll molette (comme le plugin Mousewheel),
- Soit on « fixe » les éléments avec du
position:fixed
ou dubackground-attachment:fixed
Le plus simple (et léger) est donc d’utiliser la méthode fixée, et plus précisément celle du background-attachment:fixed
. Mais voilà, un arrière-plan fixé le sera toujours par rapport au viewport, jamais par rapport à son conteneur. Et comme un élément peut être positionné librement, on va devoir adapter la position de son background pour qu’elle coïncide avec la position de l’objet (par exemple, si l’élément est positionné à 100px de l’extrémité gauche du viewport, la position en X du background doit être décalée de la même distance).
Et pour le décalage vertical de l’arrière-plan lors du scroll ? Pas de souci pour un élément de type « background », vu qu’il est censé occuper toute la surface du conteneur, mais pour un « sprite » dont la taille et la position peuvent être définies arbitrairement ? Et bien l’astuce consiste à créer un « couloir » qui occupe toute la hauteur du conteneur, et d’y faire « glisser » l’arrière-plan lors du scroll. Et pour positionner initialement le background dans son couloir, on récupère ses coordonnées (grâce à .offset()
) que l’on va reporter à la position de son background. Cela correspond à cette portion (simplifiée) de code utilisée par le plugin :
var offset = $(this).offset();
$(this).css({
backgroundPosition : offset.left + 'px ' + offset.top + 'px',
top : 0,
bottom : 0,
height : 'auto'
});
Et pour finir, voyons l’algo permettant de définir si un élément est dans le champ de vision ou non. Par raisonnement logique, un élément est visible si :
- Son point le plus bas est au dessus du point le plus haut du screen,
- Son point le plus haut est en dessous du point le plus bas du screen.
Ce qui nous donne :
var offset = $(this).offset; // Coordonnées élément
var windowTop = $(window).scrollTop(); // Distance haut site / début screen
var topElt = offset.top;
var btmElt = offset.top + $(this).height(); // Car pas d'offset.bottom
var topScreen = windowTop;
var btmScreen = windowTop + $(window).height();
if ( btmElt > topScreen && topElt > btmScreen ) {
// Visible
}
Pour les autres subtilités de code, vous pouvez jeter un coup d’œil à la version non minifée et commentée du plugin.
Installation / Configuration
Comme précisé plus haut, il n’y a aucune contrainte concernant la mise en page des éléments (de même pour la dénomination des classes / id). On implémente donc notre structure sans se préoccuper du plugin :
(le code suivant est celui utilisé pour mettre en place la première démo)
<div id="parallaxWrapper">
<div class="pxBackground"></div>
<div class="pxSprite"></div>
</div>
#parallaxWrapper {
position:relative; /* Référence aux enfants en absolute */
width:610px; height:350px; }
#parallaxWrapper [class*="px"] { /* Cible tous les enfants contenant la chaine "px" */
visibility:hidden; /* Plugin feature ! (voir les explications) */
position:absolute; }
#parallaxWrapper .pxBackground {
top:0; left:0; bottom:0; right:0; /* Alternative à top:0; left:0; width:100%; height:100%; */
background:#070707 url(background.jpg) no-repeat; }
#parallaxWrapper .pxSprite {
top:-100px; right:100px; /* Position initiale de l’élément */
width:200px; height:200px;
background:url(sprite.png) no-repeat; }
Deux features à prendre en compte :
- On peut utiliser
position:fixed
pour avoir un élément en permanence dans le champ de vision (exemple sur la deuxième démo). - En utilisant
visibility:hidden
on laisse le plugin positionner correctement l’élément avant de l’afficher. Inconvénient : si vous avez un problème de JS les éléments resteront invisibles. Cela peut s’avérer problématique si l’élément sert de conteneur (d’où le choix d’une structure avec deux éléments distincts, et pas le « sprite » en fils unique et le « background » en conteneur principal).
C’est le moment de récupérer le script :
On ajoute le JS nécessaire à son utilisation (dans <head>
ou avant </body>
) :
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="jquery.parallax.min.js"></script>
<script>
$(document).ready(function() {
$('.pxBackground').parallax();
$('.pxSprite').parallax({
'coeff' : -0.4,
'type' : 'sprite'
});
});
</script>
coeff
: Coefficient de déplacement (par défaut0
)1
fera descendre l’élément d’une hauteur équivalente à celle d’un scroll,0
donnera à l’élément un rendu de type « fixed »,-1
fera remonter l’élément d’un pas de scroll.
type
: Type de l’élément (par défautbackground
)background
: Élément dont la position initiale de l’arrière-plan est alignée avec l’extrémité haute du conteneur (l’élément peut être un conteneur lui-même). Un alignement horizontal peut être défini (on peut par exemple centrer l’image si sa largeur est supérieure à celle du conteneur avecbackground-position:center 0
).sprite
: Élément libre. Attention si vous utilisezbackground-position
(cas d’un sprite CSS), la valeur doit être donnée en pixel et les éléments du sprite doivent être alignés sur l’axe horizontal (images les unes à côté des autres, et pas en dessous). La raison est donnée dans la rubrique #Fonctionnement (mise en place du « couloir »).
« That’s all folks ! »
Changelog
-
v1.1, 19/12/13 : Debug IE<10
.css('backgroundPosition')
renvoitundefined
sur IE<9, fixé avec condition +.css('backgroundPositionX')
.- Déplacement de
.css('background-attachment', 'fixed')
après première utilisation de.css('backgroundPosition')
pour éviter qu'IE9 ne renvoit0% 0%
.
Commentaires (3)
Lâcher un com'
Je te remercie mille fois je cherchais comment faire mais je galérais. Donc encore merci :)
Bonjour. J’ai quelque connaissance sur l’utilisation de JQuery grâce aux tutoriels sur http://www.alphorm.com/tutoriel/formation-en-ligne-jquery et je dois dire que votre article m’intéresse beaucoup.
@floflo / @Monier :
Merci du retour. Je me suis récemment interrogé sur la légitimité de cet article, vu que le plugin n’est pas responsive, mais content de constater qu’il suscite malgré tout un peu d’intérêt. :)
Laisser un commentaire