Maison >interface Web >tutoriel CSS >Comment créer un graphique animé de carrés imbriqués à l'aide de masques

Comment créer un graphique animé de carrés imbriqués à l'aide de masques

Lisa Kudrow
Lisa Kudroworiginal
2025-03-18 11:03:23835parcourir

Comment créer un graphique animé de carrés imbriqués à l'aide de masques

Nous avons de nombreux types de graphiques bien connus: bar, beignet, ligne, tarte, vous l'appelez. Toutes les bibliothèques de graphiques populaires les soutiennent. Ensuite, il existe les types de graphiques qui n'ont même pas de nom. Découvrez ce tableau de rêve avec des carrés empilés (imbriqués) qui peuvent aider à visualiser les tailles relatives, ou comment les différentes valeurs se comparent les unes aux autres:

Ce que nous faisons

Sans aucune interactivité, la création de ce design est assez simple. Une façon de le faire est d'empiler des éléments (par exemple, des éléments SVG , ou même des divs HTML) dans des tailles décroissantes, où tous leurs coins inférieurs à gauche touchent le même point.

Mais les choses deviennent plus difficiles une fois que nous avons introduit une certaine interactivité. Voici comment cela devrait être: lorsque nous déplaçons notre souris sur l'une des formes, nous voulons que les autres se fanent et s'éloignent.

Nous allons créer ces formes irrégulières à l'aide de rectangles et de masques - littéral avec des éléments et . Si vous êtes entièrement nouveau dans les masques, vous êtes au bon endroit. Ceci est un article de niveau d'introduction. Si vous êtes plus assaisonné, alors peut-être que cet effet de découpe est une astuce que vous pouvez emporter avec vous.

Maintenant, avant de commencer, vous vous demandez peut-être si une meilleure alternative au SVG à l'utilisation de formes personnalisées. C'est vraiment une possibilité! Mais des formes de dessin avec un peut être intimidant, ou même devenir désordonnée. Ainsi, nous travaillons avec des éléments «plus faciles» pour obtenir les mêmes formes et effets.

Par exemple, voici comment nous devrions représenter la plus grande forme bleue en utilisant un .

 <svg viewbox="0 0 320 320" width="320" height="320">
  <chemin d="M320 0H0V56H264V320H320V0Z" fill="# 264653"></chemin>
</svg>

Si le 0h0v56… n'a aucun sens pour vous, consultez «la syntaxe du chemin SVG: un guide illustré» pour une explication approfondie de la syntaxe.

Les bases du graphique

Étant donné un ensemble de données comme ceci:

 type dataSetEntry = {
  étiquette: chaîne;
  valeur: numéro;
};

type dataSet = dataSetEntry [];

const RawDataset: DataSet = [
  {label: 'bad', valeur: 1231},
  {label: 'début', valeur: 6321},
  {étiquette: «développement», valeur: 10028},
  {label: 'accompli', valeur: 12123},
  {étiquette: 'exemplaire', valeur: 2120}
]]

… Nous voulons nous retrouver avec un SVG comme ceci:

 <svg viewbox="0 0 320 320" width="320" height="320">
  <rect width="320" height="320" y="0" fill="...">  rect>
  <rect width="264" height="264" y="56" fill="...">  rect>
  <rect width="167" height="167" y="153" fill="..."> 
  <rect width="56" height="56" y="264" fill="...">  rect>
  <rect width="32" height="32" y="288" fill="...">  rect>
</rect></rect></rect></rect></rect></svg>

Déterminer la valeur la plus élevée

Cela deviendra évident dans un instant pourquoi nous avons besoin de la valeur la plus élevée. Nous pouvons utiliser le math.max () pour l'obtenir. Il accepte n'importe quel nombre d'arguments et renvoie la valeur la plus élevée dans un ensemble.

 const dataSetHighestValue: Number = Math.max (
  ... RawDataset.map ((Entrée: DataSeTentry) => Entry.Value)
));

Comme nous avons un petit ensemble de données, nous pouvons simplement dire que nous obtiendrons 12123.

Calcul de la dimension des rectangles

Si nous regardons la conception, le rectangle représentant la valeur la plus élevée (12123) couvre toute la zone du graphique.

Nous avons arbitrairement choisi 320 pour les dimensions SVG. Étant donné que nos rectangles sont des carrés, la largeur et la hauteur sont égales. Comment pouvons-nous faire 12123 égal à 320? Et les valeurs moins «spéciales»? Quelle est la taille du rectangle 6321?

Interrogé une autre manière, comment cartographier un nombre d'une plage ([0, 12123]) à un autre ([0, 320])? Ou, en termes plus mathématiques, comment pouvons-nous évoluer une variable à un intervalle de [a, b]?

Pour nos fins, nous allons implémenter la fonction comme celle-ci:

 const RemapValue = (
  Valeur: numéro,
  Frommin: numéro,
  Frommax: numéro,
  Tomin: numéro,
  Tomax: numéro
): numéro => {
  return ((valeur - fromin) / (Frommax - fromin)) * (tomax - tomin) tomin;
};

RemapValue (1231, 0, 12123, 0, 320); // 32
RemapValue (6321, 0, 12123, 0, 320); // 167
RemapValue (12123, 0, 12123, 0, 320); // 320

Étant donné que nous cartographions les valeurs sur la même plage dans notre code, au lieu de passer les minimums et les maximums encore et encore, nous pouvons créer une fonction de wrapper:

 const ValueReMapper = (
  Frommin: numéro,
  Frommax: numéro,
  Tomin: numéro,
  Tomax: numéro
) => {
  return (valeur: nombre): numéro => {
    return remapValue (valeur, Frommin, Frommax, tomin, tomax);
  };
};

const RemapDatasetValuetOsvgDimension = ValueRemapper (
  0,
  dataSetHiGhestValue,
  0,
  svgdimension
));

Nous pouvons l'utiliser comme ceci:

 remapdatasetvalutosvgdimension (1231); // 32
remapdatasetvalutosvgdimension (6321); // 167
RemapdatasetvalutOsvgdimension (12123); // 320

Créer et insérer les éléments DOM

Ce qui reste a à voir avec la manipulation DOM. Nous devons créer les éléments et les cinq , définir leurs attributs et les ajouter au DOM. Nous pouvons faire tout cela avec les fonctions de base CreateElementNS, SetAttribute et APPEDCHILD.

Notez que nous utilisons les CreateElementns au lieu de la création plus courante. C'est parce que nous travaillons avec un SVG. Les éléments HTML et SVG ont des spécifications différentes, donc ils relèvent d'un autre espace de noms URI. Il arrive juste que le CreateElement utilise facilement l'espace de noms HTML! Donc, pour créer un SVG, nous devons être aussi verbeux:

 Document.CreateElementns ('http://www.w3.org/2000/svg', 'svg') en tant que svgsvgementment;

Nous pouvons sûrement créer une autre fonction d'assistance:

 const CreateSVGNElement = (élément: String): svgement => {
  return document.createElementns ('http://www.w3.org/2000/svg', élément);
};

Lorsque nous ajoutons les rectangles au DOM, nous devons prêter attention à leur commande. Sinon, nous devions spécifier explicitement l'indice Z. Le premier rectangle doit être le plus grand, et le dernier rectangle doit être le plus petit. Mieux vaut trier les données avant la boucle.

 const data = rawdataset.sort (
  (A: DataSeTentry, B: DataSeTentry) => B.Value - A.Value
));

data.ForEach ((d: dataSetEntry, index: numéro) => {
  const REct: svGrecedElement = créeSvGnSelement («rect») comme svGrecedElement;
  const REctDimension: Number = remapDataSetValuetoSvgdimension (d.Value);

  rect.setAttribute ('width', `$ {rectdimension}`);
  rect.setAttribute ('height', `$ {rectdimension}`);
  rect.setAttribute ('y', `$ {svgdimension - rectdimension}`);

  SVG.APPEndChild (RECT);
});

Le système de coordonnées commence par le haut à gauche; C'est là que le [0, 0] est. Nous allons toujours dessiner les rectangles du côté gauche. L'attribut x, qui contrôle la position horizontale, est par défaut à 0, nous n'avons donc pas à le définir. L'attribut Y contrôle la position verticale.

Pour donner l'impression visuelle que tous les rectangles proviennent du même point qui touche leurs coins inférieurs à gauche, nous devons pousser les rectangles vers le bas pour parler. Par combien? Le montant exact que le rectangle ne remplit pas. Et cette valeur est la différence entre la dimension du graphique et le rectangle particulier. Si nous assemblons tous les bits, nous nous retrouvons avec ceci:

Nous avons déjà ajouté le code de l'animation à cette démo à l'aide de CSS.

Découper des rectangles

Nous devons transformer nos rectangles en formes irrégulières qui ressemblent au numéro sept, ou la lettre L tourna de 180 degrés.

Si nous nous concentrons sur les «parties manquantes», nous pouvons voir qu'ils découpent les mêmes rectangles avec lesquels nous travaillons déjà.

Nous voulons cacher ces découpes. C'est ainsi que nous allons finir avec les formes L que nous voulons.

Masquage 101

Un masque est quelque chose que vous définissez et appliquez plus tard à un élément. En règle générale, le masque est incliné dans l'élément à lequel il appartient. Et, généralement, il devrait avoir un ID unique car nous devons le référencer afin d'appliquer le masque à un élément.

 <svg>
  <masque>
    
   mask>
</masque></svg>

Dans la balise , nous avons mis les formes qui servent de masques réels. Nous appliquons également l' attribut de masque aux éléments.

 <svg>
  <masque>
    
   mask>
  <rect mask="url (#MycleVerlyNamedMask)"> 
</rect></masque></svg>

Ce n'est pas le seul moyen de définir ou d'appliquer un masque, mais c'est le moyen le plus simple pour cette démo. Faisons un peu d'expérimentation avant d'écrire un code pour générer les masques.

Nous avons dit que nous voulons couvrir les zones de découpe qui correspondent aux tailles des rectangles existants. Si nous prenons le plus grand élément et que nous appliquons le rectangle précédent comme masque, nous nous retrouvons avec ce code:

 <svg viewbox="0 0 320 320" width="320" height="320">
  <masque>
    <rect width="264" height="264" y="56" fill=""> 
   mask>
  <rect width="320" height="320" y="0" fill="# 264653" mask="url (#themask)"> 
</rect></rect></masque></svg>

L'élément à l'intérieur du masque a besoin d'une valeur de remplissage. Qu'est-ce que cela devrait être? Nous verrons des résultats entièrement différents en fonction de la valeur de remplissage (couleur) que nous choisissons.

Le remplissage blanc

Si nous utilisons une valeur blanche pour le remplissage, nous obtenons ceci:

Maintenant, notre grand rectangle est la même dimension que le rectangle de masquage. Pas exactement ce que nous voulions.

Le remplissage noir

Si nous utilisons à la place une valeur noire, cela ressemble à ceci:

Nous ne voyons rien. C'est parce que ce qui est rempli de noir est ce qui devient invisible. Nous contrôlons la visibilité des masques à l'aide de remplissages blancs et noirs. Les lignes en pointillés sont là comme une aide visuelle pour référencer les dimensions de la zone invisible.

Le remplissage gris

Maintenant, utilisons quelque chose entre le blanc et le noir, disons gris:

Ce n'est ni entièrement opaque ni solide; c'est transparent. Donc, maintenant, nous savons que nous pouvons contrôler le «degré de visibilité» ici en utilisant quelque chose de différent des valeurs blanches et noires, ce qui est une bonne astuce à garder dans nos poches arrière.

Le dernier bit

Voici ce que nous avons couvert et appris sur les masques jusqu'à présent:

  • L'élément à l'intérieur du contrôle la dimension de la zone masquée.
  • Nous pouvons rendre le contenu de la zone masquée visible, invisible ou transparent.

Nous n'avons utilisé qu'une seule forme pour le masque, mais comme pour n'importe quelle balise HTML à usage général, nous pouvons nicher autant d'éléments enfants que nous le souhaitons. En fait, l'astuce pour réaliser ce que nous voulons consulter deux éléments SVG . Nous devons les empiler l'un sur l'autre:

 <svg viewbox="0 0 320 320" width="320" height="320">
  <masque>
    <rect width="320" height="320" y="0" fill="???"> 
    <rect width="264" height="264" y="56" fill="???"> 
   mask>
  <rect width="320" height="320" y="0" fill="# 264653" mask="url (# maskw320)"> 
</rect></rect></rect></masque></svg>

L'un de nos rectangles de masquage est rempli de blanc; l'autre est rempli de noir. Même si nous connaissons les règles, essayons les possibilités.

 <masque>
  <rect width="320" height="320" y="0" fill="noir"> 
  <rect width="264" height="264" y="56" fill="white"> 
 mask></rect></rect></masque>

Le est la dimension du plus grand élément et le plus grand élément est rempli de noir. Cela signifie que tout dans ce domaine est invisible. Et tout sous le plus petit rectangle est visible.

Maintenant, faisons tourner les choses où le rectangle noir est au-dessus:

 <masque>
  <rect width="320" height="320" y="0" fill="white"> 
  <rect width="264" height="264" y="56" fill="noir"> 
 mask></rect></rect></masque>

C'est ce que nous voulons!

Tout dans le plus grand rectangle rempli de blanc est visible, mais le plus petit rectangle noir est au-dessus (plus près de nous sur l'axe Z), masquant cette partie.

Générer les masques

Maintenant que nous savons ce que nous devons faire, nous pouvons créer les masques avec une relative facilité. C'est similaire à la façon dont nous avons généré les rectangles colorés en premier lieu - nous créons une boucle secondaire où nous créons le masque et les deux rectes.

Cette fois, au lieu d'ajouter les rectes directement au SVG, nous l'ajoutons au masque:

 data.ForEach ((d: dataSetEntry, index: numéro) => {
  const Mask: svgmaskElement = CreateSvGnSelement ('mask') comme svgmaskElement;

  const REctDimension: Number = remapDataSetValuetoSvgdimension (d.Value);
  const REct: svGrecedElement = créeSvGnSelement («rect») comme svGrecedElement;

  rect.setAttribute ('width', `$ {rectdimension}`);
  // ... définir le reste des attributs ...

  mask.setAttribute ('id', `maskw $ {rectdimension.tofixed ()}`);

  masque.ApendChild (RECT);

  // ... Création et définition des attributs pour le plus petit rectangle ...

  SVG.APPEndChild (masque);
});

data.ForEach ((d: dataSetEntry, index: numéro) => {
    // ... notre code pour générer les rectangles colorés ...
});

Nous pourrions utiliser l'index comme identifiant du masque, mais cela semble une option plus lisible, du moins pour moi:

 mask.setAttribute ('id', `maskw $ {rectdimension.tofixed ()}`); // maskw320, masw240, ...

En ce qui concerne l'ajout du rectangle plus petit dans le masque, nous avons facilement accès à la valeur dont nous avons besoin car nous avons précédemment commandé les valeurs rectangulaires du plus haut au plus bas. Cela signifie que l'élément suivant de la boucle est le rectangle plus petit, celui que nous devons référencer. Et nous pouvons le faire par son index.

 // ... partie précédente où nous avons créé le masque et le rectangle ...

const smallErrectIndex = index 1;

// il n'y a pas de prochain quand nous sommes sur le plus petit
if (data [smallErrectIndex]! == Undefined) {
  const SmallErrectDimension: nombre = remapdatasetvaluetosvgdimension (
    données [smallErrectIndex] .Value
  ));
  const smallErrect: svGrecedElement = CreateSVGNELlement (
    'rect'
  ) comme svGreceLelement;

  // ... définir les attributs du rectangle ...

  masque.APPEndChild (Small Earrect);
}

SVG.APPEndChild (masque);

Ce qui reste, c'est d'ajouter l'attribut de masque au rectangle coloré dans notre boucle d'origine. Il devrait correspondre au format que nous avons choisi:

 rect.setAttribute ('mask', `url (# maskw $ {rectdimension.tofixed ()})`); // maskw320, maskw240, ...

Le résultat final

Et nous avons fini! Nous avons réussi à faire un graphique fabriqué à partir de carrés imbriqués. Il se sépare même de survoler de la souris. Et tout ce qu'il a fallu, c'était un SVG en utilisant l'élément pour dessiner la zone de découpe de chaque carré.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn