Vous êtes ici : Accueil du blogjQuery » Parallaxe sur background et sprite avec un plugin jQuery

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.

  1. Démos [#]
  2. Caractéristiques [#]
  3. Fonctionnement [#]
  4. Installation / Configuration [#]

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 :

Voir la démo

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çoit

Wikipé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 du background-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 :

Commenté (3ko)Minifié (1ko)

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éfaut 0)
    • 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éfaut background)
    • 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 avec background-position:center 0).
    • sprite : Élément libre. Attention si vous utilisez background-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') renvoit undefined 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 renvoit 0% 0%.

Commentaires (3)

Lâcher un com'

  1. floflo
    18 avril 2015 à 20h51

    Je te remercie mille fois je cherchais comment faire mais je galérais. Donc encore merci :)

  2. Monier
    01 juin 2016 à 11h23

    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.

  3. BeliG
    01 juin 2016 à 12h11

    @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

Balises HTML autorisées dans la rédaction du message :
<strong> <a> <code> <q>

Les champs marqués d'une étoile sont obligatoires.

Current month ye@r day *