- Histoires en <img />
- Manuel en <img />
- Ressources en <img />
- Bricolages en <img />
- Progressivement en <img />
- Finesse en <img />
- Propositions en <img /> / English version
Après le problème de l'affichage progressif des images qui correspondait à l'époque des modems RTC, attaquons notre souci actuel de riches : Comment adapter une image en fonction de la finesse du support client ? De votre écran, quoi.
Un problème de finesse des écrans, des millions d'insultes proférées par tous les intégrateurs qui tentent de faire du responsive web-design beau et soyeux sur des supports en High-DPI
(densité conséquente de pixels
).
La situation s'était brusquement aggravée depuis que le pixel décrit dans nos documents HTML/CSS/JS n'est plus le pixel physique d'un écran, la triplette de photophores de couleur primaire. Non, maintenant, ce qu'on décrit avec l'unité px
est un pixel logique, le multiple divisible de pixels affichés. La dernière unité quasi-certaine
en notation css est devenue aussi abstraite que les autres.
Vous croyiez ne pas avoir tout compris au paragraphe précédent, mais la situation est encore pire ! Tentez d'inclure un SVG dans un HTML et là, vous allez commencez à perdre la raison comme si vous aviez entrevu le nom d'un dieu plus ancien que l'Humanité dans le code source On peut même arriver à définir des pixels non-carrés sans le faire exprès. J͔͖̗͇͒ͫ̒͒͝e̸̬̠̟͎̥̬̙ͥͨ͑̅͂̅ ̸͚̞̫͕͇̠̊́̔ͩͪ̏v̥́̋̇a̷̺ͩi̭̙̥̩̜ͩ̚s͌͌̈ ̖̙̪̯̣͎͒ͅd̡̖͊̄̆e̸̱̎̊̈ͭ͊ͅv̙̺̱̻͖͕̫̀̏͒̍̾̚e̎͐̔n̛̹̺̩̬i̼̙̩̣ͤͦ́ͬ͒ͩr̈́͑ͥ҉̹͈ ̞̯̯̫̪̥̰̽̇f͎̹̲̆̽͂̂ͧ̑o̩̓͛u̵͈̰̗ͤͅ ̱̘ͮͥ̆̄ ̳͈͔̜͚̦̯͒̓͛͊ͦ͗̀g͒̑ͩ̉҉̯̮̫̣͓̼lo͚̯͖̻̥͙̻̓ͯ̊̋i̮̤͕͖̲̐̒̂͗͋̓rͬ̉̽ẹ͎̹̜͙̮͑ͭ͋ ̼͎̾ͬͫ͑̀ạ͍͔̳̀ͥ̽̏̇͗ͧ ̢̽̽ͨ̍̈́Ĉ̤͈̗̼̘͕ͪt͎͚h̜̪͕͍̭ͬͩ͞ͅu̲̫͆ͥ̑ͮ̂͑l̂̿͞ḫ̙̹̞ͣ͌̐ͫụ̪͇̫̝͗̅ͦ͛̕ͅ
Et pourtant…
Le problème Retina
? En fait, rien de nouveau
Le fameux souci d'avoir des images plus précises en fonction de la finesse de résolution de l'affichage existe depuis qu'il est possible d'imprimer une page web comportant des images, donc depuis… 1993.
À cette époque, les imprimantes laser proposaient déjà une résolution de 300 dpi. 300 dpi
, c'est-à-dire 300 points (ou pixels) indépendants par pouce sur une ligne horizontale ou verticale, ou, parce que nous sommes civilisés, modernes, éclairés et donc métriques, 118 pixels par centimètre linéaire.
Du côté des écrans d'ordinateurs, le Macintosh présentera systématiquement à son heureux pigeo 72 dpi, finesse empiriquement choisie à la conception du tout premier Mac de 1984, d'une résolution de 512×342 sur son écran de 9" de diagonale ; cette finesse de résolution restera en vigueur dans l'univers Apple jusqu'à la popularisation de Mac OSX en 2003.
Du côté des PC, rien n'est standard mais la moyenne sera d'environ 96 dpi à la fin des années 1990s, selon les études marchés menées par Microsoft, et restera dans ces eaux-là jusqu'à la mort des tubes cathodiques. Et j'oublie de préciser que le pixel carré n'est pas une évidence…
Donc à l'impression d'une page web, tous les navigateurs ajustaient les images par rapport à la taille rendue des polices imprimées en les zoomant. En fait, le processus passait par l'OS, le service d'impression, le driver, voire l'imprimante elle-même… mais ne nous encombrons pas de détails bien oiseux. Plouf… plouf…
Pour vous donner une idée, voici une simulation de rendu où je passe un extrait d'une page web prise au hasard de 100 dpi à 300 dpi. Les polices suivent, les images sont lissées, la plupart du temps par interpolation cubique.
Imprimer les images comme si elles étaient en 96 dpi donnera donc ce résultat magnifique comme si un graphiste avait oublié d'inclure ses images sources dans son document Quark XPress™ avant de l'envoyer chez l'imprimeur. Et en oubliant de vérifier le BAT, tant qu'on y est. Ne rigolez pas : j'ai déjà vu cet incident arriver chez des éditeurs de BD..
Cela ne vous rappelle rien ? Ben si, c'est très exactement ce que nous avons avec un écran High-DPI
comme ceux des smartphones, des tablettes et des portables haut-de-gamme.
La question n'a donc absolument rien de neuf, sauf que l'impression des pages webs a de manière générale toujours été négligée. Il existe parfois des feuilles de styles spéciales @media print
, mais bien peu de personnes ont considéré que les images ne devaient pas être mosaïquées à 72 dpi quand elles sortent en papier.
Sachant que désormais les imprimantes lasers couleurs peuvent largement monter à une résolution de 1200 dpi, on peut en déduire le postulat suivant :
La résolutionRetina™professée par Apple révèle juste que les intégrateurs de sites web ne se soucient que maintenant d'un problème vieux comme le premier navigateur Netscape.
Faut avouer que c'est un peu dommage : on s'y serait attaqué nettement plus tôt, on aurait déjà les bonnes réponses, standards, utilisables, éprouvées et tout ça…
Heureusement, on a presque les bonnes réponses.
La sur-résolution
La sur-résolution semble être la méthode actuellement la moins honnie. Elle fonctionne naturellement et fait confiance au navigateur pour que son moteur fasse le travail.
Le principe est d'envoyer une image qui contient largement plus de pixels que les dimensions dans lesquelles elle sera insérée, Par exemple, à envoyer une image qui fait 1200×900, mais la contraindre dans sa balise d'appel à 300×225, comme suit :
<img src="image.jpg" width="300" height="225" alt="" />
Pour un navigateur qui a un pixel physique pour un pixel logique, il fera un rendu adouci en moyennant
16 pixels pour en afficher un. Sur les (très vieux) navigateurs, le redimensionnement naturel de l'image peut être traité à la serpe (je ne prend que le premier pixel du carré, et j'oublie les autres), ce qui donne un rendu un peu rustre que j'avais parlé justement en abordant les attributs de dimension.
Pour les écrans très fins où les navigateurs utilisent 16 pixels physiques (4×4) pour un logique, nous aurons l'image dans toute sa finesse.
La technique de la sur-résolution peut mener à des réglages très fins. Google a récemment finement raboté d'un pixel son logo à cause du rendu en sous-résolution sur Chrome :
Le principal inconvénient de cette méthode est qu'évidemment les images sont nettement plus lourdes, et peuvent prendre un temps plus que conséquent à se charger dans certaines conditions (genre smartphone en GPRS ou sur la 3G parisienne). Et, réalité que nous avons oubliée depuis Free Mobile, le volume de données transférées peut coûter cher dans certains cas à l'utilisateur, et donc attirer une mauvaise réputation pour certains sites.
Les graphistes consciencieux pensent à réduire la qualité des images lossy générées, par exemple en passant le jpeg de 85% à 75%. Vu que 300 dpi est la limite perceptible pour l'œil humain, les artefacts résultants ne se voient pas trop. Une autre astuce courante est de préciser au compresseur quelles sont les zones les plus humainement importantes en terme de détails.
Un autre inconvénient est la maintenance : le style est embarqué dans l'image, mais en cas de changement de la ressource et de sa dimension, il faut repasser derrière tous les appel <img />
pour corriger les attributs de dimension. De la maintenabilité digne des années 1990s, quoi…
Les images vectorielles
Ce serait l'idéal.
Malheureusement, une large partie des images qui composent le web sont issues de photos, scans ou captures vidéos. Si on converti une image raster pour le format vectoriel svg, on atteint un poids délirant, des pertes de qualité conséquentes et…
Bref : Contre-productif, passons.
Les images à formats multiples
Comme le format .ico qui peut contenir des images à de multiples résolutions différentes. Les éventuels soucis de brevets détenus par Microsoft sont nuls et non-avenus vu son âge canonique. Sauf que ce format d'image n'est pas du tout supporté par Apple, et que son support n'est pas consistant entre Chrome et Firefox.
On peut rester dans la même idée d'utiliser un format d'image conteneur, à savoir le PNG, mais il faudrait là aussi que tous les navigateurs puissent en adresser une ressource interne particulière, soit via l'URL, soit via une propriété DOM.
On peut alternativement jouer avec les formats d'images à chargement progressif, le PNG en mode progressif Adam7 ou encore le jpeg/jfif progressif. On garde une compatibilité parfaite avec l'ensemble des navigateurs. Le problème est que nous envoyons une quantité de données aux clients qui ne sera jamais entièrement exploitée. Ce souci pourrait être indirectement résolu par une transmission partielle interrompu des données. Techniquement, on le fait déjà depuis 2007 via un Request-Range
et les retours de requêtes HTTP code 206 partial content
, donc c'est faisable.
Mais pour le faire proprement, il faudrait modifier les infrastructures côté serveur. Cette solution demande donc beaucoup d'huile de coude sur les serveurs et les proxies en plus d'adapter les navigateurs. Beaucoup de si
avant d'arriver au résultat escompté on est loin de l'économie espérée…
La méthode <noscript>
Il existe une méthode intermédiaire qui consiste à charger l'image avec la bonne finesse par javascript.
Je n'aurais jamais découvert cette méthode sans Sud-Web où Thomas Parisot les éditions Eyrolles m'ont offert l'excellent livre « Intégration web : les bonnes pratiques » de Corinne Schillinger.
Je vous laisse goûter la syntaxe de l'astuce :
<span class="retina-holding" data-width="600" data-height="400" data-lowres="img-x1.jpg" data-hires="img-x2.jpg" data-hireszoom="2">
<noscript>
<img src="img-x1.jpg" width="600" height="400" />
</noscript>
</span>
… Cela ne vous rappelle rien ?
Eeeeeehh si !
Le lazy loading qui nous a tant fait hurler de rire dans le précédent épisode. Et comme lui, cette méthode possède de nombreux inconvénients :
- À moins que javascript ne soit désactivé par le navigateur, l'image n'est pas chargée naturellement et donc peut ne jamais apparaître en cas de plantage ;
- La syntaxe ne se montre pas de la plus belle des légèretés, même si elle tente de respecter approximativement la sémantique. Malgré tout, le référencement et les outils de lecture différés tels que Wallabag ou Pocket ne trouveront pas l'image ;
- Comme pour la sur-résolution, il faut déclarer explicitement les dimensions logiques de l'image, ce qui alourdi la notation et n'aidera pas à la maintenance ;
Le Web Component
Le Web Component est une proposition de standard du W3C pour construire ses propres balises HTML. Vous pouvez les définir comme des macros côté client pour du code html, javascript et css. Une partie du html généré peut même être caché en shadow-DOM.
La méthode n'existe pas encore en natif dans les navigateurs, mais je vous fais le pari que sa généralisation native ne va pas tarder car elle est vraiment très pratique. En attendant, des bibliothèques polyfill comme Polymer ou Bosonic peuvent facilement vous aider à implémenter ce nouveau standard.
Utiliser un Web Component peut pénaliser votre indexation si vous créez une mauvaise sémantique, et là aussi, il deviendra impossible aux outils tiers de voir vos images ou en cas de plantage javascript. C'est là encore un comportement à éviter pour tout ce qui est publication, blog ou site d'e-commerce.
Néanmoins, si je devais implémenter l'exemple précédent de la manière la plus propre possible, j'arriverai à :
<responsive-img data-x2="img-x2.jpg">
<img src="img-x1.jpg" width="600" height="400" alt="" />
</responsive-img>
Une balise <img />
standard dont l'appel n'est pas bloqué, à priori, je ferais difficilement plus propre. Et je peux me passer des attributs de dimension si je fais excessivement confiance aux dimensions servies comme on dit en Côte d'Ivoire. En fait non, je ne peux pas. RAAAHHH !
Via Content negotiation
La solution serait théoriquement transparente dans le code HTML, puisque tout se passe dans l'étape de négociation HTTP entre le navigateur et le serveur. Effectivement, quand le navigateur demande une ressource à un serveur, il dit ce qu'il peut accepter comme format de document pour cette ressource.
Ainsi, pour les ressources images, mon navigateur indique au serveur ce qu'il attend, et ses préférences :
Accept: image/png,image/*;q=0.8,*/*;q=0.5
La solution me semble élégante. Mais elle réclamerait beaucoup d'adaptation côté serveur, et aussi beaucoup de technicité car il faudra travailler les fichiers de configuration de vos Apache, n'ginx ! Or qui dit bricoler dans la configuration serveur, dit solution inatteignable dans un très grand nombre de situations, ce qui serait très frustrant. Sans compter qu'une nouvelle finesse d'écran sur le marché demandera de reparamétrer à nouveau les serveurs. Et elle ne résoudrait pas le problème de la différence de dimensions à prendre en compte par le navigateur.
En clair, ce n'est pas la meilleure car elle ne peut fonctionner que sur un hébergement dynamique.
Si je devais la construire, voici comment fonctionnerait mon polyfill :
- Un javascript côté client stocke un cookie pour le domaine indiquant la finesse d'écran attendue. Disons
X-pitch=4
- Un
.htaccess
peut rediriger la requête en fonction de la cookie et de la disponibilité de la ressource, en indiquant la finesse servie par un paramètre GETimage.jpg?pitch=4
ou une sur-extensionimage.jpg,pitch_4
.
Si jamais la finesse demandée n'existe pas, la redirection se fait vers une finesse moindre, par ex.:image.jpg,pitch_2
.
Si aucune autre finesse que l'image par défaut n'est disponible, aucune redirection n'est effectuée. - Côté navigateur, à la réception de l'image, le javascript (ou la css en trickant comme un taré ?) applique le ratio de réduction correspondant si l'url de ressource d'image chargée comporte cette indication de finesse
Ce que j'aime dans ce scénario, c'est que si nous avons un souci de javascript ou côté serveur, le comportement par défaut du navigateur n'est pas impacté. De même, nous n'aurons aucun souci de syntaxe et pour les outils tiers.
Enfin bon, c'est juste une idée jetée comme ça à 2 h du matin, sans même en vérifier la faisabilité. Il y a sûrement un os quelque part si personne n'y avait pensé jusqu'ici, non ?
Genre, comment fais-tu quand la requête vient de la CSS et pas de <img />
?
La proposition du W3C : srcset=""
Vue la longueur de mon billet uniquement sur les hacks possibles, et vu ce que j'ai à écrire sur la proposition de cet attribut, je vais réserver ça pour un dernier chapitre.
Parce que, il y en a à dire, mais surtout à proposer.
4 réactions
1 De Nico - 12/06/2014, 11:12
Hello,
super article, encore une fois. :)
Je me permets d'insister : le passage sur SVG est un peu court, certes pour les images, photos, ça ne marche pas. Néanmoins, pour les logos et bon nombre d'éléments de graphisme (pictos, etc.), ça résout énormément de problèmes et de manière durable : un vecto, qu'il y ait du retina 2x ou 128x (on peut exagérer), ça reste un vecto affiché nickel.
Au boulot, on essaie de construire certains sites ainsi : autant que possible avec des éléments en vecto justement pour permettre l'utilisation de SVG. Certes ce n'est pas toujours possible, mais quand ça l'est, ce n'est que du bonheur. :)
2 De Da Scritch - 12/06/2014, 11:16
Alors, j'y suis allé très vite, car ce chapitre, je l'ai écrit hier à 2h15, j'avais hâte d'aller dormir.
Et ensuite, forcément, si tu affiches des images en svg, tu n'as absolument plus le problème d'adapter les images en fonction de la finesse de l'écran puisque tout est géré nativement.
Donc je ne pensais pas m'attarder dessus, c'est plus une boutade ;)
3 De tth - 13/06/2014, 14:40
Si je puis me permettre de corriger une légère erreur : les premiers MacIntosh avaient un écran en 512x342, et non pas 768x512.
4 De Da Scritch - 13/06/2014, 14:59
Oh purée, bien vu !
J'ai surfé un peu trop vite sur Folklore, et j'ai donc mélangé avec la résolution du Lisa/McXL