I Haz A Bug

le blog de jye.

Images et CSS Sprites

Introduction

Tout est parti d’un article de Christian Fauré : Le cloud computing est nécessairement du green computing en réaction à un article du NY Times à propos de la consommation d’énergie produit par les sites Internet. Je ne m’attarderai pas là dessus, je vais surtout parler d’un système utilisé par des sites comme Google et Facebook pour, à mon avis et d’une manière générale, gagner en performance.

Cette technique s’appelle l’Images Sprites ou le CSS Sprites, au choix. Cette technique consiste à réduire le nombre d’images d’un site en incorporant celles-ci au sein d’une seule image. On utilise ensuite la feuille de style CSS pour afficher ce que l’on souhaite en fonction de la class de l’élément HTML. 

Démonstration

Imaginons que je souhaite afficher à un endroit un picto correspondant à un lien “Ajouter” sur mon site, et à un autre endroit un picto correspondant à un lien “Supprimer“. La première chose à faire est d’ouvrir votre éditeur d’images préféré et de réunir vos images en une seule, ce qui donne :

Les deux pictos réunis en une seule image

Dans mon exemple chaque picto fait 16 pixels par 16 pixels. Pour afficher la deuxième image, je devrai donc décaler mon offset de 16 pixels. Ce qui donne comme code CSS ceci :

a.ajouter, a.ajouter:hover{
    background:#fff url(path/to/icons.png) 0px 0px no-repeat;
    display:block;
    height:16px;
    width:16px;
    text-indent:-9999px;
    overflow:hidden;
} 

a.supprimer, a.supprimer:hover{
    background:#fff url(path/to/icons.png) -16px 0px no-repeat;
    display:block;
    height:16px;
    width:16px;
    text-indent:-9999px;
    overflow:hidden;
}

Je force la hauteur et la largeur à 16 pixels, et je décale de 16 pixels le background pour l’image delete. L’attribut overflow:hidden permet de ne pas avoir un mauvais affichage du surlignage et text-indent permet de déplacer le texte du lien hors de l’écran. Il faut ensuite dans votre code html, spécifier la class des éléments HTML auxquels vous voulez voir attribuer chaque picto :

<a class="ajouter" href="#ajouter">Ajouter</a>
<a class="supprimer" href="#supprimer">Supprimer</a>

Voilà le résultat (j’ai mis display:inline-table pour mon exemple) :

Ajouter | Supprimer

Heureusement pour vous, il existe un outil en ligne qui vous permet de ne pas avoir à produire tout ça à la main : CSS Sprite Generator. Pour l’utiliser, vous devez zipper toutes vos images et les lui fournir. Une fois que vous avez remplit correctement les informations d’offset, tailles et autres options CSS pour vos images, l’application web se chargera de produire votre image finale ainsi que la CSS qui correspond.

Explication

Exemple sur la page de résultats dun recherche Google

Unique image de la page de résultats d'une recherche.

Alors pourquoi faire ça ? Et bien, pour gagner en rapidité ! Lorsque vous affichez une page web, un nombre plus ou moins important de requêtes HTTP (demandes au serveur) sont effectuées afin de récupérer tout ce qui est nécessaire à l’affichage de la page. Une image représente une requête HTTP. Plus il y a de requêtes, plus le chargement de la page s’alourdit. Pour savoir comment réduire le nombre de requêtes HTTP, je vous conseille de lire (en anglais) cet article de Tenni Theurer publié sur le blog Yahoo! User Interface : “Performance Research, Part 1: What the 80/20 Rule Tells Us about Reducing HTTP Requests”. Voici un extrait :

Table 1 shows popular web sites spending between 5% and 38% of the time downloading the HTML document. The other 62% to 95% of the time is spent making HTTP requests to fetch all the components in that HTML document (i.e. images, scripts, and stylesheets). The impact of having many components in the page is exacerbated by the fact that browsers download only two or four components in parallel per hostname, depending on the HTTP version of the response and the users browser. Our experience shows that reducing the number of HTTP requests has the biggest impact on reducing response time and is often the easiest performance improvement to make.

table1.gif

Et quand on voit que Google ou Facebook, pour ne prendre que les deux sites probablement les plus visités sur Internet, utilisent cette technique, on se doute que cela leur permet d’économiser de précieux kilo octets de transfert (que ça soit en taille de requête, mais aussi en temps de latence ou en nombres de requêtes comme cité dans le paragraphe au-dessus). Et si vous souhaitez tester votre site et voir ce que vous pouvez optimiser, je vous conseille ce site, parmi d’autres : Web Page Analyzer.

Tags: , , , , ,

26 commentaires pour “Images et CSS Sprites”

  1. RemiB dit :

    Une technique sympa à utiliser, même hors du contexte “green”
    Petit bémol à ton argumentaire, le fait de charger l’image alors que certaines parties ne seront peut être pas affichées par l’utilisateur, décalage pour hover sur certains boutons par exemple, qui ne seront jamais survolés n’est ce pas un gachi de bp ? :)

  2. Blah dit :

    on se doute que cela leur permet d’économiser de précieux kilo octets de transfert

    Ca peut être le cas et parfois non (cf message de RemiB), dans tous les cas ça n’est absolument pas la raison pour laquelle ils font ça. Ce qui est impressionnant c’est que tu devrais le savoir puisque le paragraphe précédent explique la vraie raison.

  3. jye dit :

    Quand je parle de kilo octets de transfert, je ne parle pas du contenu de la requête HTTP mais de la requête HTTP en elle-même. Enfin j’avoue que le terme kilo octets n’est pas vraiment bien employé dans ce cas.

    Entre faire une requête HTTP contenant une grosse image, et 4 requêtes contenant 4 images, il y a des chances que le cumul des 4 images totalise le poid de la première, mais l’enveloppe HTTP est divisée par 4. Sans parler du gain une fois l’image stockée en cache, on interroge qu’une seule fois le serveur pour savoir si l’on doit invalider le cache. Je n’ai pas précisé qu’on gagnait aussi en temps de réponse vu qu’effectivement, c’est écrit juste au-dessus. Mais vu qu’apparemment je suis pas super clair, j’ai édité mon article pour que ça soit un peu plus clair. Merci pour la remarque en tout cas.

    Pour ce qui est de ta remarque RemiB, c’est à toi de voir ce que tu juges nécessaire d’incorporer dans ton sprite. Je pense que ce genre de site ne met pas en place cette pratique sans avoir tester s’il est nécessaire ou pas de l’appliquer et si un gain est effectivement apporté.

  4. RemiB dit :

    Je n’en doute pas, c’était juste mon coté 7ru3r3b3lz contre le “tout green”

  5. Wiz dit :

    @jye
    Effectuer une seule requête http au lieu de x a aussi pour effet d’accélerer dans le sens ou une seule requête est transmise, et l’on attend une seule réponse au lieu de x. (Enfin je suppose que tu l’avais compris, j’ai juste pas trouvé ça bien mit en évidence)
    Cela dit, je suis pas certain que la priorité soit le “green”, j’imagine qu’ils veulent simplement économiser les ressources consommées.

  6. LeGreg dit :

    Ahah c’est marrant.
    C’est effectivement assez utilisé en programmation jeux (3d ou 2d) mais on appelle ça texture atlas et pas css sprites (en fait le sprite en 2d c’est l’objet individuel et on met plusieurs sprites dans le texture atlas).

    Pour le côté green c’est un “angle”, mais on peut-être aussi très green en ayant le plus de pages statiques possibles (diminue la charge côté serveur par rapport à une instanciation de PHP) et en réduisant le nombre et la taille des images sur les pages les plus lues.

  7. merwan dit :

    Le Web Page Analyzer est très pratique, merci.

  8. __MaX__ dit :

    J’utilise cette technique depuis un bail principalement pour les rollover, qui provoquaient un lag si l’image était chargée au survol. Je vais continuer à améliorer ça selon tes conseils, c’est une bonne idée.

  9. Conradson dit :

    Par contre un sprite css ne peut pas être utilisé en background repeat, et pose parfois problème avec IE6 sur des blocks en position absolute avec des marges (même si tout pose problème avec IE6).

    Ça reste une très bonne technique d’optimisation de requête HTTP, et même niveau poids il est souvent moins lourd d’avoir une seule image, car les couleurs ne sont codées qu’une seule fois.

    Niveau optimisation je vous conseille l’excellent bouquin de Steve Souders, ‘Optimiser les performances de son site’, paru chez O’Reilly (même si l’éditeur a fermé, il doit rester possible de trouver le bouquin).

    Sinon l’extension YSlow pour Firefox permet de se faire une idée de ce qui est améliorable, et tous les aspects sont repris dans le bouquin que j’ai cité au-dessus.

    Sinon, ton exemple n’a-t-il pas un problème, car tu utilise du sprite css, mais tu utilise deux images différentes (add.png et delete.png), alors que tu devrais en utiliser une seule (du type icones.png).

  10. divide dit :

    Ca me fait penser à la difficulté technique que j’ai rencontré en changeant ma homepage pour afficher 5 flux rss/html différents: le temps de réponse de chaque serveur s’additionnant, la page met 2-3sec pour apparaitre.
    Pour contourner (partiellement) ça, j’ai mis en place une fonction qui rapatrie les différents flux en local (copy/paste) et ne les update que s’ils sont plus vieux de quelques minutes, ce qui me permet deja d’alleger grandement la charge. L’idéal serait que je puisse ne commander cette update qu’en fin de chargement de page (donc affichage immédiat de l’ancien flux, et update préparé pour le visiteur suivant), mais je n’ai pas réussi à lui faire exécuter ce code php après la fin du chargement html. Tu saurai pas comment faire ça ? (balancer tout le html au client pour qu’il puisse afficher la page, puis continuer a exécuter un bout de code php après sans ralentir l’affichage client)

  11. Conradson dit :

    @divide
    en appelant le script en Ajax, avec affichage des anciens flux pour la page en cours, mais en même temps rapatriage des nouveau flux par le script appelé par l’Ajax, ainsi ça ne bloquerait pas l’affichage de la page en cours, et ça préparerai les futur affichages.

    Tu peux aussi développer un cron qui appelle automatiquement le script à intervalle régulier, sans faire de vérification à chaque lancement de la page.

  12. jye dit :

    Conradson a dit :
    Sinon, ton exemple n’a-t-il pas un problème, car tu utilise du sprite css, mais tu utilise deux images différentes (add.png et delete.png), alors que tu devrais en utiliser une seule (du type icones.png).

    Effectivement, mon exemple de code contenait une énorme boulette, merci !

    divide a dit :
    Tu saurai pas comment faire ça ? (balancer tout le html au client pour qu’il puisse afficher la page, puis continuer a exécuter un bout de code php après sans ralentir l’affichage client)

    Ta solution, c’est faire de l’xmlHTTPRequest (de l’Ajax). Sache que le PHP, c’est un code exécuté par le serveur. Il est donc impossible d’exécuter du code PHP à la fin du chargement d’une page, car le chargement d’une page est effectué coté client.

  13. divide dit :

    Ben c’est bien pour ca que je pensais que ca ne poserai pas de problème, en envoyant tout le html dont le client a besoin, puis en continuant ma cuisine coté serveur. Mais ce con de client attend que j’ai finit l’execution de tout mon code php (même après la balise /body) avant d’afficher l’html…

    body
    phpaffichagefluxenhtml
    /body
    phpupdateflux

    en tout cas je vais me doc sur l’Ajax, thanks :]

  14. Pedro dit :

    Pas con, utiliser un vieux concept (sprites css) pour vendre “green”.
    Après les icones “xhtml 1.0″, “css 2.0″ et “w3c” sur les sites, maintenant le sticker “bio”. A quand une taxe éco citoyenne pour les intégrateurs n’utilisant pas des sprites ?

  15. jye dit :

    Pedro, je ne comprends pas pourquoi tu t’emballes comme ça, qui plus est, sur mon article qui ne traite pas du tout du débat “green”. Mon article parle d’une technique aidant l’optimisation d’un site.

  16. remouk dit :

    @divide: Jamais utilisé mais en PHP register_shutdown_function devrait te permettre d’exécuter du code une fois le script terminé (sauf si tu termines par un exit()).

  17. divide dit :

    ah super, exactement ce qu’il me fallait. Thanks :)

  18. Pedro dit :

    T’inquiètes pas jye, je m’emballe pas du tout :D D’ailleurs ton article est très bien, simple et efficace.
    Non, mon commentaire venait juste du fait que ça commence a me faire marrer de voir cette tendance a vouloir voir du vert partout, jusqu’à faire des études sur le taux de CO2 que génère une requête sur Google. Les sprites css existent depuis un bail, c’est dommage de mettre ça en parallèle avec l’article du NYT. A l’origine, ça a été utilisé dans un but d’optimisation d’un site, pas pour rejeter moins de CO2.

  19. D0h dit :

    Excellent, je cherchais justement comment optimiser mon projet pour la 3G, je vais tenter un essai de suite.

    Par contre, cela nécessite que toutes les images du site sois affiché via le css ?

    Et quid de la compatibilité IE6/IE7 ?

  20. Conradson dit :

    D0h dit :
    Et quid de la compatibilité IE6/IE7 ?

    Auto-quote :
    > Par contre un sprite css ne peut pas être utilisé en background repeat, et pose parfois problème avec IE6 sur des blocks en position absolute avec des marges (même si tout pose problème avec IE6).

    ____________

    Pour IE7, j’ai pas constaté de problème.

    En fait, tout dépend de l’utilisation que tu en fais, si tu n’as pas un code assez compliqué au niveau du positionnement des blocks, ça devrait passer sur tous les navigateurs. Si tu places tout en position absolute avec des marges, ça va coincer pour IE6.

    Tu peux toujours faire une css spéciale pour IE6, sans sprite css pour ces zones là.

  21. Conradson dit :

    J’ajoute (flemme d’éditer), que l’on peut gagner beaucoup en optimisation avec les points suivants :
    - limiter le nombre de css et de javascripts appelés. Faites des fichiers regroupant plusieurs css et javascripts si les tailles sont raisonnables
    - placer ses codes et scripts Javascripts en bas de page, ainsi la page sera affichée, puis le code Javascript sera interprété, sans interruption. Parfois, certaines bibliothèques mal conçue n’apprécie pas, à voir au cas par cas (Mootools accepte bien, pas Scripaculous par exemple)
    - minimiser le javascript et les css (http://developer.yahoo.com/yui/compressor/)
    - mettre des expire header aux fichiers appelés par la page, afin qu’il soit mis en cache par le navigateur
    - quand la première page est très sobre et succincte (genre la page d’accueil Google), vous pouvez pré-charger des images qui seront affichées sur les autres pages du site, afin de rendre plus fluide la navigation (c’est le cas pour le sprite css affiché dans les recherche Google).

  22. Regnareb dit :

    Y a quand même un beau soucis dans ton code :
    Sur Opera on voit +- | -
    Sur IE7 on ne voit rien.

    Sinon vieux, mais toujours aussi bon.

    J’utilise cette technique depuis un bail principalement pour les rollover, qui provoquaient un lag si l’image était chargée au survol. Je vais continuer à améliorer ça selon tes conseils, c’est une bonne idée.

    Jamais eu de soucis avec les rollovers sur Opera, sauf ceux en Js mais là faut être stupide pour faire ça en JS (ou alors avoir un besoin très spécifique).

  23. A dit :

    J’ai jamais compris un truc, pourquoi quand je surfe sur un forum par exemple, au lieu de charger simplement le texte qui change d’une page a l’autre le browser télécharge aussi les images de l’interface(ce qui relenti le bidule) alors que les images du forum sont toujours les mêmes?

  24. jye dit :

    Tu as un exemple de forum que je regarde un truc avant d’affirmer quoi que ce soit ?

    En fait y’a de fortes chances que ton navigateur ne re-récupère pas les images, il fait surement simplement une demande au serveur pour savoir si ces images ont changés depuis la dernière fois, histoire de savoir s’il y a besoin de les re-télécharger.

    Ca me permet de faire un parallèle car c’est là (entre autres) aussi que le CSS Sprites est intéressant, exemple :

    Sans CSS Sprites

    Une page avec 4 images de 10 ko - Premier affichage :

    - 1 requête pour récupérer image 1 -> 10 ko
    - 1 requête pour récupérer image 2 -> 10 ko
    - 1 requête pour récupérer image 3 -> 10 ko
    - 1 requête pour récupérer image 4 -> 10 ko
    -> Total 40 ko

    Deuxième affichage:

    - 1 requête pour vérifier si il y a besoin de récupérer image 1 -> 2 ko
    - 1 requête pour vérifier si il y a besoin de récupérer image 2 -> 2 ko
    - 1 requête pour vérifier si il y a besoin de récupérer image 3 -> 2 ko
    - 1 requête pour vérifier si il y a besoin de récupérer image 4 -> 2 ko
    -> Total 8 ko

    Avec CSS Sprites

    Une page avec 1 image de 45 ko contenant 4 images- Premier affichage :

    - 1 requête pour récupérer image 1

    Une page avec 4 images -> 45 ko
    -> Total 45 ko

    - Deuxième affichage:

    - 1 requête pour vérifier si il y a besoin de récupérer image -> 2 ko
    -> Total 2 ko

    Le premier chargement sera un peu plus long, mais tu gagneras par la suite en temps de chargements. Bien sur, il faut bien analyser son site pour voir si c’est intéressant ou pas à mettre en place. Mais l’idée est là !

  25. A dit :

    Non pas d’exemple particulier, tu as raison sans aucun doute mais le fait est qu’au finale j’ai l’impression de poiroter pour au finale avoir.. la meme image. Ce serait plus efficace si d’office il met la meme image (ce qui est toujours le cas sur un forum), et si jamais il y a eu une modification dans ce cas la il télécharge la version a jour. Evidement cela pourrait marcher avec un forum mais ce serait bordelique sur certains sites, au webmaster d’activer cette option.

  26. Des geeks et des lettres dit :

    Merci pour l’article, j’ai fait un article sur mon blog sur les css sprites (http://bit.ly/6c5cZT)

Laisser un commentaire

Si vous avez un compte sur WeFrag, connectez-vous pour publier un commentaire.

Vous pouvez, entre autres, utiliser les tags XHTML suivant :
<a href="" title="">...</a>,<b>...</b>,<blockquote cite="">...</blockquote>,<code>...</code>,<i>...</i>