Dans notre cas, un client utilise le plugin SPIP Nivoslider pour afficher sur la home page un diaporama de plusieurs images modifiables par l’utilisateur (qui peut donc en mettre beaucoup !) en assez grand format.
Malheureusement il en résulte un temps de chargement de la page assez important : plus de 6s mesurés par WebPageTest sous IE8 avec une connexion de type ADSL, pour 8 images affichées dans le diaporama.
Nous allons voir comment une optimisation assez simple à mettre en œuvre permet de gagner environ 2 secondes sur le temps de chargement et sur le temps d’affichage de la page.
Un bouchon dans le chargement de la page
Il suffit de regarder la cascade de chargement de la page pour comprendre le problème :
Toutes les images du diaporama sont chargées d’un coup, au début du chargement de la page (presque en premier après le chargement des fichiers CSS et JS, dont on remarque qu’ils sont bien concaténés et minifiés). Le chargement des images consomme toute la bande passante et pendant ce temps là aucune autre image de la page ne peut se charger.
Regardons comment le diaporama est construit. Le squelette chargé de son affichage est tout simple : il affiche les images associées à un article dans un conteneur html, et appelle le javascript de Nivoslider pour lancer le diaporama ensuite.
Le squelette inclus inclure/slider.html utilisé pour afficher le diaporama est assez simple :
<!-- SLIDER -->
<div id="cadre-slider">
<div class="slider-wrapper theme-default">
<div id="slider" class="nivoSlider">
<BOUCLE_doc(DOCUMENTS){id_article}>
[(#URL_DOCUMENT
|image_aplatir{jpg,ffffff,75}
|inserer_attribut{alt,#TITRE})]
</BOUCLE_doc>
</div>
</div>
</div>
<script type="text/javascript">
jQuery(window).load(function() {
jQuery('#slider').nivoSlider({
effect: 'fade', // Specify sets like: 'fold,fade,sliceDown'
animSpeed: 1000, // Slide transition speed
pauseTime: 3000 // How long each slide will show
});
});
</script>
Seule la première image est visible initialement, les autres étant masquées par la feuille de style du site. Alors pourquoi sont-elles toutes chargées d’un coup ?
Comment le navigateur décide de charger (ou non) les images ?
Ce comportement est tout à fait normal : les navigateurs modernes sont optimisés pour charger le plus vite possible toutes les images de la page. Pour cela le navigateur repère toutes les balises <img>
dans le HTML de la page et lance leur chargement dans leur ordre d’apparition.
Le navigateur n’attend pas de faire le rendu CSS pour lancer ce chargement, car cela lui ferait perdre trop de temps. D’ailleurs certains navigateurs n’attendent même pas d’avoir construit le DOM : ils repèrent et lancent le chargement des images le plus vite possible, en consultant le HTML brut de la page.
Dans tous les cas, le navigateur ne sait pas quelles images seront visibles ou non quand il décide de lancer leur chargement.
La stratégie choisie est donc de considérer que toutes les images présentes dans la page sont utiles et devront être chargées : autant commencer le plus vite possible !
Mais dans notre cas c’est bien dommage, car seule la première image est visible initialement, les autres ne l’étant que plus tard, au fur et à mesure de l’animation du diaporama.
Optimiser le diaporama
Pour optimiser le temps de chargement de la page, nous allons donc aider un peu le navigateur en modifiant la construction de notre diaporama.
Modifier le HTML
Puisque le navigateur charge toutes les images qu’il trouve dans la page, nous allons modifier le code HTML pour n’y indiquer que la première image, qui doit être visible lors de l’affichage initial, mais pas les suivantes. De cette façon, même si un utilisateur ne dispose pas de javascript activé, il pourra voir la première image s’afficher, même si il ne voit pas le diaporama : on ne dégrade pas le service rendu.
Pour cela nous créons un filtre SPIP dans le fichier PHP inclure/slider_fonctions.php. Ce petit filtre aura pour but de laisser la première image visible, et de renommer l’attribut src
en data-src
sur les images suivantes.
On lui passe donc la balise <img>
en premier argument, puis le compteur de la boucle en second argument :
<?php
function slider_img_masque_src($img, $compteur){
if ($compteur==1) return $img;
$src = extraire_attribut($img,"src");
$img = inserer_attribut($img,"data-src",$src);
$img = vider_attribut($img,"src");
return $img;
}
Dans le squelette inclure/slider.html on modifie la boucle en appliquant le filtre sur la balise qui affiche l’image :
<BOUCLE_doc(DOCUMENTS){id_article}>
[(#URL_DOCUMENT
|image_aplatir{jpg,ffffff,75}
|inserer_attribut{alt,#TITRE}
|slider_img_masque_src{#COMPTEUR_BOUCLE})]
</BOUCLE_doc>
Ainsi, là ou on avait auparavant le HTML suivant :
<img src='local/cache-gd2/8ba0c55328110304d5e57c7903f19faa.jpg' width='950' height='271' alt='' />
<img src='local/cache-gd2/ef82a3256f30c66f2399d7aadf67b34d.jpg' width='950' height='180' alt='' />
<img src='local/cache-gd2/8626fcf4d611aa86a1e46b70e00bc932.jpg' width='950' height='351' alt='' />
<img src='local/cache-gd2/5c6c47afa6b54b57d92c11861b6fcd14.jpg' width='950' height='180' alt='' />
<img src='local/cache-gd2/d589cf24c0ff701625eb9d2afb5f6884.jpg' width='900' height='200' alt='' />
<img src='local/cache-gd2/3a5849e9722782647f1c59f590a8fd7d.jpg' width='950' height='235' alt='' />
<img src='local/cache-gd2/1a6ee7fc86d556788621ed6135fcf7e0.jpg' width='950' height='197' alt='' />
<img src='local/cache-gd2/177305720686894776afbfedd61ff70e.jpg' width='946' height='180' alt='' />
on a maintenant le source suivant, où seule la première image est affichable par le navigateur :
<img src='local/cache-gd2/8ba0c55328110304d5e57c7903f19faa.jpg' width='950' height='271' alt='' />
<img width='950' height='180' alt='' data-src='local/cache-gd2/ef82a3256f30c66f2399d7aadf67b34d.jpg' />
<img width='950' height='351' alt='' data-src='local/cache-gd2/8626fcf4d611aa86a1e46b70e00bc932.jpg' />
<img width='950' height='180' alt='' data-src='local/cache-gd2/5c6c47afa6b54b57d92c11861b6fcd14.jpg' />
<img width='900' height='200' alt='' data-src='local/cache-gd2/d589cf24c0ff701625eb9d2afb5f6884.jpg' />
<img width='950' height='235' alt='' data-src='local/cache-gd2/3a5849e9722782647f1c59f590a8fd7d.jpg' />
<img width='950' height='197' alt='' data-src='local/cache-gd2/1a6ee7fc86d556788621ed6135fcf7e0.jpg' />
<img width='946' height='180' alt='' data-src='local/cache-gd2/177305720686894776afbfedd61ff70e.jpg' />
Modifier le JS
Il suffit ensuite de modifier le javascript pour charger les images au fur et à mesure. Là, on a du faire quelques essais pour trouver le bon compromis : il ne faut pas attendre l’affichage d’une image par le slider pour lancer son chargement, car dans ce cas elle n’est pas prête à temps et ne s’affiche pas, laissant un vide. La bonne solution est donc de lancer le chargement de l’image suivant quand on vient juste d’afficher une image. Et par suite, il faut prendre de l’avance, et charger la seconde image dès le lancement du slider (puisque la première image est déjà affichée).
Pour faire cela on créé une fonction javascript qui lance le chargement d’une seule image :
function sliderLoadNextImg(slider){
var toload = jQuery("img:not(.loaded)[data-src^=]",slider);
if (toload.length) { toload = toload.eq(0); toload.attr('src',toload.attr('data-src')).attr('data-src','').addClass('loaded');}
}
La fonction cherche la première image qui a un attribut data-src et le copie dans l’attribut src, ce qui lance son chargement. Si toutes les images sont déjà chargées, la fonction ne fait rien.
Il suffit alors d’utiliser les callback proposées par nivoslider pour appeler cette fonction :
- lors de l’initilisation du slider
- après l’affichage d’une image
Ce qui nous donne :
<script type="text/javascript">
function sliderLoadNextImg(slider){
var toload = jQuery("img:not(.loaded)[data-src^=]",slider);
if (toload.length) { toload = toload.eq(0); toload.attr('src',toload.attr('data-src')).attr('data-src','').addClass('loaded');}
}
jQuery(window).load(function() {
jQuery('#slider').nivoSlider({
effect: 'fade', // Specify sets like: 'fold,fade,sliceDown'
animSpeed: 1000, // Slide transition speed
pauseTime: 3000, // How long each slide will show
afterLoad: function(){sliderLoadNextImg(jQuery('#slider'))},
afterChange: function(){sliderLoadNextImg(jQuery('#slider'))}
});
});
</script>
Un chargement plus rapide
On évalue avec l’outil WebPageTest l’impact de notre optimisation sur le chargement de la page :
On voit clairement que le bouchon a sauté : une seule image chargée qui laisse la place au chargement des autres composants de la page. Celle ci-se charge maintenant en un peu moins de 4.5s.
Un rendu plus rapide
A noter que les cascades montrent de manière tompeuse que le début du rendu aurait été retardé par l’optimisation.
L’analysé détaillée du film de rendu de la page montre que dans le premier cas le rendu ne débutait réellement qu’au bout de 5s (seule la couleur de background était rendue à 1.6s), et que la page n’était visuellement complète qu’au bout de 6.6s.
Après optimisation, le rendu débute vers 3s et est visuellement complet au bout de 4.3s (à ce moment la page était encore vierge avant l’optimisation).
Généraliser l’optimisation pour tous les utilisateurs
On voit qu’il est ainsi facile, moyennant quelques modifications, d’optimiser le chargement d’un diaporama à l’aide du plugin SPIP Nivoslider. Mais il est dommage de devoir faire soi-même ce travail à chaque utilisation du plugin.
Nous verrons donc dans un prochain billet comment améliorer le plugin pour qu’il propose par défaut la meilleure stratégie de chargement possible et généraliser ainsi les bonnes pratiques de Performance Web.
Crédit photo Mark Robinson
Vos commentaires
# Le 16 septembre 2013 à 16:18, par RastaPopoulos En réponse à : Slider d’images : attention au temps de chargement
Pour garder en mémoire des sources sous la main, j’ai fait une petite compilation autour des tests récents sur les carrousels : http://seenthis.net/messages/175747
# Le 25 octobre 2013 à 18:56, par Jean-Baptiste En réponse à : Slider d’images : attention au temps de chargement
Bonjour Cédric, J’ai vu sur le SVN de SPIP que tu avais mis à jour le plugin Nivo Slider en intégrant ces améliorations au plugin (version 3.0). Par contre, c’est toujours la version 2.0 du plugin qui est installée sur les sites Nursit et disponible au téléchargement sur SPIP-Contrib. Une mise à jour est-elle prévue ? Merci pour ces améliorations, en tous cas
# Le 20 décembre 2013 à 10:47, par RastaPopoulos En réponse à : Slider d’images : attention au temps de chargement
Une nouvelle ressource sur le sujet : http://shouldiuseacarousel.com/
Et l’interview associée : http://www.creativebloq.com/accessibility-expert-warns-stop-using-carousels-7133778
Répondre à cet article
Suivre les commentaires : |