Guide ultime des images responsives & retina ready

La « technologie » Retina, si on peut l’appeler ainsi, consiste en une densité de pixels assez élevée pour que l’œil (d’où le nom de retina) ne puisse discerner les pixels sur l’écran. Cependant, avec une telle densité, pour ne pas que tout paraisse minuscule, la valeur en pixel de chaque élément est doublée. Concrètement, cela signifie que dans votre feuille CSS, tout élément que vous définissez, par exemple, avec une width: 100px; fera en réalité 200px. Tout est multiplié par deux.

À de telles résolutions, autant dire que vos images peuvent vite paraître pour le moins pixelisées. Il faut donc penser aux différentes tailles d’écrans, mais aussi aux différentes densités d’écans. Un 22″ HD (1920×1080) n’aura pas besoin de la même qualité d’image qu’un 22″ en 4k (4096×2016). Voyons comment penser nos images pour qu’elles soient éclatantes en toute situation.

Définition et résolution

Commençons par clarifier deux notions qui sont très souvent source de confusion. La définition d’une image numérique fait référence à ses dimensions en pixels, soit 1000x1000px, 1920×1080 etc. C’est simplement le nombre de pixels en longueur fois le nombre de pixels en largeur. L’opération donne le nombre total de pixels que contient l’image.

Quant à la résolution, elle concerne la densité d’information d’une image (ou d’un écran). Ainsi, pour une taille donnée, une image peut avoir plus ou moins d’informations. On parle généralement de ppi ou ppp en fançais pour les images numériques et de dpi ou ppp en français pour l’impression.

Dans le cas du numérique, on compte le nombre de pixels par pouces, tandis que dans l’imprimerie, il s’agit du nombre de points (les petites goutes d’encre) par pouce.

Ainsi, une image de 1000x1000px à 100dpi fera 10″x10″ (25,4cm). Si l’on veut que la même image possède une résolution de 300dpi, cette dernière devra être affichée sur une surface de 3,33×3,33″ (8,5cm). Si l’on veut conserver la même taille physique d’image pour une résolution de 300dpi, alors notre image source devra être de 3000x3000px.

On comprend donc que la résolution est un rapport entre la définition et la dimension qu’occupe l’image. C’est pourquoi, sur les supports numériques, parler de la résolution d’une image n’a que peu de sens. Une image de 300x300px affichée sur un écran sera affichée à la taille de 300x300px, le ratio est donc de 1:1.

Et le rétina fut

Il y a une petite exception à ce que je viens de dire plus haut : les écrans à haute densité – plus communément appelés rétina. Ce type d’écran possède une densité de pixels très importante. À tel point que l’œil ne peut plus discerner les points des pixels (d’où le nom de rétina…).

illustration humoristique des écrans retina d'Apple

Retina est une marque d’Apple, la majeure partie des fabricants proposent aujourd’hui des écrans à haute densité de pixels. Le terme rétina est utilisé dans l’usage courant de façon générique pour désigner les écrans à haute résolution.

Il n’y a pas de norme particulière permettant de définir qu’à partir d’une certaine densité de pixels par pouce, un écran est rétina ou non. À partir d’une certaine distance, tout écran devient rétina. C’est pourquoi, les écrans de téléphone – que l’on regarde de près – nécessitent en général une densité plus importante que ceux des ordinateurs.

Cela a pu varier selon les époques et les constructeurs, mais traditionnellement, les écrans présentaient une résolution de 96dpi. Ainsi, le rétina correspond à 2×96 soit une résolution de 192dpi, mais il existe des densités bien plus importantes.

Avec ce type d’écran, si on maintenait un ration de 1:1, tout serait tout petit. Donc pour conserver des proportions raisonnables, tout est affiché avec un ratio de 2:1 ou plus. Le résultat ? Une image de 1000x1000px est affichée comme une image de 2000x2000px. Ainsi, plusieurs pixels physiques de l’écran affichent le même pixel de l’image, d’où la perte visible de qualité.

Pour un résultat optimal, il faudrait fournir une image de 2000x2000px à afficher en 1000x1000px. Comme ça, chaque pixel de l’image correspond à un pixel de l’écran. Évidemment, pour des écrans à densité encore plus importante, il faut des images d’encore meilleure qualité.

Les formats d’images

À chaque situation sa solution. Il y a d’innombrable formats d’images, entre les images vectorielles et matricielles, voyons la meilleure option pour chaque cas d’usage.

Les vecteurs, parés en toute circonstance

Évidemment, une image vectorielle fait fi des notions de pixels. Elle est vectorielle, elle ne contient donc pas un ensemble de points à afficher, mais plutôt les coordonnées géométriques des formes qu’il faut rendre. Ainsi, elle se redimensionne à l’infini. Donc qu’il s’agisse de densité ou de taille, un seul SVG toutes les configurations.

Pour tout ce qui est logo et image créé avec des outils tels qu’Illustrator ou Inkscape, n’hésitez pas, utiliser le SVG ! C’est léger, très bien supporté par les navigateurs et ce sera toujours d’une parfaite qualité. Gardez aussi à l’esprit que les différents logiciels d’édition n’optimisent pas forcément les fichiers SVG. Avant de les mettre en ligne, je vous conseille de leur faire subir une petite cure d’amincissement.

L’inconvénient, c’est que le SVG n’est pas adapté pour les photos par exemple. Traduire un paysage ou un portrait en éléments vectoriels revient à faire un fichier extrêmement lourd ou à perdre un nombre considérable de détails. Dans ces cas là, pas le choix, il faudra se replier sur les formats matriciels.

Les formats bitmap

Les images raster, bitmap ou encore matricielles en français, sont celles qui contiennent les informations des pixels, par opposition aux images vectorielles. Il existe des centaines de format. Nous ne verrons que ceux utilisable sur le web.

Vous connaissez certainement les trois formats les plus populaires et nous allons en introduire une quatrième :

gif

C’est un format lossless, la qualité reste constante, il permet la transparence (d’une couleur seulement), l’animation et fonctionne avec une palette de 256 couleurs. Il est donc à utiliser pour les images aux formes géométriques principalement. Entre le svg et le png, sauf pour les animations, il n’est aujourd’hui plus très concurrentiel.

png

Il s’agit aussi d’un format lossless et il existe en deux versions : le png8 (pour 8 bits) qui embarque une palette de 256 couleurs ; et le png24 (pour 24 bits) qui fonctionne en mode couleur directe et couvre plusieurs millions de couleurs. Étant donné qu’il est lossless, les images présentant des forme clairement définies profiteront de la netteté du png. Il aura de bons résultats en terme de compression sur des images présentant de larges zones de couleurs homogènes. De manière générale cependant, il est bien plus volumineux que le jpeg.

jpeg

C’est le seul des droits formats à être lossy. C’est à dire que la compression du jpeg élimine des informations de l’image. On perd des détails. De ce fait, c’est aussi le format qui fournit les fichiers les plus légers. On peut définir la qualité de compression du jpeg. Plus la qualité est basse, plus la compression sera agressive et plus on perdra en détails. Le jpeg est très adapté aux images avec de nombreux détails et du bruit. De par le fonctionnement de son algorithme de compression, on constatera rapidement une altération de la qualité au niveau des dégradés de couleurs et des formes précises (texte par exemple) dès lors que le niveau de compression est un tant soit peu élevé.

webp

Il s’agit d’un format open source créé par Google avec et sans perte, qui supporte la transparence, les animations et fonctionne en mode de couleur directe. À qualité égale, il offre une taille inférieur de 20 à 40% au png dans son mode lossless et au jpeg dans son mode lossy. Là où le bas blesse, c’est que le support navigateur est encore très faible

En résumé, dans la plupart des cas pour la photo, tentez le jpeg. S’il y a de gros aplat de couleur où des formes géométriques très précises qui « bavent » un peu en jpeg, tentez le png. Pour le webP, nous verrons qu’il est possible de l’utiliser en complément des autres formats dans certains cas. Il ne pourra cependant pas faire cavalier seul pour le moment.

Place au code

Assez discuté théorie, voyons ce que l’on peut faire concrètement !

Les images en CSS

Une partie non négligeable des images utilisées sur nos sites le sont à des fins décoratives. À cet effet, il arrive bien souvent qu’elles soient gérées par CSS via la propriété background.

.header {
  // image de 768px de large
  background-image: url('/img/meeting-s.jpg');

  // l'image est centrée et recouvre intégralement la zone
  background-position: center;
  background-size: cover;
}

@media (min-width: 769px) {
  .header {
    // image de 992px de large
    background-image: url('/img/meeting-m.jpg');
  }
}

@media (min-width: 992px) {
  .header {
    // image de 1200px de large
    background-image: url('/img/meeting-l.jpg');
  }
}

@media (min-width: 1200px) {
  .header {
    // image de 2500px de large
    background-image: url('/img/meeting-xl.jpg');
  }
}

On a là un exemple classique d’une image de fond gérée en CSS et qui répond à différentes tailles d’écrans. On utilise par défaut une petite image puis, grâce aux media queries, en fonction des tailles d’écrans (respectivement > 768, 991 et 1200px), on utilise une image de plus grande définition pour qu’elle corresponde à la largeur de l’écran.

Les média queries permettent également de cibler les résolutions d’écrans. C’est en outre très bien supporté par les navigateurs. Il n’y a donc plus qu’à adapter notre scénario pour qu’il utilise une image adéquate en fonction de la définition et de la résolution de notre écran !

.header {
  // image de 768px de large
  background-image: url('/img/meeting-s.jpg');

  // l'image est centrée et recouvre intégralement la zone
  background-position: center;
  background-size: cover;
}

@media (min-resolution: 192dpi) {
  .header {
    // image 2x plus grande : 1536px de large
    background-image: url('/img/meeting-s@2x.jpg');
  }
}

@media (min-width: 769px) {
  .header {
    // image de 992px de large
    background-image: url('/img/meeting-m.jpg');
  }
}

@media (min-width: 769px) and (min-resolution: 192dpi) {
  .header {
    // image de 1984px de large
    background-image: url('/img/meeting-m@2x.jpg');
  }
}

@media (min-width: 992px) {
  .header {
    // image de 1200px de large
    background-image: url('/img/meeting-l.jpg');
  }
}

@media (min-width: 992px) and (min-resolution: 192dpi) {
  .header {
    // image de 2400px de large
    background-image: url('/img/meeting-l@2x.jpg');
  }
}

@media (min-width: 1200px) {
  .header {
    // image de 2500px de large
    background-image: url('/img/meeting-xl.jpg');
  }
}

@media (min-width: 1200px) and (min-resolution: 192dpi) {
  .header {
    // image de 5000px de large
    background-image: url('/img/meeting-xl@2x.jpg');
  }
}

Dans cet exemple, nous ne prévoyons que les écrans à densité classique et les 2x, soit 192dpi et plus. Cependant, vous pouvez prévoir différents intermédiaires, avec notamment le 1,5x pour les écrans à 120dpi et les 3x pour les écrans de 300dpi et plus (les smartphones récents par exemple). Certains smartphone comme la gamme des « + » d’Apple (6+ etc) ainsi que l’iPhone 10 ont même des résolutions de plus de 400dpi.

Vous n’avez rien de plus à faire si le background-size est fixé à cover ou contain. En revanche, si votre arrière plan possédait « juste la bonne taille », il faudra maintenant forcer celle-ci avec background-size pour que l’image apparaisse aux bonnes dimensions et dans la résolution supérieure. Sans quoi elle sera simplement deux fois plus grande à l’écran mais sans gain de qualité.

Les images en HTML

Jusqu’à présent, nous ne disposions que de la balise image classique <img src="…"> pour inclure des images dans nos pages. C’était quelque peu limité. Nous utilisions alors des hacks en détectant la résolution de l’écran via JavaScript et en injectant la source de l’image à posteriori. Tout cela est révolu car le HTML nous a finalement doté des bons outils.

Cette nouvelle syntaxe permet de répondre à quatre besoins différents :

  • servir des images responsives (différentes tailles),
  • servir des images adaptées à la densité de l’écran,
  • servir des images dans différents format (hello webP),
  • servir différents types d’images selon des règles contextuelles (image recadrée sur petit écran par ex.).

Nous utiliserons respectivement les termes taille, dpi, mime et art pour les quatre besoins mentionnés ci-dessus.

Nous verrons l’introduction de deux nouvelles balises. Pour les anciens navigateurs qui ne les supportent pas, aucun problème. Il y a dans tous les cas la balise img qui servira de fallback, le reste sera ignoré. Quoi qu’il en soit, soyez rassuré, car aussi bien picture que source sont très bien supportées.

Dans les cas où les navigateurs prennent en compte les nouvelles balises et attributs, celles-ci écrasent les valeurs définient dans la src de img.

Utilisation d’images de différentes tailles

Il s’agit là simplement de faire du responsive classique. Si l’écran est plus grand, on propose au navigateur une image différente.

<img
  src="buzut-400.jpg" alt="Buzut AFK IRL"
  sizes="100vw"
  srcset="buzut-200.jpg 200w, 
          buzut-400.jpg 400w,
          buzut-800.jpg 800w,
	  buzut-1200.jpg 1200w">

On voit que la balise img gagne quelques propriétés. Ici, sizes n’a qu’une valeur et indique que l’image devra faire 100% de la largeur du viewport. Le navigateur peut donc choisir entre une image de 200, 400, 800 ou 1200px de large.

<img
  src="buzut-400.jpg" alt="Buzut AFK IRL"
  sizes="(min-width: 640px) 60vw, 100vw"
  srcset="buzut-200.jpg 200w, 
          buzut-400.jpg 400w,
          buzut-800.jpg 800w,
	  buzut-1200.jpg 1200w">

Dans cet exemple, on donne au navigateur une instruction supplémentaire grâce à la media query. Si la largeur de la fenêtre du navigateur est égale ou supérieure à 640px, l’image ne fera que 60% de la largeur du viewport, sinon elle fera 100%. Les images proposées sont les mêmes que dans l’exemple précédent. Le navigateur choisit celle qui lui convient le mieux.

Utilisation d’images haute résolution

Step numéro deux du responsive, comme nous l’avons vu plus haut en CSS, il s’agit de définir des images pour les écrans rétina.

<img
  src="buzut-1x.jpg" alt="Buzut AFK IRL"
  srcset="buzut-2x.jpg 2x, buzut-3x.jpg 3x">

Ici, on propose au navigateur une image par défaut, une autre correspondant au double de la définition et enfin une troisième correspondant au triple. Selon l’écran utilisé, le navigateur choisira la plus appropriée.

Utilisation de différents formats

Pour ce cas d’usage, deux nouvelles balises font leur apparition. Il s’agit de source et de picture. La première permet de spécifier différents médias accompagnés des éléments qui permettent au navigateur de décider ou non de l’utiliser. La seconde permet de regrouper les balises source et media.

<picture>
  <source
    srcset="buzut.webp"
    type="image/webp">

    <img src="buzut.jpg" alt="Buzut AFK IRL">
</picture>

Utilisation d’images différentes selon le contexte

<picture>
  <source
  media="(min-width: 1024px)"
  srcset="buzut-paysage.jpg">
	
  <img
    src="buzut-portrait.jpg" alt="Buzut AFK IRL">
</picture>

On fournit ici par défaut une image en gros plan. Si le navigateur fait au moins 1024px de large, on spécifie à ce dernier d’utiliser l’image en format panoramique.

On mixe le tout

On y va maintenant franco en utilisant ensemble les quatre possibilités vues précédemment.

<picture>
  <source
    media="(min-width: 1280px)"
    sizes="50vw"
    srcset="buzut-paysage-200.webp 200w,
    		buzut-paysage-400.webp 400w,
    		buzut-paysage-800.webp 800w,
    		buzut-paysage-1200.webp 1200w,
    		buzut-paysage-1600.webp 1600w,
    		buzut-paysage-2000.webp 2000w"
    type="image/webp">
  <source
    sizes="(min-width: 640px) 60vw, 100vw"
    srcset="buzut-portrait-200.webp 200w,
    		buzut-portrait-400.webp 400w,
    		buzut-portrait-800.webp 800w,
    		buzut-portrait-1200.webp 1200w,
    		buzut-portrait-1600.webp 1600w,
    		buzut-portrait-2000.webp 2000w"
    type="image/webp">
  <source
    media="(min-width: 1280px)"
    sizes="50vw"
    srcset="buzut-paysage-200.jpg 200w,
    		buzut-paysage-400.jpg 400w,
    		buzut-paysage-800.jpg 800w,
    		buzut-paysage-1200.jpg 1200w,
    		buzut-paysage-1600.jpg 1800w,
    		buzut-paysage-2000.jpg 2000w">
  <img
    src="buzut-portrait-400.jpg" alt="Buzut AFK IRL"
    sizes="(min-width: 640px) 60vw, 100vw"
    srcset="buzut-portrait-200.jpg 200w,
    		buzut-portrait-400.jpg 400w,
    		buzut-portrait-800.jpg 800w,
    		buzut-portrait-1200.jpg 1200w,
    		buzut-portrait-1600.jpg 1600w,
    		buzut-portrait-2000.jpg 2000w">
</picture>

On résume tout ça ? Pour les navigateurs larges d’au moins 1280 pixels, la photo ne fera que 50% du viewport et on utilise la photo en mode panorama. Pour les navigateurs intermédiaires – entre 640 et 1279px – la même photo est utilisée mais elle fera 60% du viewport et pour les navigateurs encore plus petits, elle fera 100%. Dans chacun des cas, on propose au navigateur de choisir parmi différentes tailles d’images.

Lorsque les largeurs des images sont précisées, on n’utilise pas les notations nx. Le navigateur peut faire les maths lui-même. Enfin, si le navigateur supporte webP, il utilisera une image dans ce format.

Voilà, nous avons fait le tour de tout ce qu’il faut savoir sur le responsive et le retina ! Des considérations matérielles au choix de la meilleur image en HTML ou CSS en passant par les différents formats, vous devriez être incollable. Et vos sites devront être parfaits en toutes situations.

Déjà 2 réponses, rejoignez la discussion !

  • Noname

    dit :

    Je crois que vous avez voulu écrire « Quant à la définition […] » au lieu de « Quant à la résolution […] » puisque vous avez expliqué la résolution dans le paragraphe précédent.

    • Buzut

      dit :

      Je voulais bien dire « résolution », c’est dans le paragraphe d’avant que j’avais inversé ! Merci d’avoir porté cette incohérence à mon attention :)

Laisser un commentaire

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