Images SVG inline avec Ruby on Rails & Middleman

Automatiser l’intégration de vos images avec un helper

Helper pour les images SVG avec Ruby on Rails (ou Middleman)

J’utilise de plus en plus des images SVG sur mes projets web. Il y a 2 principaux avantages à utiliser ce type d’images :

  • les Scalable Vector Graphics (en français “graphique vectoriel adaptable”), sont par définition compatible avec les écrans HD.
  • Ce sont des fichiers textes qui sont intégrés directement dans le HTML, vous gagnez en performance puisqu’il n’y a pas de requête http supplémentaire.

Le principal problème est que cela “pollue” nos partials. Dans un contexte d’application Ruby on Rails ou Middleman, un simple appel avec une ligne image_tag se transforme en centaines de lignes avec des glyph unicode suivies de coordonnées.

Voici 2 solutions pour résoudre ce problème.

Méthode 1 : Quick’n’dirty

Les SVG sont des fichiers textes basés sur du .xml, nous pouvons les inclures dans nos fichiers .html. Dans notre application Rails ou Middleman le hack est d’inclure des partials ou vues qui contiennent notre image.

Pour faire ceci, commencez par renommer tous votres fichiers SVG en prefixant par un underscore et en ajoutant l’extention de votre langage de template.

Exemple : logo.svg se transforme en _logo.html.erb

Ensuite nous devons le ranger au bon endroit.

  • Avec Rails app/views/svg/_logo.html.erb
  • Avec Middleman source/svg/_logo.html.erb

Ensuite, vous pouvez ajouter une image simplement avec un partial.

Ruby on Rails

  <%= render :partial => 'svg/logo' %>

Middleman

  <%= partial 'svg/logo' %>

Boom ! sh$t done !

Méthode 2 : Rendu dynamique

La première solution fonctionne, mais ce n’est pas propre et comme je l’ai précisé : c’est un hack. Les images ne respectent plus l’architecture app/assets/images (Rails) ou source/images (Middleman). De plus nous devons renommer toutes les images. Ça ne change pas le comportement, mais ma santé mentale n’apprécie pas trop.

Il existe une méthode plus intelligente.

Au lieu d’utiliser un partial, il serait beaucoup plus judicieux d’utiliser un helper.

Ruby on Rails

  def svg(name)
    file_path = "#{Rails.root}/app/assets/images/#{name}.svg"
    return File.read(file_path).html_safe if File.exists?(file_path)
    '(not found)'
  end

Middleman

  def svg(name)
    root = Middleman::Application.root
    file_path = "#{root}/source/images/#{name}.svg"
    return File.read(file_path) if File.exists?(file_path)
    '(not found)'
  end

Le helper cherche le chemin de l’image, dès qu’il le trouve il retourne le résultat au format texte. Pour éviter les erreurs on affiche (not found) au lieu de provoquer une erreur.

Pour afficher une image c’est aussi simple que cela :

  <%= svg 'logo' %>

Chargement plus rapide

Pour aller plus loin vous pouvez ajouter une stratégie de cache afin d’améliorer la performance, il est facile de mettre en place le fragment caching de Rails.

Rails

  def svg(name)
    file_path = "#{Rails.root}/app/assets/images/#{name}.svg"
    if File.exists?(file_path)
      cache { File.read(file_path).html_safe }
    else
      '(not found)'
    end
  end

Je ne suis pas sur du comportement mais je pense qu’après chaque deploy en production vous devrez faire un Rails.cache.clear.

⚠ Attention

Prenez garde, dans certains cas utiliser des SVG ralenti votre page comparé à des images .png. Je n’ai personellement pas de chiffres à proposer mais GitHub a publié quelques données suite à leur abandon des font icon pour des images SVG.

Benchmark performance images SVG Benchmark avant / après l’utilisation de SVG

N’utilisez jamais des images SVG pour des images complexes. Dans cet exemple les dégradés, ombres portées, … alourdissent l’image.

Choix pour une image svg Comparaison des types d’images

Ressources

Helper pour les images SVG avec Ruby on Rails (ou Middleman)