Construire des SVG adaptatifs avec, et CSS Media Queries

<symbol>
, <use>
et CSS Media Queries.J’ai beaucoup écrit récemment sur la façon dont je préparer et optimiser Code SVG à utiliser comme graphiques statiques ou dans animations. J’adore travailler avec SVG, mais il y a toujours eu quelque chose qui me dérange.
Pour illustrer comment je construis des SVG adaptatifs, j’ai sélectionné un épisode de Le spectacle de McGraw rapide appelé « Bow Wow Bandit», First Diffusion en 1959.

Dans ce document, un tirage rapide McGraw enrôle ses tabacs de sang pour sauver son acolyte Baba Looey. Comme la plupart des cartes de titre Hanna-Barbera de l’époque, l’œuvre d’art a été faite par Lawrence (Art) Goble.

Disons que j’ai conçu une scène SVG comme celle qui est basée sur Bow Wow Bandit, qui a un rapport d’aspect 16: 9 avec un viewBox
Taille de 1920 × 1080. Ce SVG évolue de haut en bas (l’indice est dans le nom), donc il a l’air net quand il est gigantesque et quand c’est minute.

Mais sur les petits écrans, le rapport d’aspect 16: 9 (démo en direct) pourrait ne pas être le meilleur format et l’image perd son impact. Parfois, une orientation de portrait, comme 3: 4, conviendrait mieux à la taille de l’écran.

Mais, c’est là que réside le problème, car il n’est pas facile de repositionner les éléments internes pour différentes tailles d’écran en utilisant simplement viewBox
. En effet viewBox
vous ne pouvez donc pas facilement changer leur disposition entre le bureau et le mobile. C’est un problème car les animations et l’interactivité reposent souvent sur les positions des éléments, qui se brisent lorsque viewBox
changements.

Mon défi était de servir une version 1080 × 1440 de Bow Wow Bandit sur des écrans plus petits et un autre en plus grand. Je voulais la position et la taille des éléments internes – comme Draw McGraw et ses tabacs Dawg – pour changer pour mieux adapter ces deux dispositions. Pour résoudre ce problème, j’ai expérimenté plusieurs alternatives.
Note: Pourquoi n’utilisons-nous pas seulement le <picture>
avec des SVG externes? Le <picture>
élément est génial pour les images réactives, mais cela ne fonctionne qu’avec des formats raster (comme JPEG ou WebP) et des fichiers SVG externes traités comme des images. Cela signifie que vous ne pouvez pas animer ou styliser des éléments internes à l’aide de CSS.
Montrer et cacher SVG
Le choix le plus évident était d’inclure deux SVG différents dans mon balisage, l’un pour les petits écrans, l’autre pour les plus grands, puis les afficher ou les masquer en utilisant CSS et requêtes médiatiques:
<svg id="svg-small" viewBox="0 0 1080 1440">
<!-- ... -->
</svg>
<svg id="svg-large" viewBox="0 0 1920 1080">
<!--... -->
</svg>
#svg-small { display: block; }
#svg-large { display: none; }
@media (min-width: 64rem) {
#svg-small { display: none; }
#svg-mobile { display: block; }
}
Mais en utilisant cette méthode, les deux versions SVG sont chargées, ce qui, lorsque les graphiques sont complexes, signifie télécharger beaucoup, beaucoup, beaucoup de code inutile.
Remplacement des SVG à l’aide de JavaScript
J’ai pensé à utiliser JavaScript pour échanger dans le plus grand SVG à un point d’arrêt spécifié:
if (window.matchMedia('(min-width: 64rem)').matches) {
svgContainer.innerHTML = desktopSVG;
} else {
svgContainer.innerHTML = mobileSVG;
}
Laissant de côté le fait que JavaScript serait désormais essentiel à la façon dont la conception est affichée, les deux SVG seraient généralement chargés de toute façon, ce qui ajoute la complexité DOM et le poids inutile. De plus, la maintenance devient un problème car il y a maintenant deux versions de l’œuvre d’art à maintenir, doubler le temps qu’il faudrait pour mettre à jour quelque chose d’aussi petit que la forme de la queue de Quick Draw.
La solution: une bibliothèque de symboles SVG et plusieurs utilisations
Rappelez-vous, mon objectif est de:
- Servir une version de Bow Wow Bandit sur des écrans plus petits,
- Servir une version différente à des écrans plus grands,
- Définissez mes œuvres d’art une seule fois (sèche), et
- Être capable de redimensionner et de repositionner les éléments.
Je n’en lis pas assez à ce sujet, mais le <symbol>
L’élément vous permet de définir des éléments SVG réutilisables qui peuvent être cachés et réutilisés pour améliorer la maintenabilité et réduire le ballonnement du code. Ils sont comme des composants pour SVG: créer une fois et utiliser partout où vous en avez besoin:
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="quick-draw-body" viewBox="0 0 620 700">
<g class="quick-draw-body">[…]</g>
</symbol>
<!-- ... -->
</svg>
<use href="#quick-draw-body" />
UN <symbol>
c’est comme stocker un personnage dans une bibliothèque. Je peux le référencer autant de fois que j’ai besoin, pour garder mon code cohérent et léger. En utilisant <use>
Éléments, je peux insérer le même symbole plusieurs fois, à différentes positions ou tailles, et même dans différents SVG.
Chaque <symbol>
doit avoir le sien viewBox
qui définit son système de coordonnées interne. Cela signifie prêter une attention particulière à la façon dont les éléments SVG sont exportés à partir d’applications comme Sketch.
Exportation pour les bases individuelles
J’ai déjà écrit Comment j’exporte des éléments en couches pour faciliter le travail avec eux. Ce processus est un peu différent lors de la création de symboles.

Normalement, j’exporterais tous mes éléments en utilisant viewBox
taille. Mais quand je crée un symbol
J’en ai besoin pour avoir son propre viewBox
.

J’exporte donc chaque élément en tant que SVG de taille individuelle, ce qui me donne les dimensions dont j’ai besoin pour convertir son contenu en un symbol
. Prenons le SVG du chapeau de McGraw de rapidement, qui a un viewBox
Taille de 294 × 182:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 294 182">
<!-- ... -->
</svg>
J’échange les balises SVG <symbol>
Et ajoutez ses illustrations à ma bibliothèque SVG:
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="quick-draw-hat" viewBox="0 0 294 182">
<g class="quick-draw-hat">[…]</g>
</symbol>
</svg>
Ensuite, je répète le processus pour tous les éléments restants de mon œuvre. Maintenant, si jamais j’ai besoin de mettre à jour l’un de mes symboles, les modifications seront automatiquement appliquées à toutes les instances qu’elle est utilisée.
En utilisant un <symbol>
Dans plusieurs SVG
Je voulais que mes éléments apparaissent dans les deux versions de Bow Wow Bandit, un arrangement pour les écrans plus petits et un arrangement alternatif pour les plus grands. Donc, je crée les deux SVG:
<svg class="svg-small" viewBox="0 0 1080 1440">
<!-- ... -->
</svg>
<svg class="svg-large" viewBox="0 0 1920 1080">
<!-- ... -->
</svg>
… Et insérez des liens vers mes symboles dans les deux:
<svg class="svg-small" viewBox="0 0 1080 1440">
<use href="#quick-draw-hat" />
</svg>
<svg class="svg-large" viewBox="0 0 1920 1080">
<use href="#quick-draw-hat" />
</svg>
Symboles de positionnement
Une fois que j’ai placé des symboles dans ma disposition en utilisant <use>
ma prochaine étape consiste à les positionner, ce qui est particulièrement important si je veux des dispositions alternatives pour différentes tailles d’écran. Les symboles se comportent comme <g>
groupes, donc je peux les évoluer et les déplacer en utilisant des attributs comme width
, height
et transform
:
<svg class="svg-small" viewBox="0 0 1080 1440">
<use href="#quick-draw-hat" width="294" height="182" transform="translate(-30,610)"/>
</svg>
<svg class="svg-large" viewBox="0 0 1920 1080">
<use href="#quick-draw-hat" width="294" height="182" transform="translate(350,270)"/>
</svg>
Je peux placer chacun <use>
élément indépendamment en utilisant transform
. Ceci est puissant car plutôt que de repositionner des éléments à l’intérieur de mes SVG, je déplace le <use>
références. Ma disposition interne reste propre et la taille du fichier reste petite car je ne reproduis pas les illustrations. Un navigateur ne le charge qu’une seule fois, ce qui réduit la bande passante et accélère le rendu de la page. Et parce que je fais toujours référence à la même chose symbol
leur apparence reste cohérente, quelle que soit la taille de l’écran.
Animé <use>
Éléments
Voici où les choses sont devenues difficiles. Je voulais animer des parties de mes personnages – comme le chapeau de Quick Draw inclinant et ses jambes qui donnent des coups de pied. Mais quand j’ai ajouté des animations CSS ciblant les éléments internes à l’intérieur d’un <symbol>
rien ne s’est passé.
Conseil: Vous pouvez animer le <use>
élément lui-même, mais pas des éléments à l’intérieur du <symbol>
. Si vous voulez que des pièces individuelles se déplacent, faites-en leurs propres symboles et animez chacun <use>
.
Il s’avère que vous ne pouvez pas styliser ou animer un <symbol>
parce que <use>
Crée des clones de dom ombre qui ne sont pas facilement ciblables. Donc, je devais être sournois. À l’intérieur de chacun <symbol>
Dans ma bibliothèque SVG, j’ai ajouté un <g>
élément autour de la partie que je voulais animer:
<symbol id="quick-draw-hat" viewBox="0 0 294 182">
<g class="quick-draw-hat">
<!-- ... -->
</g>
</symbol>
… Et l’a animé à l’aide d’un sélecteur de sous-chaîne d’attribut, ciblant le href
attribut du use
élément:
use[href="#quick-draw-hat"] {
animation-delay: 0.5s;
animation-direction: alternate;
animation-duration: 1s;
animation-iteration-count: infinite;
animation-name: hat-rock;
animation-timing-function: ease-in-out;
transform-origin: center bottom;
}
@keyframes hat-rock {
from { transform: rotate(-2deg); }
to { transform: rotate(2deg); } }
Une fois que j’ai créé mes deux SVG visibles – un pour les petits écrans et un pour les plus grands – la dernière étape consiste à décider de la version à afficher à quelle taille d’écran. J’utilise CSS Media Queries pour masquer un SVG et afficher l’autre. Je commence par afficher le SVG à petit écran par défaut:
.svg-small { display: block; }
.svg-large { display: none; }
Ensuite, j’utilise un min-width
Requête médiatique pour passer au SVG à grand écran à 64rem
et ci-dessus:
@media (min-width: 64rem) {
.svg-small { display: none; }
.svg-large { display: block; }
}
Cela garantit qu’il n’y a jamais qu’un seul SVG visible à la fois, en gardant ma mise en page simple et le DOM sans encombrement inutile. Et parce que les deux SVG visibles font référence au même caché <symbol>
bibliothèque, le navigateur ne télécharge l’illustration qu’une seule fois, quel que soit le nombre <use>
Des éléments apparaissent à travers les deux dispositions.

Emballage
En combinant <symbol>
, <use>
CSS Media Queries et transformations spécifiques, je peux construire SVG adaptatifs qui repositionnent leurs éléments sans duplication de contenu, chargeant des actifs supplémentaires ou s’appuyant sur JavaScript. Je dois définir chaque graphique une seule fois dans une bibliothèque de symboles cachés. Ensuite, je peux réutiliser ces graphiques, au besoin, à l’intérieur de plusieurs SVG visibles. Avec CSS faisant la commutation de mise en page, le Le résultat est rapide et flexible.
C’est un rappel que certaines des techniques les plus puissantes du Web n’ont pas besoin de grands cadres ou d’outillage complexes – juste un peu de savoir-faire SVG et une utilisation intelligente des bases.

(GG, YK)
Source link