Sprites CSS : Meurs, découpe d’images, meurs !

Par Dave Shea

A l'époque où les jeux vidéos étaient encore amusants (aux heures de gloire du 8-bits), les graphismes étaient par nécessité beaucoup plus simples. Les arrière-plans et les personnages en 2 dimensions étaient dessinés individuellement, point par point, à la façon du pixel-art aujourd'hui renaissant. Des centaines, et plus tard des milliers de petites images appelées « sprites » formaient les briques de la construction visuelle d'un jeu.

Exemple de sprites

La complexité des jeux augmentant, des techniques furent développées pour gérer cette multitude de sprites tout en conservant la fluidité des jeux. L'une d'entre elles rassembla l'ensemble des ces petites images sur une grille maîtresse, images ensuite récupérées en cas de besoin par un code qui consignait les positions de chaque graphique individuel, et qui les affichait sélectivement à l'écran.

Mais qu'est-ce que tout ceci a à voir avec le web ?

Tout ce qui est ancien trouve un jour ou l'autre un nouveau souffle, et tandis que l'essor des jeux en 3D rendait les grilles de sprites obsolètes, l'émergence simultanée des appareils mobiles, offrant des capacités de jeu en 2D, les a remises en vogue. Avec un peu de mathématiques et beaucoup de CSS, nous pouvons maintenant en reprendre le concept de base pour l'appliquer au monde de la conception graphique sur le web.

Plus précisément, nous allons remplacer le découpage d'images traditionnel (et le Javascript qui lui est inévitablement associé) par une solution à base de CSS. Grâce à la façon dont les CSS fonctionnent, nous pourrons même aller plus loin : en construisant une grille d'images et en imaginant une façon d'en récupérer individuellement chacune des cellules, nous pourrons stocker tous les boutons, les éléments de navigation, etc. dans un seul fichier image, y compris les différents états (initiaux et survolés) associés aux liens.

Comment fonctionnent les sprites CSS ?

En fait, CSS contient tous les outils dont nous avons besoin, moyennant un peu de réflexion créative.

Commençons avec l'image maîtresse elle-même. En divisant le rectangle en quatre parties, vous remarquerez sur cette image que nos images initiales sont disposées en haut, avec leur état survolé :hover correspondant juste en dessous. Il n'y a pas pour l'instant de division claire entre les quatre liens. Imaginez donc que chaque groupe de mots est un lien. (Pour une question de simplicité, nous continuerons tout au long de l'article à nous référer aux images de lien comme étant les images initiales et aux états :hover comme étant les « états survolés ». Cette méthode peut être étendue aux états :active, :focus et :visited, mais nous ne nous y attarderons pas ici.)

Ceux d'entre vous qui sont familiers avec la technique de rollovers de Petr Stanicek (Pixy) voient peut-être déjà où nous voulons aller. Cet article doit beaucoup à l'exemple de Pixy pour la fonction de base sur laquelle nous allons nous reposer.

D'abord, un peu de HTML. Tous les bonnes astuces CSS essaient d'ajouter une couche de visuels sur un bloc de code propre, et cette technique n'y fait pas exception :

<ul id="skyline">
	<li id="panel1b"><a href="/traduction/sprites#1"></a></li>

	<li id="panel2b"><a href="/traduction/sprites#2"></a></li>
	<li id="panel3b"><a href="/traduction/sprites#3"></a></li>
	<li id="panel4b"><a href="/traduction/sprites#4"></a></li>

</ul>

Ce code servira de base pour notre exemple. La mode est aux balisages légers, simples, qui se dégradent élégamment dans les vieux navigateurs et dans ceux dans lesquels les CSS ont été désactivées, ce qui est une bonne tendance pour l'industrie. C'est un idéal vers lequel il est intelligent de se diriger. (Nous ignorerons pour l'instant tout texte dans les liens. Appliquez par la suite votre technique de remplacement favorite pour masquer le texte que vous ajouterez au final.)

Appliquer les CSS

Avec ces blocs de base, il est temps de construire la CSS. Une petite note avant de commencer - à cause d'un petit défaut d'Internet Explorer, nous allons être obligé de placer l'image de l'état survolé par dessus l'image initiale quand nous en aurons besoin, plutôt que de remplacer l'une par l'autre. Visuellement, il n'y a pas de réelle différence tant que nous les positionnons précisément, mais cette méthode évite l'ennuyeux clignotement qui apparaîtrait sinon dans IE.

#skyline {
   width: 400px;
   height: 200px;
   background: url(test-3.jpg);
   margin: 10px auto;
   padding: 0;
   position: relative;
}
#skyline li {
   margin: 0;
   padding: 0;
   list-style: none;
   position: absolute;
   top: 0;
}
#skyline li, #skyline a {
   height: 200px;
   display: block;
}

De façon assez contre-intuitive, nous n'allons pas du tout affecter l'image initiale aux liens, mais plutôt à l'élément <ul>. Vous verrez pourquoi dans un instant.

Le reste de la CSS de l'exemple ci-dessus définit des choses comme les dimensions du bloc #skyline et des items de liste, commence le positionnement de ces derniers, et désactive les puces indésirables.

Nous allons laisser les liens comme des blocs vides et transparents (mais avec malgré tout des dimensions bien précises) pour pouvoir déclencher leur action (c'est-à-dire suivre ledit lien), et nous allons les positionner grâce aux <li> qui les contiennent. Si nous commencions à positionner les liens eux-mêmes en ignorant de fait les <li>, nous trouverions vite des erreurs dans des navigateurs plus anciens, autant donc l'éviter.

Positionner les liens

Les <li> sont positionnées de façon absolue, mais pourquoi ne se trouvent-ils pas en haut de la fenêtre du navigateur ? Une propriété bizarre mais utile des éléments positionnés est que tous les éléments enfants qu'ils contiennent basent leur position absolue, non pas sur les coins de la fenêtre du navigateur, mais sur les coins de l'élément parent positionné le plus proche. Résultat, puisque que nous avons appliqué position:relative sur #skyline, nous sommes capables de positionner en absolu les <li> par rapport au coin supérieur gauche de #skyline lui-même.

#panel1b { left: 0;     width: 95px;  }
#panel2b { left: 96px;  width: 75px;  }
#panel3b { left: 172px; width: 110px; }
#panel4b { left: 283px; width: 117px; }

Ainsi, #panel1 n'est pas horizontalement positionné du tout. #panel2 est positionné à 96px à droite du bord gauche de #skyline, etc. Nous avons attribué aux liens une valeur display:block et la même hauteur qu'aux <li> dans le listing précédent, afin qu'ils remplissent complètement les <li> qui les contiennent, ce qui est exactement ce que nous souhaitons.

À cette étape nous avons une zone réactive basique, sans état :hover. Il est probablement plus simple de voir ce qui se passe en affichant les bordures.

Survols

Dans le passé nous aurions appliqué du Javascript pour changer l'image de l'état survolé. Au lieu de celà, nous avons groupé ces états dans une seule image. Tout ce dont nous avons donc besoin, c'est d'une méthode pour extraire sélectivement chaque état pour le lien approprié.

Si nous appliquons l'image maîtresse à l'état :hover sans aucune valeur additionnelle, nous rendons uniquement le coin supérieur gauche visible - ce n'est pas vraiment ce que nous voulons - plutôt que le morceau correspondant à la zone du lien (c'est ce que nous voulons). Nous avons donc besoin de modifier la position de l'image d'une façon ou d'une autre.

Nous travaillons avec des valeurs connues, en pixels. Un soupçon de mathématiques devrait donc nous permettre de décaler suffisamment cette image d'arrière-plan, tant verticalement qu'horizontalement, de manière à ce que seul le morceau contenant l'état survolé soit affiché.

C'est exactement ce que nous faisons ici :

#panel1b a:hover {
   background: transparent url(test-3.jpg)
               0 -200px no-repeat;
}
#panel2b a:hover {
   background: transparent url(test-3.jpg)
               -96px -200px no-repeat;
}
#panel3b a:hover {
   background: transparent url(test-3.jpg)
               -172px -200px no-repeat;
}
#panel4b a:hover {
   background: transparent url(test-3.jpg)
               -283px -200px no-repeat;
}

D'où sortons-nous ces valeurs ? Voici à quoi elles correspondent : la première valeur représente bien sûr le décalage horizontal (depuis le bord gauche), et la seconde le décalage vertical.

Toutes les valeurs verticales sont les mêmes. Comme l'image maîtresse est haute de 400 pixels et que les états survolés se situent dans la moitié inférieure, nous avons simplement divisé la hauteur par 2. Déplacer tout l'arrière-plan de 200 pixels vers le haut nous oblige à appliquer la valeur comme un nombre négatif. Pensez au bord du haut comme À un point de départ, ou 0. Pour positionner l'image d'arrière-plan 200 pixels au dessus de ce point, il paraît sensé de déplacer le point de départ de -200px.

De la même façon, si le bord gauche de chaque lien est à 0, nous devons décaler horizontalement l'image d'arrière-plan de la largeur de tous les <li> précédant celui avec lequel nous sommes en train de travailler. Le premier lien ne nécessite pas de décalage, puisqu'il n'a aucun pixel à sa gauche. Le second lien nécessite un décalage de la largeur du premier ; le troisième, de la largeur combinée des deux premiers liens ; le dernier, de la somme des largeurs des trois autres liens.

Expliquer le processus est un peu lourd, mais jouer avec les valeurs va rapidement vous montrer comment ce décalage fonctionne. Une fois que le système vous sera familier, celui-ci ne présentera plus aucune difficulté.

Voici ce que ça donne : des rollovers CSS utilisant une seule image, et qui se dégradent en une simple liste non-ordonnée.

Boutons

Il n'y a aucune raison particulière à laisser les liens en contact, côte à côte, comme ils l'étaient dans l'exemple précédent. Les zones réactives peuvent être pratiques dans certains cas, mais que se passe-t-il si nous voulons séparer chaque lien dans son propre bouton isolé ? Nous pourrions ainsi ajouter des bordures et des marges, laisser l'arrière-plan sous-jacent s'afficher à travers, et, de façon générale, les traiter séparément, selon nos besoins.

En fait, les briques de base sont déjà en place. Nous n'avons pas besoin de modifier notre code de façon trop radicale : le changement principal réside dans la création d'une nouvelle image d'arrière-plan qui ne continue pas de lien en lien comme dans le dernier exemple ci-dessus. A partir du moment où l'on ne peut plus compter sur l'élément <ul> pour placer l'image d'arrière-plan originale, nous allons plutôt l'appliquer sur chaque <li>, et la décaler de la même manière que nous décalions l'état survolé dans l'exemple précédent.

Avec une image appropriée et un peu d'espacement entre chaque <li>, nous avons des boutons.

Remarquez que dans cet exemple nous avons ajouté une bordure de 1 pixel, qui contribue bien sûr à la largeur finale du lien. Ceci affecte nos valeurs de décalage, et nous avons compensé en ajoutant 2px aux déplacements de l'arrière-plan aux endroits appropriés.

Les formes irrégulières

Jusqu'à présent nous nous sommes concentrés uniquement sur des formes rectangulaires, qui ne se chevauchent pas. Mais quid des image maps bien plus complexes, que des logiciels comme Fireworks ou ImageReady exportent si facilement ? Détendez-vous, nous avons aussi prévu le cas.

Nous allons commencer de la même façon que pour le premier exemple, en appliquant l'image d'arrière-plan sur l'élément <ul>, en masquant les puces des items de liste, en définissant des largeurs, et ainsi de suite. La grosse différence est l'endroit où nous positionnons les <li>, le but étant d'entourer chaque élément graphique par une boite qui le serre au plus près.

À nouveau, grâce à la capacité de positionner de façon absolue relativement au coin supérieur gauche de l'élément <ul>, nous sommes capable de placer précisément nos liens là où nous les voulons. Tout ce qu'il reste à faire maintenant est de mettre en place les états :hover.

Le plus embêtant dans ce cas est qu'un seul jeu d'images "avant" et "après" ne suffit pas. A cause des objet qui se chevauchent, se reposer sur une seule image survolée afficherait des morceaux survolés des objets entourant le nôtre. En fait, il montrerait exactement les morceaux qui sont à l'intérieur des limites du lien. (Le plus simple est encore de le voir en action.)

Comment éviter cela ? En ajoutant un second état survolé, et en choisissant soigneusement où vont se placer les différents objets. L'image maîtresse dans ce cas a séparé les éléments mauve et bleu sur le premier état survolé, et les verts, les oranges et les jaunes sur le deuxième. Cet arrangement permet à chaque état survolé d'être affiché correctement, sans inclure de morceaux des éléments adjacents. L'illusion est complète.

Avantages et inconvénients

Quelques réflexions pour conclure. Notre nouvelle méthode des sprites CSS a été testée avec succès dans la plupart des navigateurs modernes, l'insigne exception étant Opera 6, qui n'applique pas d'image d'arrière-plan aux états :hover des liens. Pourquoi, nous n'en sommes pas sûrs, mais çela signifie que nos rollovers n'y fonctionnent pas. Les liens fonctionnent toujours, et s'ils ont été étiquetés correctement, le résultat est une zone réactive statique mais utilisable dans Opera 6. Nous y survivrons, surtout qu'Opera 7 est disponible depuis un bout de temps.

L'autre souci est familier à tous ceux qui ont déjà passé un peu de temps sur le remplacement d'images . Dans les rares cas où les utilisateurs ont désactivé les images dans leurs navigateurs mais pas les CSS, un grand vide apparait dans la page, là où nous voulions que nos images soient placées. Les liens sont toujours présents et cliquables, mais rien n'apparaît visuellement. Au moment de la rédaction de cet article, il n'y avait pas de parade connue.

Ensuite il y a la taille du fichier. La tendance habituelle est de supposer qu'une image complète de taille double doit être plus lourde que son équivalent en petites images découpées, car l'aire totale de l'image sera généralement plus grande. Cependant, tous les formats d'images ont une certaine charge opérationnelle (qui explique pourquoi une GIF transparente de 1px sur 1px prend environ 50 octets), et plus vous avez de pièces, plus les charges opérationnelles s'additionnent rapidement. De plus, une image maîtresse de type GIF requiert seulement une table de couleurs, tandis que chaque pièce nécessiterait la sienne. Des tests préliminaires suggèrent une taille totale inférieure pour les sprites CSS, ou, au moins, pas significativement plus importante.

Finalement, n'oublions pas que notre balisage est agréable et propre, avec tous les avantages que cela entraîne. Les listes HTML se dégradent merveilleusement, et un bon mécanisme de remplacement par image laissera le texte des liens accessible aux lecteurs d'écran. Remplacer les graphiques du sprite est simplissime, puisque toutes nos dimensions et nos décalages sont contrôlés par un seul fichier CSS, et que la totalité de l'imagerie est contenue dans une seule image.


Translated with the permission of A List Apart and the author[s].

Fiche technique

À propos de l'auteur

Créateur du populaire CSS Zen Garden, Dave Shea est un graphiste qui pense que ce truc, là, CSS, ça pourrait aller loin, quand même. Dave écrit quotidiennenment sur MezzoBlue, et vient de lancer sa propre agence, Bright Creative.

Articles similaires

Voici la liste des dix articles les plus récents en rapport avec cet article :

Intermédiaire

CSS

© 2001-2014 Pompage Magazine et les auteurs originaux - Hébergé chez Nursit.com ! - RSS / ATOM - About this site