1

Plugin SPIP : « Image responsive »

20 mars 2015
par ARNO*

Le plugin « Image responsive », que j’ai créé en même temps que Cédric développait le plugin « Adaptive images », repose sur une logique sensiblement différente.

Les problématiques sont, à la base, les mêmes, dans le cadre d’un site responsive (c’est-à-dire qui s’adapte à la taille de l’écran du visiteur) :

  • afficher de grandes images sur un grand écran, et afficher de petites images sur un petit écran,
  • afficher des images « deux fois plus grandes » sur un écran à haute définition (notamment les smartphones et tablettes récents, mais aussi désormais certains ordinateurs portables), et afficher une image « normale » sur les autres écrans.

Mon problème est, avant tout, que j’ai des maquettes très personnalisées, et qu’une façon unique d’adapter les images sur l’ensemble du site ne me convient pas. Outre des images de grande taille, j’ai par exemple besoin de traiter les vignettes de navigation, qui s’affichent à différentes tailles sur différents supports, sans pour autant être de « grandes images ».

Le principe de ce plugin consiste à afficher (ou non) une vignette de l’image, puis à charger (dès que Javascript se déclenche) l’image définitive aux bonnes dimensions et à la bonne résolution. Depuis les dernières versions, il est de plus possible de créer avec le plugin des balisages HTML modernes qui permettent le fonctionnement responsive sans Javascript et facilitant le préchargement des contenus.

Au passage, le plugin permet d’automatiser un fonctionnement en « lazy load », c’est-à-dire le chargement des images uniquement lorsqu’elles sont visibles dans la page.

Je l’utilise en production depuis une bonne année, et je continue à le faire évoluer. On peut par exemple le voir fonctionner sur le site Flip-Zone (il est également en fonction sur les sites de la Fémis et les Arts décoratifs, mais dans des versions un peu moins optimisées).

Installation

Le plugin est disponible sur le site Plugins SPIP (et donc en installation et mise à jour automatiques via l’interface privée de SPIP), à partir de SPIP 3.0.

Si vous utilisez les URL « propres » dans SPIP, il faut modifier votre fichier .htaccess pour y ajouter le contenu du fichier ajouter_a_htaccess.txt livré avec le plugin.

Attention : votre travail ne fait alors que commencer. Contrairement à Adaptative images, ce plugin n’aura d’intérêt que si vous modifiez vos squelettes pour l’utiliser selon vos besoins. Si vous ne modifiez pas vos squelettes, il ne se passera rien…

La fonction image_responsive

Le principe est d’introduire une nouvelle fonction : image_responsive.

De base, elle fonctionne ainsi :

  1. [(#LOGO_ARTICLE_NORMAL|image_responsive)]

Sans autre réglage, le fonctionnement sera le suivant :

  • une image de 120 pixels de large se chargera avec la page (mais l’image s’affichera « en grand », c’est-à-dire la largeur du bloc qui contient l’image – par exemple, si l’endroit où s’affiche l’image fait 300 pixels, on affichera ce fichier basse définition de 120 pixels de large, mais « agrandi » à la largeur de 300 pixels) ;
  • après le premier affichage de la page, un script calculera la largeur réelle du bloc d’affichage (dans notre exemple, 300 pixels), et remplacera l’image basse définition par une image de 300 pixels de large ;
  • si on redimensionne l’écran et que la largeur disponible pour afficher l’image est de 150 pixels, alors on chargera une image d’exactement 150 pixels ;
  • si on est sur un écran à haute densité (Retina dans le jargon Apple) et qu’on dispose de 300 pixels de large pour afficher l’image, alors on chargera un fichier de 600 pixels de large qu’on affichera sur cette largeur de 300 pixels (en haute densité donc).

Maquetter via les CSS

La balise <img> est ici remplacée par un code différent, et l’image elle-même est dans un conteneur plus complexe (qui peut, d’ailleurs, changer en fonction des versions du plugin, de façon à intégrer les dernières possibilités du HTML5). De fait, il ne faut plus contrôler le style de l’image, en particulier sa largeur d’affichage, via la balise <img> elle-même, mais par l’élément qui la contient – le résultat du filtre image_responsive occupera de toute façon toute la largeur disponible de son conteneur).

Par exemple, il faudra bien prendre soin à intégrer l’image responsive dans un conteneur, div ou span selon les besoins, que l’on pourra ainsi maquetter via les CSS (c’est bien tout l’intérêt, puisqu’on fait du responsive) :

  1. [<div class="logo">(#LOGO_ARTICLE_NORMAL|image_responsive)</div>]

Le travail de maquette se fera donc dans le style de div.logo, et non plus directement sur l’image elle-même. L’image, dans tous les cas, occupera toute la largeur disponible fixée par div.logo.

Emplacement de l’image pré-réservé

Puisque l’image va occuper toute la largeur, la balise <img> ne peut plus contenir d’indications de height et de width. C’est classique en responsive (puisque la taille d’affichage des images va varier selon l’écran), mais cela pose le problème classique de pré-réserver l’espace occupé par l’image.

Classiquement, on indiquait dans le code HTML les dimensions de l’image pour que l’affichage de la page commence en réservant l’espace correct de l’image, avant même de l’avoir chargée. Sinon, cela donnait l’effet désagréable d’afficher la page sans l’image puis d’avoir tout le texte qui se déplace violemment une fois que le navigateur a chargé l’image.

Le plugin fabrique automatique le code HTML/CSS qui permet de pré-réserver l’emplacement correct de l’image, sans utiliser height et width (le plugin va utiliser un padding-bottom en pourcentage de la largeur, permettant de définir les proportions du bloc image sans connaître ses dimensions en pixels). C’est donc transparent pour le webmestre, mais ne vous étonnez donc pas de l’absence de height et de width dans le code généré (surtout si PageSpeed vous le réclame) : c’est fait exprès, et le code généré est conçu justement pour contourner leur absence.

Première subtilité : des images réduites plus nettes

Une première difficulté que le plugin a dû contourner, c’est le fait que plus on réduit la taille d’une image, plus elle semble légèrement « floue », ou un peu « molle ». C’est un problème classique lorsqu’on prend une image de grande taille et qu’on la réduit à la taille d’une vignette

Ce qu’on pouvait résoudre dans SPIP, auparavant, en faisant passer un filtre de renforcement de netteté après une réduction de taille (par exemple image_renforcement{0.1}).

Ce plugin introduit une nouvelle fonction, image_reduire_net que l’on peut utiliser à la place de image_reduire. À partir de PHP 5.1 (utilisation de imageconvolution), cette fonction va réduire et renforcer l’image réduite, et cela en fonction du redimensionnement (plus la vignette est petite par rapport à l’image d’origine, et plus on va renforcer la netteté). Sur PHP avant 5.1, le script devrait fonctionner très bien, mais sans le renforcement de la netteté.

En pratique, c’est totalement transparent pour le webmestre : le simple fait d’utiliser le filtre image_responsive va faire calculer les images plus petites avec cette nouvelle fonction, et on maintiendra donc automatiquement les images à un niveau de netteté cohérent.

Deuxième subtilité : des images haute définition de poids raisonnable

Attention : ce comportement est désactivée à partir de la version 7.6.0 du plugin (mai 2019) : les images haute définition sont désormais identiques aux images deux fois plus grandes compressées normalement, de façon à préserver la qualité d’affichage (au détriment, donc, du poids des fichiers). On peut rétablir le comportement « ultra-compressé » des images sur écran haute définition en ajoutant :

  1. define("_IMAGE_RESPONSIVE_RETINA_HQ", true);

Un (énorme) souci avec la gestion de la haute définition, c’est qu’on se retrouve à envoyer des images avec quatre fois plus de pixels à destination des écrans haute densité, qui sont actuellement très majoritairement des écrans de smartphones. Pour afficher une image de 320x240 pixels à l’écran, on charge un fichier de dimensions 640x480 pixels : quatre fois plus de points, mais surtout un fichier JPEG sept fois plus lourd, alors qu’on a une connexion moins rapide qu’à la maison (j’ai posté sur Seenthis une explication avec un exemple d’image qui passe de 22ko à… 169ko de cette façon).

La solution ici consiste, lorsqu’on appelle les images à haute densité, à créer des fichiers JPEG beaucoup plus compressés. Après différents essais, j’ai décidé de fabriquer des JPEG compressés en qualité 50% au lieu de 85% habituels de SPIP. À l’usage, cela nous semble le bon équilibre entre le poids du fichier et le fait qu’on ne perçoive pas la dégradation de l’image. Attention : si on affiche une telle image sur un écran de densité « normale », alors on verra un fichier JPEG très dégradé ; mais sur un écran à haute densité, on ne parvient pas à détecter les artefacts de compression. Mais on perçoit bien, en revanche, cette impression de « haute densité » des images par rapport à l’utilisation d’images « normales ». (Dans l’exemple sur Seenthis, l’image haute densité revient à un poids de 34ko, bien plus raisonnable que les 169ko obtenus sans cette astuce.)

À l’usage, on expédie ainsi des images avec quatre fois plus de points, mais dans des fichiers dont le poids est seulement 30% plus lourd qu’une image de taille normale.

Troisième subtilité : une fonction pour recadrer une image sans la réduire

Puisque le plugin gère lui-même des réductions de taille des images selon les critères nécessaires (taille réelle d’affichage, mais aussi densité de l’écran), il devient très habituel de simplement lui faire passer l’image d’origine, sans se soucier de sa taille. On ne va donc plus gérer des |image_reduire, puisque le plugin va faire « mieux » automatiquement.

On a alors une difficulté si on a pris l’habitude de normaliser les proportions des images (par exemple toutes les vignettes de navigation dans un format 16x9, pour donner un aspect esthétique cohérent et élégant) : le filtre image_recadre fonctionne sur des valeurs absolues.

Le plugin introduit donc une fonction supplémentaire, destinée à remplacer image_recadre : image_proportions. Si je veux une image recadrée en 16x9, je fais simplement :

  1. [(#LOGO_ARTICLE_NORMAL|image_proportions{16,9})]

J’obtiens alors une image aux proportions 16x9, mais conservant une de ses dimensions d’origine (si l’image d’origine fait 1200x800 pixels, j’obtiens après ce filtre une image de 1200x675 pixels ; si l’image d’origine fait 300x240 pixels, j’obtiens après ce filtre une image de 300x169 pixels).

Évidemment, l’idée est ici de l’utiliser juste avant le filtre image_responsive.

Retour à image_responsive : fixer des tailles autorisées

Nous en sommes donc toujours à utiliser le filtre de manière automatique :

  1. [(#LOGO_ARTICLE_NORMAL|image_responsive)]

Si c’est bien pratique pendant la phase de fabrication du site, une fois que le site est réellement actif et visité par le public, ce fonctionnement entièrement automatique devient très problématique : si les tailles d’affichage de l’image ne sont pas strictement définies par les CSS, on risque de fabriquer des centaines de versions de cette image (selon les différentes tailles d’écran des visiteurs).

Il est donc important, une fois qu’on a bien compris les tailles d’affichage que l’on va réellement utiliser pour cette image en fonction de nos scénarios responsive, les différentes tailles autorisées :

  • le premier intérêt étant évidemment de limiter le nombre de fichiers fabriqués par le plugin,
  • le second étant de préparer l’utilisation d’un balisage HTML5 particulièrement efficace (voir plus loin dans cette documentation).

Commençons par un cas extrême : je n’autorise qu’une seule taille d’affichage. Il y a de nombreux cas où c’est pertinent (parfois : petit logo du site, ou petits boutons de partage…). On indique directement cette taille ainsi :

Quelle que soit la taille d’affichage final de l’image, on charge une image de 150 pixels de large (si on n’est pas idiot, il y a des chances que la taille d’affichage réel sera bien 150 pixels…). Mais attention : ce n’est pas équivalent à image_reduire{150} (ni même image_reduire_net{150}). Si l’écran du visiteur est à haute densité (smartphone récent notamment), alors le plugin va automatiquement charger une seconde version de l’image : une image de 300 pixels de large (éventuellement plus compressée s’il s’agit d’un JPEG).

Le code précédent, indiquant une taille unique, autorise en fait l’utilisation de deux fichiers différents, ce qui permet de faire un affichage « simple » qui sait s’adapter à un écran haute densité. (Nous verrons plus loin que, dans ce cas, un balise HTML moderne est directement fabriqué par le plugin.) C’est très adapté pour ces petits boutons-outils, du style boutons de partage sur Facebook/Twitter, qui apparaissent toujours à la même taille sur les différents supports, mais qu’on veut passer en double-définition sur écran Retina.

Si la taille unique répond à la problématique des écrans haute densité, elle ne suffit pas pour gérer les différentes tailles d’affichage selon des tailles d’écran. Par exemple, une image occupant toute la largeur de l’écran (écran qui fera les 320 pixels d’un smartphone « usuel » tenu verticalement, jusqu’à un écran de 1920 pixels de large). C’est le problème central du responsive : je ne peux pas utiliser simplement l’image de 320 pixels sur un écran de 1920 pixels, ça serait très laid (image extrêmement pixelisée), comme je ne veux pas expédier une image de 1920 pixels, extrêmement lourde, pour finalement l’afficher sur un tout petit écran de 320 pixels. Comme on l’a vu, ce problème est résolu automatiquement par le plugin sans avoir besoin d’indiquer des variables, mais avec le risque alors de fabriquer autant de fichiers qu’il existe de largeurs de fenêtres (disons 1920-320 = 1600 variantes de l’image). On se retrouve à fabriquer des giga-octets d’images si on laisse le filtre sans limites.

On va donc indiquer les différentes tailles d’images que l’on autorise en les séparant par un slash :

  1. [(#LOGO_ARTICLE_NORMAL|image_responsive{160/320/480/640/960/1440/1920})]

Cela signifie que, si la taille d’affichage est inférieure à 160 pixels (par exemple 120 pixels), on charge l’image de 160 pixels (mais on l’affiche plus petite, sur une largeur de 120 pixels), puis de 320 pixels jusqu’à 320 pixels, etc.

En pratique, je limite encore largement le nombre de variantes. Si on parvient à réduire à 3 ou 4 valeurs autorisées, c’est aussi bien, puisqu’on calculera alors beaucoup moins d’images.

Effet de bord induit : on fait travailler PHP sur chaque image

Arrivé à ce stade, il est important de comprendre que les images ne sont pas « pré-calculées » au moment du calcul de la page, comme c’est le cas habituellement avec SPIP (si je fais image_reduire, je fabrique l’image réduite au moment où je calcule la page). Avec ce plugin, je ne fabrique rien d’autre que le balisage HTML, et le fichier « image » qui est appelé est en fait une page PHP (d’un genre spécial : qui fabrique la vignette de l’image).

Bien entendu, il y a un système de cache de fichier pour chaque taille d’image déjà visitée, mais on se retrouve tout de même à déclencher un script PHP à chaque hit sur ce « fichier image » (ce script PHP se faisant évidemment passer pour une image).

Une fois la taille d’image calculée, ce script est très rapide et travaille peu, mais il y a de l’occupation de mémoire, une instance qui tourne, etc. Sur un site à fort traffic et/ou avec beaucoup d’images, ça peut demander une adaptation de la configuration.

Et finalement, sur un site très visité et/ou avec beaucoup d’images, la meilleure solution sera sans doute d’installer Varnish et de bien veiller à ce que les images responsive passent bien par son cache. (C’est ce que je fais sur Flip-Zone, qui contient plus de 100.000 images très grandes, toutes gérées désormais via image_reponsive.)

Contrôler le cache du plugin

Le plugin va donc fabriquer des images en fonction de leur taille d’affichage. Les images calculées sont logiquement installées dans un sous-dossier de /local, que l’on peut contrôler et vider dans la page « Vide le cache » de l’espace privé, dans le pavé « Images calculées automatiquement ».

Le plugin ajoute cependant un pavé supplémentaire dans la page « Vider le cache » de l’espace privé de SPIP : « Images pour responsive ». En effet, sur un site avec beaucoup d’images, vider complètement le cache /local peut s’avérer très violent. Or, il y a certainement des tailles d’images qui sont certainement beaucoup moins utilisées que d’autres.

Ce nouveau pavé de contrôle du cache va afficher le poids occupé par « toutes les images », les images « non vues depuis 3 jours », les images « non vues depuis une semaine », « un mois », « 3 mois ». On peut alors effacer sélectivement ce que l’on veut (supprimer images réduites qui n’ont pas été vues depuis un mois ne va certainement pas pénaliser le serveur). Note : cette fonction se base sur le atime(), et ne fonctionnera donc pas si on l’a désactivé sur ce disque dur pour faire tourner le serveur plus rapidement.

Sur un site où l’on a bien conçu et limité les tailles autorisées des images, on ne devrait pas avoir réellement d’images qui sont rarement consultées. Mais cela demande de l’habitude, et avant de réussir à bien cerner les tailles pertinentes, ce troisième pavé permet de gérer plus finement ses nettoyages de cache et éventuellement signaler qu’il faut modifier ses squelettes pour tenter d’optimiser la fabrication de tailles d’images peu utilisées.

Avec ou sous sans vignette au chargement de la page

Le principe de base du plugin est donc de charger la page HTML avec une balise <img>, puis de charger l’image à la bonne taille une fois que la page a été affichée.

La question est de savoir ce qu’on veut afficher dans ce <img> dès le chargement de la page et avant le déclenchement du script.

1. Par défaut. Si on n’a défini aucune taille autorisée, l’image par défaut sera une vignette de 120 pixels de large.

2. Première taille autorisée. Si on a défini une ou plusieurs tailles autorisées, alors c’est la première taille indiquée qui sera chargée avec la page. Dans notre exemple, c’est donc une image de 160 pixels de large qui est chargée avec la page avant le lancement du script :

  1. [(#LOGO_ARTICLE_NORMAL|image_responsive{160/320/480/640/960/1440/1920})]

Visuellement, si on est sur une taille d’affichage nettement plus grande, on obtient l’effet visuel suivant :

  • dès l’apparition de la page, l’image de grande taille apparaît très pixellisée,
  • après lancement du javascript, la « bonne » image apparaît donc.

Selon l’usage qu’on en a, ce n’est pas réellement gênant (à une époque, on avait une même une balise HTML pour faire ça, et cela produit visuellement une impression similaire au chargement d’un JPEG progressif).

3. Rien. En revanche, si l’image définitive n’est pas la version 160 pixels en densité normale, alors on déclenche deux appels au serveur au lieu d’un. Dans certains cas, on peut juger que c’est une mauvaise idée.

Dans ce cas, on peut indiquer que l’on ne veut pas charger de vignette au chargement de la page. (Solution pour l’instant : on charge quand même un petit fichier, qui est rien.gif. Je pense essayer faire autrement dans le futur.) Pour cela, il suffit d’ajouter la taille « 0 » (le chiffre zéro, évidemment) au début des tailles autorisées :

  1. [(#LOGO_ARTICLE_NORMAL|image_responsive{0/160/320/640/1280/1920})]

Charger uniquement les images visibles : mode lazyload

Sur un site avec des pages un peu longues (les pages Web deviennent d’ailleurs habituellement beaucoup plus longues quand elles sont affichées du smartphone…), on peut vouloir charger les images uniquement si elles sont visibles à l’écran. C’est le principe du lazy load.

En théorie, les navigateurs savent charger les images de manière « intelligente », en commençant en priorité par les images visibles. Mais cette méthode permet de le gérer via notre propre script, et d’être certain de ne pas charger ce qui n’est pas visible.

Pour cela on utilise une seconde variable du filtre, que l’on va fixer à 1 si l’on veut charger cette image en lazy load (par défaut : 0) :

  1. [(#LOGO_ARTICLE_NORMAL|image_responsive{0/100/200, 1})]

Ici : je ne charge rien au chargement de la page, et le script ne chargera une image de 100 ou 200 pixels (en basse ou haute densité) que si l’image est visible à l’écran (en fait : on prévoit de la marge sur cette notion de « visible », pour que les images juste en dessous de la ligne de scroll soient déjà chargées).

Alignement vertical

Le principe de base est de calculer la taille de l’image selon la largeur disponible à l’endroit où elle s’affiche. C’est l’utilisation la plus commune des images dans une maquette Web.

Avec cette variable supplémentaire (que l’on fixe à 1 si on veut ce fonctionnement ; sinon la valeur par défaut est 0), on peut forcer le calcul non plus sur la largeur de l’espace disponible, mais sur sa hauteur :

  1. [(#LOGO_ARTICLE_NORMAL|image_responsive{150/300, 0, 1})]

Ici : on affiche une vignette de 150 pixels au chargement du HTML, puis on calcule si on charge une image de 300 pixels de haut si la hauteur disponible pour cette image est plus grande que 150. (Et on ne fonctionne pas en lazy load, la seconde variable étant laissée à 0.)

Le balisage responsive directement en HTML5

Depuis quelques temps, il existe des balisages directement en HTML5, permettant de gérer des images responsive sans attendre le déclenchement de Javascript et en profitant des optimisations des navigateurs (preloading). Le plugin permet d’introduire ces balises.

L’intérêt est de permettre au navigateur de pré-charger les image de manière intelligente, ou au moins de lancer leur chargement en même temps que la page HTML et de les afficher directement aux bonnes dimensions sans attendre le lancement du script. On a une impression de fluidité et de vitesse de l’affichage qui est bien meilleur, à la fois sur les pages jamais visitées et sur les pages déjà visitées.

Mise à jour 2017. Ces balisages du HTML5 sont désormais largement répandus dans tous les navigateurs. Je recommande désormais absolument d’utiliser ces spécificités du plugin, le rendu à l’écran s’en trouve très largement amélioré (impression d’« immédiateté » dans l’affichage des images). Notez bien que, pour les navigateurs qui ne comprennent pas nativement ces balises, un script Javascript fournit automatiquement assurera l’affichage correct.

1. scrset. La première méthode est appliquée automatiquement si l’on utilise une taille unique pour l’image :

  1. [(#LOGO_ARTICLE_NORMAL|image_responsive{150})]

dans ce cas, le code HTML fabriqué par le plugin, sans aucune intervention supplémentaire de votre part, va ajouter un srcset à la balise <img> permettant de gérer directement les densité 1x et 2x.

Cette méthode est très simple à utiliser, et s’applique très bien aux vignettes de navigation, dont la taille d’affichage ne varie pas beaucoup selon les dimensions de l’affichage.

Mise à jour 2017. Selon CanIUse.com, tous les navigateurs actuels comprennent cette balise ; plus de 85% des utilisateurs en profitent.

2. picture. La seconde méthode demande un investissement plus important de votre part, mais le résultat est incomparable, puisqu’il permet un contrôle très riche de ce qu’on affiche.

Mise à jour 2017. Désormais plus de 89% des utilisateurs profitent de cette balise.

Il faut pour cela utiliser une quatrième variable à notre filtre, relativement complexe cette fois, contenant les media queries pour chaque des tailles d’image autorisées. Par exemple :

  1. [(#LOGO_ARTICLE_NORMAL
  2.         |image_responsive{
  3.                 0/50/100/200,
  4.                 0,
  5.                 0,
  6.                 (max-width:470px)/(max-width:1009px)/
  7.         })]

Télécharger

Explication :

  • avec la première variable (0/50/100/200), je charge la page sans vignette (en fait : rien.gif), et j’autorise le chargement d’images de largeur 50, 100 ou 200 pixels ;
  • la seconde variable est à zéro, donc pas de lazy load ;
  • la troisième variable est à zéro, donc la calcul se fait bien sur la largeur disponible ;
  • la quatrième variable ((max-width:470px)/(max-width:1009px)/) est celle qui va permettre de mettre en place le code HTML5 gérant nativement les images responsive.

Cette variable doit contenir autant d’entrées qu’il y a de tailles de fichiers autorisées, séparées par des slash. Dans la première variable, il y a trois tailles autorisées (50, 100, 200 – la taille « 0 » n’est pas une taille autorisée, elle ne fait qu’indiquer qu’on charge la page sans vignette). Nous avons donc ici trois valeurs de media queries séparées par des slash :

  • (max-width:470px)
  • (max-width:1009px)
  • et… rien. Ce sera donc la valeur « par défaut » si les deux autres ne s’appliquent pas. Notez qu’il faut bien un slash pour définir cette valeur « vide » dans la dernière variable.

La logique est donc :

  • si l’écran fait 470px et moins, je charge l’image de 150 pixels, en simple ou double densité selon l’écran (1x ou 2x),
  • si l’écran fait 1009px et moins, je charge l’image de 100 pixels, en simple ou double densité,
  • au-delà je charge l’image de 200 pixels (1x ou 2x)

Il y a ici deux difficultés pour vous :

  • la première, c’est qu’il faut absolument que vos valeurs de media queries indiquées soient cohérentes avec les valeurs utilisées dans vos CSS, sinon vous risquez de lancer des chargements d’image pour rien ; dans certains cas, ce n’est pas évident…
  • c’est difficile à tester, parce que de toute façon le javascript va se déclencher après le premier affichage de la page ; pour vérifier le code généré, désactiver javascript dans un navigateur compatible (aujourd’hui : Chrome).

Par rapport au fonctionnement « simple » du plugin (définir des tailles autorisées), c’est nettement plus contraignant, pour un affichage final de la page totalement identique. En revanche, pendant le chargement et l’affichage de la page, l’effet de fluidité et de rapidité est incomparable (en règle générale : tout ce que vous parvenez à afficher correctement sans attendre Javascript est un gain énorme pour l’expérience de navigation). Je pense que ça vaut vraiment le coup de s’embêter un peu…

Et par rapport à un simple usage de picturefill.js (parce qu’alors ça revient à ça…), le code HTML est vraiment plus facile à générer avec ce plugin qu’à la main. Jetez un coup d’œil, ça fait tout de même un sacré bestiau :

  1. <picture
  2.         style="padding:0;padding-bottom:126.84989429175%"
  3.         class="conteneur_image_responsive_h"
  4. >
  5.         <source media="(max-width:470px)" srcset="/local/cache-couv/f/f58b4d3df25bd84fe7f9fddf405e260f-resp50.jpg 1x,/local/cache-couv/f/f58b4d3df25bd84fe7f9fddf405e260f-resp50-2.jpg 2x">
  6.         <source media="(max-width:1009px)" srcset="/local/cache-couv/f/f58b4d3df25bd84fe7f9fddf405e260f-resp100.jpg 1x,/local/cache-couv/f/f58b4d3df25bd84fe7f9fddf405e260f-resp100-2.jpg 2x">
  7.         <source srcset="/local/cache-couv/f/f58b4d3df25bd84fe7f9fddf405e260f-resp200.jpg 1x,/local/cache-couv/f/f58b4d3df25bd84fe7f9fddf405e260f-resp200-2.jpg 2x">
  8.         <img src="rien.gif" data-src="/local/cache-couv/f/f58b4d3df25bd84fe7f9fddf405e260f.jpg" data-l="473" data-h="600" data-tailles="[\&quot;50\&quot;,\&quot;100\&quot;,\&quot;200\&quot;]" class="image_responsive">
  9. </picture>

Télécharger

Vous constaterez que, par rapport aux démonstrations habituelles de <picture>, ici on prend bien soin à gérer la densité de l’écran avec des images JPEG différentes (l’image haute définition n’est pas simplement l’image deux fois plus grandes, elle est aussi beaucoup plus compressée en JPEG pour éviter de livrer des images 7 fois plus lourdes là où notre système livre des images seulement 1,3 fois plus lourdes).

Image adaptative, recadrage et direction artistique

Ajout 2017. Le filtre image_responsive autorise le recadrage de l’image en fonction de la dimension finale d’affichage, en utilisant les media queries. C’est là une fonctionnalité extrêmement puissante.

Pour cela, nous utilisons une série supplémentaire de configurations, après la liste de media queries, constituées de valeurs séparées par des slash. Ces valeurs correspondent aux variables de la fonction image_proportions : largeur, hauteur, alignement du recadrage (top, left, …, focus, focus-center), et zoom (par défaut : 1).

Voici par exemple l’affichage d’une image sur toute la largeur de l’écran, en changeant le recadrage selon la taille de l’écran :

  1. [(#LOGO_ARTICLE_NORMAL
  2.         |image_responsive{0/480/800/1280/1920,
  3.         0, 0,
  4.         (max-width: 480px)/(min-width:481px) and (max-width: 800px)/(min-width:801px) and (max-width:1280px)/,
  5.         1x1xfocusx1.5/5x4xfocusx1.2/3x2xfocus/2x1xfocus
  6. })]
  7. </spip>

Télécharger

Nous avons ici 4 tailles autorisées, correspondant à 4 media queries, avec 4 recadrage différents :

— image de 480 pixels de large, si (max-width: 480px), avec un recadrage 1x1xfocusx1.5, c’est-à-dire un recadrage carré (1x1) selon le centre d’intérêt de l’image, avec un zoom de 150% ;

— image de 800 pixels de large, si (min-width:481px) and (max-width: 800px), avec un recadrage 5x4xfocusx1.2, c’est-à-dire un recadrage légèrement aplati (5x4) selon le centre d’intérêt de l’image, avec un zoom de 120% ;

— image de 1280 pixels de large, si (min-width:801px) and (max-width:1280px), avec un recadrage 3x2xfocus, c’est-à-dire un recadrage horizontal (3x2) selon le centre d’intérêt de l’image, sans zoom dans l’image ;

— image de 1920 pixels de large le reste du temps (pas d’indication explicite après le dernier slash de la série de media queries) avec un recadrage 2x1xfocus, c’est-à-dire un recadrage très horizontal (2x1) selon le centre d’intérêt de l’image, sans zoom dans l’image.

Voici un autre code, que j’utilise pour « remplir » l’écran de la page d’accueil selon l’orientation de l’écran (ce n’est pas un « remplissage » exact de l’écran, mais un recadrage qui, grosso modo, correspond à l’orientation de l’écran ; pour l’utilisateur l’effet est très similaire, en revanche ça permet de précharger la bonne image dès le chargement de la page). Noter ici le fait que j’utilise deux fois la même série de valeurs de largeur, mais avec des media queries différents et des recadrage différents (pour gérer l’orientation de l’écran) :

  1. [(#LOGO_ARTICLE_NORMAL|image_responsive{
  2.         0/480/800/1280/1920/480/800/1280/1920,
  3.         0, 0,
  4.         (max-width: 480px) and (orientation:portrait)/(min-width:481px) and (max-width: 800px) and (orientation:portrait)/(min-width:801px) and (max-width:1280px) and (orientation:portrait)/(min-width:1281px) and (orientation:portrait)/
  5.         (max-width: 480px)/(min-width:481px) and (max-width: 800px)/(min-width:801px) and (max-width:1280px)/,
  6.         4x5/4x5/4x5/4x5/3x2/3x2/3x2/3x2
  7.         })]

Télécharger

Un aspect vraiment important ici, c’est que le code HTML5 généré utilise le balisage <picture>, et que le chargement de ces images est ultra-efficace (préchargement, et affichage immédiat quand on revient sur une page déjà visitée).

Précalculer les images

L’une des difficultés du plugin est de passer par un script PHP à chaque fois qu’une image est affichée. (La configuration du fichier .htaccess décrite dans la partie « Installation » ci-dessus rend cet appel transparent, mais c’est bien un script qui se déclenche pour afficher une image.)

Une solution pour limiter le passage par ces scripts est d’installer un proxy inverse, tel Nginx ou Varnish, en veillant à le configurer pour qu’il passe bien les images dans son cache. De cette façon, évidemment, on limite énormément les appels de scripts sur le serveur Web.

Si l’on n’a pas cette possibilité, et si le serveur a tendance à ne pas tenir la charge, la solution consiste à demander au plugin de pré-calculer les images pour toutes les tailles, et à les stocker sur le serveur et, ainsi, à fonctionner avec des appels directs aux fichiers d’images statiques. Cela se fait en configurant _IMAGE_RESPONSIVE_CALCULER ainsi :

  1. define("_IMAGE_RESPONSIVE_CALCULER", true);

Une fois les images calculées, le serveur Web se retrouve à gérer des fichiers d’images directement, sans nécessiter aucun script PHP. En revanche, il y a deux inconvénients : (a) le temps de calcul initial de la page est nettement allongé, parce qu’il faut pré-calculer toutes les versions des images, (b) on pré-calcule toutes les tailles prévues dans le script, plutôt que d’attendre qu’elles soient réellement appelées par un visiteur. On fabrique ainsi beaucoup plus d’images dans le cache de SPIP, possiblement pour des tailles d’images qui ne sont pas réellement utilisées en pratique.

Créer des liens vers les fichiers d’images

Lorsqu’on veut aspirer le site Web (pour une installation en local, pour fabriquer une application…), les outils qui réalisent cette opération (tel wget) ont tendance à ne pas bien comprendre les codes HTML5 les plus récents, tels qu’ils sont fabriqués par ce plugin (<picture>, <source>, srcset…), et n’« aspirent » alors pas tous les fichiers d’images nécessaires.

Pour ajouter au code source de la page des liens que ces outils seront capables de « suivre » pour télécharger tous les fichiers nécessaires, on peut définir la variable suivante :

  1. define("_SPIP_LIER_RESSOURCES", true);

Des balises <link href="…" ' rel='attachment' property='url'> seront alors insérées dans le code de la page.

Et pour des images en background ?

One more thing…

Il y a une autre fonction, qui permet de gérer des images responsive en background d’un élément. Par exemple cet exemple utilisé sur les site des Arts décoratifs :

  1. <div class="logo">
  2. <a
  3.      href="#URL_RUBRIQUE"
  4.      [(#LOGO_RUBRIQUE_SURVOL|background_responsive{240/480/600/960})]>
  5. </a>
  6. </div>

Télécharger

Il n’y a donc pas ici de <img> : l’image responsive s’installera en fond (background) de la balise <a> et couvrira tout le conteneur (quitte, donc, à « déborder » de manière invisible du bloc).

C’est d’un usage nettement plus spécifique que les images responsive « normales », mais ça peut être intéressant pour plusieurs raisons :

  • si le conteneur (ici <a>) est plus haut que large (par exemple il occupe toute la taille d’un écran de smartphone vertical), alors l’image est recadrée verticalement avant d’être affichée ; cela permet d’éviter d’avoir une image de logo horizontale, affichée dans une boîte verticale (par exemple : l’écran), avec la moitié de l’image n’est donc pas affichée à l’écran). De cette façon, on optimise la taille de l’image non seulement selon la taille de l’écran, mais aussi selon l’orientation du bloc qui la contient ;
  • travailler avec une image en fond permet de réaliser des fonds en positionnement fixed, ce qui est indispensable avec certains effets de « parallaxe ».

En revanche, ici, on a le droit de passer en lazy load, mais rien d’autre. Pas d’option d’alignement vertical (c’est inutile, puisque le plugin va charger l’image pour qu’elle couvre toute la boîte horizontalement et verticalement), ni surtout de balisage HTML avancé.

Format WebP

Puisque image_responsive fabrique un beau code HTML à base de <picture> contenant éventuellement des <source>, le plugin est capable de proposer de manière transparente aux navigateurs (Chrome et Firefox pour l’instant) qui gèrent ce format, les versions des images en WebP.

Pour cela, il faut que votre version de PHP soit capable de fabriquer des images au format WebP (c’est désormais généralement le cas), et vous devez définir dans mes_options.php une nouvelle constante :

  1. define("_IMAGE_WEBP", true);

(Cette nouvelle fonctionnalité est introduite avec la version 7.7 du plugin. En cas de mise à jour d’une version plus ancienne, pensez à mettre à jour votre fichier .htaccess avec une nouvelle ligne supplémentaire.

Correction du bruit dans la transparence

Un bug rare sur certains serveurs ajoute un très léger bruit dans les zones de transparence des images PNG et WebP. Et ça peut se voir.

Si cela arrive, on peut activer une correction systématique des transparences des images PNG et WebP, ainsi :

  1. define("_IMAGE_CORRIGER_BRUIT_TRANSPARENT", true);
Qui êtes-vous ?
Votre message

Ce formulaire accepte les raccourcis SPIP [->url] {{gras}} {italique} <quote> <code> et le code HTML <q> <del> <ins>. Pour créer des paragraphes, laissez simplement des lignes vides.