Comment créer un sprite pour optimiser le chargement de ses images ?

Je m’occupe actuellement du développement du site myBonjour.fr qui permet le recensement et l’affichage de tous les blogs du type Bonjour Madame et Bonjour Poney.

Pour optimiser la vitesse de chargement des pages, je me suis intéressé à la technique aujourd’hui très populaire des Sprites CSS.

Un Sprite, qu’est-ce que c’est ? C’est une grande image qui contient plusieurs petites images utilisées au sein de votre application web, via les feuilles de style. En utilisant la propriété background CSS, il est assez facile de dire quelle partie du sprite doit être affichée en image de fond.
L’avantage de regrouper plusieurs petites images au sein d’un sprite est double. D’une part, l’image ainsi formée est souvent moins lourde que l’ensemble des petites images additionnées et, d’autre part, le nombre de requête HTTP pour charger les images est réduit à un. C’est ce dernier point qui est le plus important et le plus significatif en terme de gain de performances.

Voici un exemple de sprite :

Sprite de boutons

De nombreuses applications Web apportent une aide intéressante pour la création de Sprite à partir d’un site existant non optimisé.
J’ai retenu tout particulièrement le site http://spriteme.org qui offre un bouton à glisser dans sa barre de favoris et qui ouvre une popup en Javascript qui permet de créer un sprite à la volée et même de générer le CSS correspondant à partir du CSS existant.

Exemple avec SpriteMe

Maintenant, comment générer un Sprite soit même, directement dans son code symfony ? Vous allez voir ci-dessous le code que j’ai utilisé pour myBonjour.fr.

Pour cela, j’ai ajouté dans un premier temps une méthode privée dans mon modèle, s’occupant de générer le Sprite en question :

  private function makeSpriteForFavicons()
  {  
    $items = Doctrine_Core::getTable('MyBonjourItem')->getAllActiveItems();
    
    $width = 25;
    $height = 16;
    $space = 14;
    
    // On crée l'image vide
    $image = imagecreatetruecolor($width, ($height+$space) * count($items));
    $background = imagecolorallocatealpha($image, 255, 255, 255, 127);
    imagefill($image, 0, 0, $background);
    imagealphablending($image, false);
    imagesavealpha($image, true);
    
    // Ajout de tous les favicons à l'image
    $pos = 7;
    foreach($items as $item)
    {
      $tmp = imagecreatefrompng($item->getItemFilePath());
      imagecopy($image, $tmp, 0, $pos, 0, 0, $width, $height);
      $pos += $height + $space;
      imagedestroy($tmp);
    }

    imagepng($image, sfConfig::get('sf_web_dir').'/images/items_icons.png');
  }

Il suffit ensuite d’appeler cette méthode, quand la colonne correspondant à l’image est modifiée. Pour cela, on implémente la méthode save du modèle :

  public function save(Doctrine_Connection $conn = null)
  {   
    if(in_array('favicon_url', array_keys($this->getModified())))
    { 
      $return = parent::save($conn);
      $this->makeSpriteForFavicons();
    }
    else
      $return = parent::save($conn);
    
    return $return;
  }

Et enfin, on génère la partie correspondante dans le fichier CSS, à la volée :

// makeCSSSuccess.php
<?php $pos = 0; foreach($items as $item): ?>
div#<?php echo $item->getSlug() ?> > h3{
  background: url("/images/items_icons.png") no-repeat scroll 5px -<?php echo $pos*$width ?>px;
}
<?php $pos++; endforeach; ?>

Le fichier CSS généré par une action symfony doit être mis en cache pour éviter toute chute des performances. Il suffit de déclencher ensuite une regénération du cache lorsqu’un item est ajouté en base.
Sur myBonjour, j’ai ainsi supprimé le chargement de 60 requêtes HTTP, correspondant aux différents bonjours présents dans la liste.

Publié par Olivier Balais

Jeune ingénieur logiciel basé à Lyon (@overnetcity) passionné par les NTIC et le développement Web, je suis actuellement salarié chez Reputation VIP et effectue en parallèle des missions ponctuelles en temps que Freelance. Passionné depuis toujours par l'informatique et le développement, suite à une formation solide à l'INSA de Lyon, je me suis spécialisé dans la réalisation de bout en bout de projets web complexes.

Rejoindre la conversation

2 commentaires

  1. Bonjour Eric,

    Je ne connaissais absolument pas cette technique et je suis ravis que tu l’aies partagée ici.
    Les Data Uris semblent très très prometteuses surtout qu’il n’est pas toujours possible d’utiliser un sprite CSS dans de bonnes conditions dans certains cas (largeur ou hauteur d’un élément HTML beaucoup plus importante que la taille de l’image de background par exemple).

    Merci encore à toi

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *