Fermer

septembre 1, 2021

Une plongée profonde dans le monde merveilleux du filtrage par déplacement SVG —


Résumé rapide ↬

Qu'est-ce qu'un filtre de déplacement exactement ? Dans cet article, Dirk Weber explique la primitive de filtre SVG feDisplacementMap avec un bon nombre d'exemples pour démontrer le concept de cartes de déplacement animées.

Même aujourd'hui, le royaume magique et pervers des SVG Filter Effects est en grande partie un territoire inexploré. L'art du filtrage SVG est toujours entouré d'une aura d'alchimie : vous devez bravement plonger dans un monde sombre d'incohérences, votre dévouement sera testé à plusieurs reprises par des implémentations boguées et des effets secondaires angoissants, et vous devez apprendre des incantations compliquées. Mais, une fois maîtrisé, il vous donne un pouvoir sans précédent – un moyen de changer l'apparence totale des éléments et des sites Web en un claquement de doigt.

Dans cet article, nous allons plonger dans l'un des effets de filtre les plus spectaculaires : la primitive de filtre SVG feDisplacementMap. Afin de rendre tout cela plus facile à digérer, j'ai divisé l'article en trois parties dans lesquelles nous allons explorer :

  1. comment fonctionne la feDisplacementMapc'est-à-dire comment l'appliquer et comment contrôler sa sortie de manière prévisible ;
  2. nous explorerons ensuite des méthodes pour créer des cartes de déplacement sophistiquées en SVG (un peu plus intéressantes car nous commencerons à jouer avec JavaScript);
  3. et enfin , nous allons jeter un œil à certaines des méthodes pour animer le filtre et créer des effets visuels dramatiques.

Comme ce sera une lecture assez longue, les plus impatients voudront peut-être vérifier les démos que nous allons rencontrer avant de continuer. Toutes les démos de cet article ont été optimisées pour les dernières versions des trois principaux moteurs de navigateur.

Pour tirer le meilleur parti de cet article, vous devez déjà avoir une compréhension de base des filtres SVG. Si vous êtes un novice des filtres, vous voudrez peut-être faire un petit détour par l'introduction de Sara Soueidan ou vous diriger vers mon humble point de vue sur le sujet d'abord.

Soyez averti cependant. : pas appliqué correctement, Les filtres SVG peuvent considérablement nuire aux performances de votre site. Testez toujours de manière approfondie si vous déployez l'une des techniques décrites ici. (Source : Dirk Weber) ( Grand aperçu)

Une brève introduction sur le filtrage par déplacement

Alors, qu'est-ce qu'un filtre par déplacement ? Une opération de déplacement peut déformer visuellement tout graphique auquel elle est appliquée. Vous pouvez créer des effets de distorsion, des tourbillons ou des ondulations comme vous le feriez avec un filtre de distorsion Photoshop. Le filtrage de déplacement est un outil important dans VFXet vous avez probablement déjà vu des opérations de mapping de déplacement sur film et TV, créées avec un outil VFX comme After Effects ou GiantRed.

Pour obtenir un effet de distorsion, le filtre a besoin de deux images en entrée :

  • Le graphique source réel doit être déformé (à partir de maintenant uniquement « source »); . Cette carte contient des informations sur la façon dont nous voulons que la source soit déformée.

La ​​plupart du temps, une carte sera une image Bitmap, mais dans la partie suivante, je montrerai comment utiliser des images ou des fragments SVG en entrée.

Voyons ce qui se passe lorsque nous utilisons une image de la célèbre La Sagrada Familia de Barcelone pour « déformer » la Joconde :

Voir le stylo [Simple example of `feDisplacementMap` filtering](https://codepen.io/ smashingmag/pen/NWgNbmg) par Dirk Weber.

Voir le Pen Exemple simple de filtrage feDisplacementMap par Dirk Weber.[19659021]Répartition d'un exemple de filtre simple »/>
Visualisation des briques de ce filtre. ( Grand aperçu)
  1. La première primitive de filtre est feImage qui contient une référence à la carte (il existe d'autres primitives de filtre qui peuvent être utilisées comme entrée. Vous trouverez plusieurs démos fascinantes où feTurbulence est utilisé comme carte de déplacementmais dans cet article, nous nous concentrerons principalement sur feImage).
  2. Cette feImage est ensuite introduite dans une primitive feDisplacementMap où le la distorsion réelle se produit :
    • Un attribut scale positif ou négatif définit la force de la distorsion.
    • Le but de xChannelSelector et yChannelSelector est de déterminer laquelle des entrées les canaux à quatre couleurs de l'image (rouge, vert, bleu, alpha) doivent être appliqués à quel axe de distorsion. Les deux attributs sont par défaut le canal alpha de la carte (ce qui signifie que si vous utilisez une carte sans canaux alpha et omettez ces attributs, vous ne verrez rien de plus qu'un décalage diagonal de la source).

Nous appliquons ensuite le filtre avec CSS :

.filtered {
  filtre : url(#displacement-filter);
}

Il peut être amusant de jouer avec la distorsion des images de cette façon, mais il est imprévisible à quoi ressemblera le résultat et la plupart du temps, ce n'est pas du tout esthétique. Existe-t-il un moyen d'obtenir un contrôle parfait au pixel près sur la sortie ? Voici ce que dit la spec :

Cette primitive de filtre utilise les valeurs de pixels de l'image de in2 pour déplacer spatialement l'image de in. Voici la transformation à effectuer :

P'(x ,y) ← P( x + échelle * (XC(x,y) – .5), y + échelle * (YC(x,y) – .5))

La carte de déplacement, in2, définit l'inverse du mappage effectué.

OK, cela semble compliqué à première vue, mais il est en fait assez facile à comprendre lorsqu'il est décomposé :

  • P'(x,y) représente les coordonnées de un pixel dans le résultat ;
  • X et Y sont les coordonnées de ce pixel dans la source non filtrée ;
  • XC et YC sont les valeurs de couleur RVB normalisées (1/255) du pixel donné dans la carte ;
  • Enfin, le résultat de l'opération doit être inversé (ce qui signifie essentiellement chaque + dans la formule doit être remplacé par un -).

Nous allons effectuer quelques expériences simples pour vérifier notre formule en introduisant des bitmaps primitifs dans un filtre, composé d'une seule couleur. Disons que la carte est remplie de rgb(51, 51, 51)comment pouvons-nous nous attendre à ce que les coordonnées d'un pixel source à x=100 / y=100 soient transformées lorsque introduit dans une primitive de déplacement avec une valeur d'échelle de 100 ?

X : 100 – 100 * (51/255 – .5) = 130

Y : 100 – 100 * (51/255 – .5) = 130

Voir le Pen [`feDisplacementMap` filtering apply rgb(51, 51, 51)](https://codepen.io/smashingmag/pen/gORrLNw) par Dirk Weber.

Voir le Pen feDisplacementMap Le filtrage applique rgb (51, 51, 51) par Dirk Weber.

Le pixel résultant sera déplacé vers les coordonnées 130130. Il sera décalé à 30px vers la droite et 30px vers le bas. Que se passe-t-il lorsque nous modifions la carte à 50% de gris comme dans rgb(127,127, 127) ?

Voir le stylo [`feDisplacementMap` filtering apply 50%](https://codepen.io/smashingmag/pen/zYzqogy ) par Dirk Weber.

Voir le filtrage Pen feDisplacementMap appliquer un filtrage à 50 % par Dirk Weber.

Évidemment, une couleur neutre ne pas d'effet reconnaissable. Les pixels résultants restent en place. Et si nous modifions les valeurs de couleur à quelque chose au-dessus de 128, disons rgb(204, 204, 204)?

Voir le stylo [`feDisplacementMap` filtering apply rgb(204, 204, 204)](https://codepen.io/smashingmag/pen /qBjZRWe) par Dirk Weber.

Voir le filtrage Pen feDisplacementMap appliquer le filtrage rgb(204, 204, 204) par Dirk Weber.[19659043]Les coordonnées ont été décalées de 30px vers la gauche et de 30px vers le haut.

Nous avons maintenant suffisamment appris pour résumer la mécanique interne du filtre de déplacement dans ces trois phrases simples :

  • Toute valeur de couleur au-dessus 127 déplacera le pixel correspondant dans la direction de la valeur d'échelle ;
  • Toute valeur de couleur inférieure à 127 déplacera le pixel correspondant dans la direction opposée ;
  • Une valeur de couleur de 127 n'aura aucun effet.

De l'intuition , on aurait tendance à croire que la couleur noire n'aurait aucun effet, mais il devrait maintenant être clair que ce n'est pas le cas. En fait, le noir et blanc entraînera le décalage maximum possible vers ou en dehors de la valeur d'échelle.

Plus après le saut ! Continuez à lire ci-dessous ↓

La carte absolue

À ce stade, je dois vous présenter la carte spéciale qui sera la base de tous les effets que nous verrons à partir de maintenant. C'est une carte qui va effectuer une distorsion très simple : redimensionner une image proportionnellement. Nous l'appellerons la carte d'identité ou la carte absolue à partir de maintenant.

Exemple de carte exploitant les canaux de couleur rouge et bleu

Exemple de identité– ou carte absolue exploitant les canaux de couleur rouge et bleu. ( Grand aperçu)

Pour redimensionner une image de manière égale dans toutes les directions, les valeurs de couleur doivent progressivement diminuer d'un maximum sur un bord à un minimum sur le bord opposé. Nous utiliserons dorénavant le rouge pour X et le bleu pour Ymais au final, peu importe la couleur que vous avez choisie pour x- et yChannelSelector.

  1. Dans votre éditeur d'images préféré, ouvrez un nouveau document ;
  2. Définissez la couleur d'arrière-plan du document sur noir ;
  3. Créez un nouveau calque et remplissez-le de gauche à droite dégradé de rgb(255, 0, 0) à rgba(255, 0, 0, 0);
  4. Ajoutez un deuxième calque et ajoutez un dégradé de haut en bas à partir de rgb (0, 0, 255) à rgba(0, 0, 255, 0);
  5. Définissez le mode de fusion de ce calque sur screen.

Et voilà, vous avez construit une carte absolue ! Cette carte servira de base solide pour toutes sortes de distorsions d'image :

  • En appliquant des filtres de distorsion de type Photoshop à cette carte, nous pouvons utiliser ces effets dans les filtres CSS !
  • Nous pouvons contrôler la mise à l'échelle de la Axes x et y indépendamment en modifiant la transparence du dégradé bleu ou rouge.
  • Il est possible de "masquer" des parties de la carte avec une couleur "neutre" (rgb(127, 0 ,127) ou #7F007F) pour empêcher les parties correspondantes de l'image de se déplacer.

Voir le stylo [Variations of displacement maps and their application](https://codepen.io/smashingmag/pen/KKqzaKo) de Dirk Weber.

Voir le stylo Variations des cartes de déplacement et leur application par Dirk Weber.

Explorer différentes cartes en action

Pour obtenir une meilleure compréhension du processus J'ai fait une petite application pour explorer diverses cartes de déplacement. Toutes les cartes ont été créées en appliquant de simples filtres de distorsion Photoshop à la carte absolue.

Capture d'écran du maptester

Un maptester sur Codepen (Cliquez pour lancer →)

Le problème des bords irréguliers

Vous avez peut-être remarqué les bords pixélisés qui apparaissent parfois dans l'image de sortie. En particulier le matériel source avec un contraste élevé, par ex. la typographie ou l'illustration vectorielle, est sujette à cet effet. (Source : DirkWeber) ( Grand aperçu)

Cela est dû à plusieurs raisons :

  • Le filtre prendra l'image source sous forme de bitmap :
    S'il existe des bords anticrénelés dans la source, le filtre ne les "réinitialisera" pas après déplacer la source. Tout pixel sera transformé à son nouvel emplacement, c'est tout.
  • Erreurs d'arrondi :
    Peut-être qu'un pixel de 100,100 doit être déplacé vers 83.276, 124.217. Le filtre doit en quelque sorte mapper ces coordonnées sur des valeurs de pixels non décimales.
  • Espaces après déplacement :
    Peut-être deux pixels voisins, disons aux coordonnées x1 : 100x2 : 101 sont déplacés vers différents emplacements, peut-être x1:207.4x2: 211.3. Comment le filtre remplira-t-il l'espace entre les deux ? Indice : pas du tout. La spécification indique clairement :

« Parfois, les primitives de filtre donnent des pixels non définis. Par exemple, la primitive de filtre feOffset peut décaler une image vers le bas et vers la droite, laissant des pixels non définis en haut et à gauche. Dans ces cas, les pixels non définis sont définis sur un noir transparent. augmentez le contraste avec une feConvolveMatrix. Pas parfait, mais assez bon pour la plupart des situations. Voici une démo sur CodePen :

Voir le stylo [`FeDisplacementMap`: handling jagged edges](https://codepen.io/smashingmag/pen/PojNWwW) par Dirk Weber.

Voir le Pen FeDisplacementMap : la gestion des bords irréguliers par Dirk Weber.

Don't Give Up On Webkit !

Et puis il y a WebKit. C'est le navigateur dans lequel vous passerez la plupart du temps à déboguer vos filtres. Depuis mon premier article sur le sujet WebKit s'est considérablement amélioré. L'une des parties les plus amusantes des filtres SVG est de les appliquer au contenu HTML via CSS et, en fait, Webkit est désormais capable d'appliquer des filtres même compliqués au HTML. Au moment d'écrire ces lignes, cela n'est malheureusement toujours pas vrai pour les filtres avec feImage dans sa chaîne de rendu. Webkit n'affichera pas du tout l'élément. Parfois, il est utile d'appliquer le filtre à un qui a été enroulé autour de votre contenu HTML, mais actuellement, il existe un autre bogue dans WebKit qui laisse tout élément qui a une position ou transform Attribut CSS non filtré, cette méthode est donc loin d'être à l'épreuve des balles. Pour les besoins de cet article, nous éviterons de filtrer les éléments HTML. ]carte ? Réfléchissez un instant avant de jeter un coup d'œil à la solution.

Voir le stylo [SVG Dispacementquiz](https://codepen.io/smashingmag/pen/wveGgmm) par Dirk Weber .

Voir le Pen SVG Dispacementquiz par Dirk Weber.

Créer des cartes de déplacement SVG et les introduire dans le filtre

Nous voulons pouvoir créer notre déplacement map d'une manière qui nous permet de les modifier dynamiquement avec JavaScript et CSS et nous voulons pouvoir les animer. Quoi de plus raisonnable que de créer la carte en SVG ?

Voici la recette pour créer une carte absolue en SVG :

  • Créez deux rects ;
  • Appliquez les dégradés ;
  • Fusionnez-les avec CSS mix-blend-mode : screen ;
  • Vous êtes prêt ! (vue sur CodePen →)

Indice : N'oubliez jamais de déclarer la largeur et la hauteur en valeurs de pixels dans le SVG. Sinon, il n'apparaîtra pas dans Firefox et deviendra flou dans Chrome.

Voir le stylo [Universal SVG Identitymap](https://codepen.io/smashingmag/pen/QWgNdba) par Dirk Weber .

Voir le Pen Universal SVG Identitymap par Dirk Weber.

La carte SVG est prête, mais l'introduire dans un filtre n'est pas aussi simple qu'on pourrait le penser. Il existe essentiellement 3 façons de référencer un SVG à partir de feImageoù les deux premières souffrent d'une combinaison malheureuse de problèmes de sécurité concernant les filtres SVG et buggy navigateur comportement :

  1. En tant que ressource externe : une méthode qui ne fonctionne pas dans Webkit/Safari (qui l'aurait deviné ?)
  2. En tant que fragment SVG : une méthode qui ne fonctionne nulle part sauf Safari (vous n'avez pas vu ça venir, n'est-ce pas ?), ce qui nous laisse avec la seule méthode cross-browser fiable…
  3. Comme URL de données :

Cela signifie qu'une carte SVG doit toujours être encodée en URL à l'avance (manuellement ou avec un outil de génération), ou que la conversion doit avoir lieu sur le client, comme illustré dans cet exemple :

const feImage = document.querySelector( '#myFeImage');
const url = feImage.getAttribute('href');

récupérer (url)
  .then((réponse) => {
    return response.text();
  })
  .then((svgText) => {
    const uri = encodeURIComponent(svgText);
    feImage.setAttribute('href', 'data:image/svg+xml;charset=utf-8,${uri}`);
  })
  .catch((erreur) => {
    feImage.setAttribute('href', someFallbackURI);
  });

Vous pouvez définir mode sur CORS si vous devez charger des images d'un autre domaine ou d'un CDN :

fetch('mymap.svg', {mode : 'cors'})
    .alors(…)

Alternativement, un fragment peut être converti en une URL de données :

const myFragmentId = myFeImage.getAttribute('href');
const myFragmentHTML = document.getElementById(myFragmentId).outerHTML;
const myFragmentDataURL = encodeURIComponent(myFragmentHTML);

myFeImage.setAttribute('href', 'data:image/svg+xml;charset=utf-8,${myFragmentDataURL}');

Indice : Tout élément SVG peut être un fragment. Mais un fragment encodé d'URL doit être un élément SVG avec un attribut namespace.

Pour les très gros SVG ou les bitmaps chargés à partir d'un autre domaine, un blob peut être le meilleur choix :

fetch('mymap.svg')
  .then((réponse) => {
    retour réponse.blob();
  })
  .then((blob) => {
    const objURL = URL.createObjectURL(blob);
    feImage.setAttribute('href', objURL);
  });

Remarque : C'est une astuce utile pour contourner les problèmes inter-domaines avec les images sur CodePen.

Construire une loupe

Il est temps de mettre nos nouvelles connaissances en pratique. Cette démo montre comment modifier dynamiquement une carte de déplacement SVG qui a été appliquée à un magnifique panorama de la NASA sur Mars avec l'atterrisseur Curiosity en son centre.

Organigramme du processus derrière la loupe svg

JavaScript peut modifier dynamiquement un filtre SVG. Ici, nous utilisons JavaScript pour créer une loupe qui suit la souris de l'utilisateur. (Source : DirkWeber) ( Grand aperçu)
  1. Insérez une primitive feImage avec une référence à la absolutemap ;
  2. Créez la « loupe », un SVG contenant un cercle rempli d'un dégradé radial, à partir de rgba(127, 0, 127, 0) et se terminant par rgba(127, 0, 127, 1);
  3. Insérez une seconde feImage avec un référence à la « loupe » ;
  4. Fusionnez les deux images dans une primitive feMerge et faites du résultat le feDisplacementMap in2. Comme vous l'avez peut-être remarqué, nous utilisons ici un facteur d'échelle négatif pour nous assurer que l'image sera réduite à l'extérieur et s'affichera à sa taille normale à l'intérieur de la « loupe » ;
  5. Ajoutez du code JavaScript pour que Les attributs x et y de la feImage faisant référence à la « loupe » correspondent à la position de la souris.

Générer des cartes arbitraires avec des chemins flous

Une manière totalement différente de créer une carte de déplacement SVG est en utilisant des chemins de Bézier flous extrêmement épais au lieu de dégradés. Voici une petite application qui vous permet de modifier les points d'ancrage de Bézier dans une carte créée de cette manière.

Vizualisation comment créer des cartes de distorsion SVG arbitraires

L'utilisation de lignes floues extrêmement épaisses est une astuce pour créer arbitrairement Cartes de distorsion SVG. Cliquez pour ouvrir la démo →

Il est possible de créer de jolies cartes de cette façon, mais vous devez garder à l'esprit que le flou a un impact sur les performances de rendu. Firefox a même un seuil de 100px sur la quantité de flou autorisée.

Animation

Nous avons maintenant tout appris sur les grands principes du filtrage de déplacement et sur la création de cartes de déplacement en SVG. Nous sommes prêts pour la partie amusante : comment tout mettre en mouvement.

Les filtres SVG peuvent être animés et transformés. Un gros problème est le fait que les valeurs de filtre référençant une URL ne seront pas interpolées, mais échangées immédiatement sans aucune transition entre les deux, un comportement conforme à la spec. Peut être acceptable dans certaines situations, mais ennuyeux la plupart du temps. Nous voulons des tourbillons, des ondulations, des déformations et des morphes animés !

Quand on pense à des cartes animées, la première chose qui vient à l'esprit est un gif animé ou un WebP. Eh bien, les images animées fonctionneront dans tous les navigateurs d'une manière ou d'une autre . Mais les performances varient considérablement d'assez mauvaise à extrêmement mauvaise. Et puis il y a les limitations liées à la plate-forme : e. g. Blink n'est pas en mesure d'appliquer ce filtre de déplacement animé aux éléments qui contiennent d'autres éléments animés. Et nous n'avons pas encore parlé de la taille du fichier. Au lieu de cela, nous nous concentrerons sur les deux techniques d'animation les plus fiables à mon humble avis : SMIL (oui, SMIL est toujours une chose ces jours-ci) et JavaScript.

A le filtre sera généralement construit à partir d'une variété de primitives différentes et de chaque attribut qui a été ajouté à un nœud, comme xywidthheight ou scale peuvent être animés avec SMIL.

Un simple effet de glitch

Voici un exemple très simple : utiliser une primitive feFlood animée pour créer un effet de glitch de base :

Voir le Pen [`deDisplacementMap`: A simple glitch](https://codepen.io/smashingmag/pen/XWgdpXO) de Dirk Weber.

Voir le Pen deDisplacementMap : Un simple pépin par Dirk Weber.
  • Il y a deux primitives feFlood dans ce filtre. Le premier couvre toute la source et est rempli de la carte neutre rgb(127, 0127) pour s'assurer qu'aucun déplacement ne se produise ici.
  • Le second feFlood est rempli de rgb(255, 0, 127) pour créer un déplacement horizontal et n'obtient qu'une fraction de la hauteur du filtre.
  • Maintenant, il est facile d'ajouter des nœuds d'animation SMIL pour le y et hauteur.
  • Une primitive feMerge fusionne les deux feFlood en une seule sortie, fournissant le feDisplacementMap in2.

A Moving Displacement Map

La position d'une feImage est animable. Dans cet exemple, nous créons une animation de type psychédélique trippante en déplaçant une carte déformée répétitive le long de l'axe x :

Carte de déplacement et organigramme pour l'animation de type psychédélique

Un motif répétitif continu mobile créé avec pixelmators, le merveilleux outil de déformation (à droite) donne cet effet de déformation trippant. ( Grand aperçu)

Cet effet peut être encore plus exploité en ajoutant des masques, des flous et certaines couleurs au mélange. Voici une version améliorée de l'effet utilisant les mêmes techniques, mais d'une manière plus avancée.

Capture d'écran d'une animation de texte « gouttes » . <a href=Cliquez pour ouvrir.

( Grand aperçu)

Vous avez peut-être remarqué qu'en fonction de votre navigateur et de votre processeur, les performances de ces démos peuvent varier considérablement. C'est un fait décevant que Les filtres SVG ne soient toujours pas optimisés pour les performances . Votre GPU accélérera certaines primitives simples (par exemple, les opérations de couleur), mais lorsque vous créez un enchaînement de filtres composés et que vous fusionnez de nombreuses primitives, vous constaterez rapidement que les fréquences d'images chutent et que les fans augmentent, en particulier dans WebKit et Firefox. Les fournisseurs de navigateurs ont beaucoup de sujets sur leurs listes de tâches et les effets de filtre SVG n'y ont pas la priorité la plus élevée, d'autant plus qu'ils ne sont toujours pas trouvés si souvent dans la nature.

Cela ne signifie pas que vous ne pouvez pas utiliser de filtres SVG animés maintenant, mais vous devez les appliquer de manière responsable : de préférence limiter les dimensions de la zone de peinture animée au plus petit rectangle possible, limiter le nombre d'itérations au minimum, être faites attention aux flous et aux opérations de fusion et testez les tests sur de nombreux navigateurs et appareils. Vous trouverez ci-dessous une démonstration de la façon dont l'effet animé feImage d'en haut peut être utilisé pour pimenter une barre de progression plutôt ennuyeuse :

Voir le stylo [SVG `feDisplacementMap`: Download Progressbar](https://codepen.io/smashingmag/ pen/wveGgzr) par Dirk Weber.

Voir le Pen SVG feDisplacementMap : Télécharger Progressbar par Dirk Weber.

Voici un autre exemple de composant d'interface utilisateur, amélioré avec un effet petit et simple. Un bouton play qui se transforme en une onde sonore animée pulsée :

Voir le stylo [SVG `feDisplacementmap`: Play](https://codepen.io/smashingmag/pen/abwNpmg) par Dirk Weber .

Voir le Pen SVG feDisplacementmap : Play par Dirk Weber.

Cette fois, la carte de déplacement a été créée en floutant plusieurs primitives feFlood comme vu dans l'image ci-dessous, puis en animant l'attribut feDisplacementMapscale de la feDisplacementMap"/>

Dans cet exemple, nous n'utilisons pas d'image mais plusieurs primitives feFlood de taille différente comme carte de déplacement. Comme nous voulons obtenir une distorsion verticale, le canal rouge a été réglé sur une valeur neutre (127) et des valeurs variables dans le canal bleu. Dans une étape suivante, les primitives sont fusionnées et floues. (Source : DirkWeber) ( Grand aperçu)

Une transition de pépin entre les éléments

  • Créez 2 SVG différents pour chaque canal de notre carte. Pour chaque couleur, créez une grille de rectangles avec une intensité de couleur variant de manière aléatoire.
  • Créez 2 primitives feImage différentes. Encodez chaque SVG par URL, puis placez-le dans l'attribut href de chaque feImage.
  • Ajoutez des animations SMIL pour widthheight et y attributs.
  • Insérez un feBlend et mélangez les deux feImages en une seule sortie.
  • Ajoutez quelques feDropShadows colorés pour un effet split-color sympa.
  • Mélanger le tout, puis l'insérer dans une feDisplacementmap.
  • Animer l'attribut scale avec SMIL.
  • Feel libre d'expérimenter en modifiant les formes (par exemple, utilisez des cercles au lieu de rects), en appliquant différents timings, en ajoutant des effets de flou, etc. créer cet effet glitch génial. Plusieurs nœuds d'animation SMIL pour les attributs ywidth et height de chaque image sont ajoutés. (Source : DirkWeber) ( Grand aperçu)

    Animation de la carte elle-même

    Jusqu'à présent, nous avons appris que l'animation des attributs de filtre avec SMIL peut nous aider à obtenir des effets visuels vraiment sympas. D'autre part, nous avons déjà vu comment des fragments SVG peuvent être utilisés comme carte de déplacement. Comme les SVG sont animables avec JavaScript, SMIL et CSS, il semble évident que nous pouvons appliquer des animations directement à une carte SVG, n'est-ce pas ?

    Malheureusement, les animations SMIL et CSS dans les images SVG sont utilisées
    en tant qu'entrée pour feImage ne s'exécutera pas lorsque le SVG ou le fragment est une URL
    codé. Nous devrons écrire du JavaScript pour une solution fiable et prendre en compte le fait que deux approches différentes pour les navigateurs Webkit et Blink/Quantum sont nécessaires. Dans un premier temps, voyons à quoi ressemblera la manière « idéale » d'animer une carte :

    • Créez le fragment SVG contenant votre carte ;
    • Référencez-le à partir de la feImage qui contrôle votre ]feDisplacementMap's in2;
    • N'hésitez pas à tout animer dans votre fragment avec JavaScript comme vous le souhaitez. Lancez votre propre script ou utilisez votre bibliothèque.

    « Cela semble trop facile. Où est le piège ? » Bien sûr, vous avez raison. La méthode décrite ci-dessus est le chemin idéalla façon dont les choses devraient fonctionner mais voici un fait étrange : cela ne fonctionnera pas ailleurs que sur Webkit. Pour que notre animation s'exécute dans Blink et Firefox, nous devons implémenter une solution plutôt bidon et cela ne vous plaira pas :

    • Créez le fragment SVG contenant votre carte.
    • Dans chaque image de votre animation, changez tout the values of every animated attribute.
    • In every frame of your animation create a new URL encoded string containing a “snapshot” of the fragment and write it into the feImages href attribute.

    You' re probably thinking: “This is ugly! I don’t like it and you are a despicable person!”. I feel your pain. The front end is a hostile habitat and sometimes we must do abhorrent things to survive (fun fact: the “ugly” method performs better in Blink than the “pure” method in Webkit will).

    Let’s Rock!

    Let’s solve a real-world problem with this approach: here’s what happens to “The Rock” when we apply these two simple displacement maps:

    See the Pen [SVG `feDisplacementmap`: Start- and endpoint of Warp](https://codepen.io/smashingmag/pen/jOwqyVx) by Dirk Weber.

    See the Pen SVG feDisplacementmap: Start- and endpoint of Warp by Dirk Weber.

    And here’s how an animated warp from one map to the other will look:

    See the Pen [SVG `feDisplacementmap`: Animated Warp](https://codepen.io/smashingmag/pen/PojNWWj) by Dirk Weber.

    See the Pen SVG feDisplacementmap: Animated Warp by Dirk Weber.

    Breaking Down The Animation

    The first thing to do is a feature detection, so we can decide which of the two approaches must be applied. Here’s a little script that draws a tiny SVG into a HTML5 canvas:

    async function testSVGFragmentToFeImg() {
      if (!document.createElement("canvas").getContext) {
        return false;
      }
    
      const testCode = '
            
                
                
                    
                    
                
            
            
            
        ';
      const imgURI = 'data:image/svg+xml;charset=utf-8,${encodeURIComponent(
        testCode
      )}';
      const cnvs = document.createElement("canvas");
      const ctx = cnvs.getContext("2d");
      cnvs.width = 10;
      cnvs.height = 10;
      ctx.fillStyle = "rgb(0,0,0)";
      ctx.fillRect(0, 0, 10, 10);
    
      const isSupported = new Promise((resolve) => {
        const svg2img = new Image(10, 10);
    
        svg2img.onload = () => {
          ctx.drawImage(svg2img, 0, 0);
          const colA = ctx.getImageData(1, 1, 1, 1).data;
          const colB = ctx.getImageData(9, 1, 1, 1).data;
    
          resolve(colA[1] !== colB[1]);
        };
    
        svg2img.onerror = () => resolve(false);
        svg2img.src = imgURI;
      });
    
      return await isSupported;
    }
    

    By measuring the color values in the rendered image we can find out if the current browser supports having SVG fragments as input in feImage, so we can decide which version of our animation we should use.

    The SVG Map

    The map is constructed from two elements: an absolute map and an animated SVG polyline. As the absolute map will not change during the animation, we will convert it into a data-URI and put it directly into a feImage primitive.

    A second feImage will contain the reference to the polyline. Both primitives are then merged into one with the help of the feMerge primitive:

    
    
        
    
        
    
        
            
            
        
    
        
    
    

    The Animation

    Depending on which browser it runs in, our script has to do different things on every frame: in Blink/Quantum-based browsers it must update a string and a href attribute, in WebKit it must update the polyline’s point attribute. Did I mention that the animation should still look the same in every environment?

    Luckily for us, the fantastic Animejs JavaScript animation library is predestinated for exactly this kind of task. Besides having everything on board to expect from an animation library (such as easing functions, keyframes timelines and more), it is able to change values in a JavaScript object and call an update function on every frame. Exactly what we need here.

    Let’s dig through the code:

    // The feImage filter primitive that will get the reference to the polyline:
    const feImagePolyline = document.getElementById('feimage-polyline');
    
    // The polyline’s "points" attribute start coordinates:
    const pStart = '141,90 220,168 118,210 138,210 36,168';
    
    // The polyline’s "points" attribute end coordinates:
    const pEnd = '140,40 230,105 30,190 220,190 26,85';
    
    // An animejs configuration object containing base values:
    const animeBaseConfig = {
        duration: 4000,
        loop: 100,
        direction: 'alternate',
        easing: 'easeInOutQuad',
        round: 10
    };
    
    // We create an array with two string segments containing parts of the SVG fragment:
    let polyTpl = '
        
            
                
                    
                
            
            
        
        '.split(‘~');
    
    // This variable will store the animation specific animejs configuration settings:
    let animeConfig;
    
    // Time for action. We call the feature detection script and,
    // as soon as the promise fulfils,
    // conditionally create an animejs configuration object:
    testSVGFragmentToFeImage().then((fragmentInFeImageSupported) => {
        if (!fragmentInFeImageSupported) {
        // Fragments in feImage are not supported. This must be a Blink/Quantum based browser.
    
        // We store the polyline’s point coordinates in this JavaScript object.
        // It’s the animation target for Animejs that will be updated in every frame
        const points = {
            p: pStart
        };
    
        // Of course we do not want to url encode the string on every
        // frame again and again (performance!), we only do it once in advance:
    
        polyTpl = polyTpl.map(part => encodeURIComponent(part));
    
        // The animejs configuration for Blink/Quantum based browsers:
        animeConfig = {
            targets: points,
            p: pEnd,
            update: function () {
                // this function is called in every frame of the animation.
                // It will update the feImage’s “href” value with a "snapshot" of the current polyline:
                const href = `data:image/svg+xml;charset=utf-8,${polyTpl[0]}${points.p}${polyTpl[1]}`;
                feImagePolyline.setAttribute('href', href);
            }
        };
    } else {
        // This must be a Webkit browser. Let’s give it another treatment:
        const filter = document.getElementById('filter');
    
        // An animejs configuration for Webkit based browsers:
        animeConfig = {
            targets: '#line',
            points: pEnd
        };
    
        // Finally we insert the Fragment into the DOM:
        filter.insertAdjacentHTML('beforebegin', `${polyTpl[0]}${pStart}${polyTpl[1]}`);
        feImagePolyline.setAttribute('href', '#polylinemap');
    }
    
    // Now we are safe to trigger the animation by calling animejs with the
    // merged base and specific configuration objects:
    anime({
        ...animeBaseConfig,
        ...animeConfig
    });
    

    This was a very simple example of an animated feDisplacementFilter input. Let’s end this deep dive by looking at three more examples of filter animations.

    1. A “Ripple” Fade Applied To A Modal Box

    We are all used to modal dialog boxes fading in and out. Why not use a water effect to make the dialog appear? Here we animate the radial gradient in a map to create this example of a ripple animation:

Breakdown of simple filter example

A “ripple” fade in effect on a modal. (Source: DirkWeber) (Large preview)

2. Using Animejs’ Grid Animation

Animejs’ staggering and “grid” properties can help you create really cool typographic effects. This effect was created by animating a grid of circles:

Breakdown of simple filter example

Circles arranged in an animated grid make up for this effect. (Source: DirkWeber) (Large preview)

An unconventional way of swapping a second-level submenu. The fade-in effect is achieved by moving a striped map horizontally:

Breakdown of simple filter example

A “waving” animation on a submenu. (Source: DirkWeber) (Large preview)

Please note that the demos shown above are highly experimental and mainly intended to demonstrate the concept of animated displacement maps. If you want to use any of these techniques in a live project, always take my earlier recommendations to heart. And forgive me for not going into detail on every example — it simply would exceed the scope of this article.

Smashing Editorial" width="35" height="46" loading="lazy" decoding="async(vf, il, yk)




Source link