Salut J'étudie en front-end, et en parallèle, dans le projet de formation, je développe SPA sur Vue.js pour le back-end, qui collecte les données du bot de recherche. Le bot génère de 0 à 500 entrées, et je dois: télécharger, trier selon les critères spécifiés, afficher dans le tableau.
Ni le back-end ni le bot ne peuvent trier les données, je dois donc télécharger toutes les données et les traiter côté navigateur. Le tri est très rapide, mais la vitesse de téléchargement dépend de la connexion et les 500 enregistrements indiqués peuvent être chargés de 10 à 40 secondes.
Au début, lors du chargement, j'ai montré un spiner, dont l'inconvénient est que l'utilisateur ne sait pas quand le téléchargement se terminera. Dans mon cas, le nombre d'enregistrements que le bot a trouvé est connu à l'avance, vous pouvez donc montrer combien de% d'enregistrements sont chargés.
Pour égayer l'attente de l'utilisateur, j'ai décidé de lui montrer le processus de chargement:
- chiffres - combien de% des enregistrements sont déjà chargés
- calendrier - temps de chargement de chaque enregistrement
- remplissage -% charge. Comme le graphique remplit un bloc rectangulaire lors de son chargement, il est clair quelle partie du bloc reste Ă remplir
Voici l'animation du résultat que je visais et que j'ai obtenu:

... à mon avis, ça s'est avéré drôle.
Dans l'article, je vais vous montrer comment progresser pas à pas vers le résultat. Je n'ai pas dessiné de graphiques de fonctions dans le navigateur avant le village, donc le développement de l'indicateur m'a apporté des connaissances simples mais nouvelles sur l'utilisation de SVG et de Vue.
Choix d'une méthode de rendu Canvas ou SVG
J'ai utilisé Canvas dans un jeu de serpent simple sur JS et SVG, dans un projet, je l'ai simplement inséré dans la page dans la balise d' objet et j'ai remarqué que lors de la mise à l'échelle, les images SVG restaient toujours nettes (c'est pourquoi c'était un vecteur) et Canvas observé l'image floue. Sur la base de cette observation, j'ai décidé de dessiner un graphique en utilisant SVG, car vous devez commencer un jour.
Plan de travail
Sur la base du cadre Vue sélectionné et de la méthode sélectionnée de formation d'images à l'aide de SVG, je me suis fait le plan de travail suivant:
- Recherchez et étudiez des informations sur le sujet de l'utilisation de SVG avec Vue
- Expériences avec la formation et le changement de SVG dans le contexte de Vue
- Indicateur de chargement du prototype
- Affectation de l'indicateur de chargement dans un composant Vue distinct
- Application du composant dans le SPA
Pour commencer
Création d'un projet viergeJ'ai installé cli vue . Pour créer un nouveau projet, je saisis vue create loadprogresser sur la ligne de commande, sélectionne les paramètres du projet par défaut, crée un nouveau projet vue avec le nom loadprogresser, puis en supprime les inutiles:
Recherchez et étudiez des informations sur le sujet de l'utilisation de SVG avec Vue
Grand site avec des informations utiles sur HTML, CSS et SVG css.yoksel.ru Un bon exemple avec SVG est disponible dans la documentation de Vue elle -même Exemple de graphique SVG et un tel lien . Sur la base de ces matériaux, le modèle de composant minimal avec SVG est né à partir duquel je commence:
<template> <div class="wrapper"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="100%" height="100%"> //svg // svg- </svg> </div> </template>
Expériences avec la formation et le changement de SVG dans le contexte de Vue
SVG rectangle rect
rect - un rectangle, la figure la plus simple. Je crée un svg avec des dimensions 100x100px et dessine un rectangle rect avec les coordonnées initiales 25:25 et des tailles 50x50 px, la couleur de remplissage par défaut est le noir (pas de style)
Style SVG et pseudo-classe de vol stationnaire:
Je vais essayer de styliser le rectangle rect en svg. Pour ce faire, j'ajoute la classe «sample» à svg, dans la section style du fichier vue j'ajoute les styles .sample rect (je colore le rectangle rect avec du jaune) et .sample rect: hover qui stylise l'élément rect lorsque vous survolez dessus:
Code source <template> <div id="app"> <svg class="sample" version="1.1" xmlns="http://www.w3.org/2000/svg" width="100px" height="100px"> <rect x=25 y=25 width="50px" height="50px"/> </svg> </div> </template> <script> export default { name: 'app' } </script> <style> .sample rect { fill: yellow; stroke: green; stroke-width: 4; transition: all 350ms; } .sample rect:hover { fill: gray; } </style>
Implémentation de JSfiddle
Conclusion: svg s'intègre parfaitement dans le modèle vue-file et est stylisé avec les styles prescrits. Un début a été fait!
Chemin SVG comme base de l'indicateur
Dans cette section, je remplacerai rect par path, <path :d="D" class="path"/>
dans l'attribut d de la balise path, je passerai la chaîne D avec les coordonnées du chemin depuis vue. La connexion est établie via v-bind:d="D"
, qui est abrégé en :d="D"
La ligne D = «M 0 0 0 50 50 50 50 0 Z» trace trois lignes avec les coordonnées 0: 0-> 0: 50-> 50: 50-> 0:50 et ferme le contour avec la commande Z, formant un carré de 50x50px à partir de coordonnées 0: 0. En utilisant le style de chemin, la forme reçoit une couleur de remplissage jaune et une bordure grise de 1px.
Source PATH jaune <template> <div id="app"> <svg class="sample" version="1.1" xmlns="http://www.w3.org/2000/svg" width="100px" height="100px"> <path :d="D" class="path"/> </svg> </div> </template> <script> export default { name: 'app', data(){ return { D:"M 0 0 0 50 50 50 50 0 Z" } } } </script> <style> .path { fill:yellow; stroke:gray; } </style>
Indicateur de chargement du prototype
Dans la version minimale, j'ai fait un schéma simple. Un conteneur svg d'une hauteur de 100 pixels, d'une largeur de 400 pixels est inséré dans le modèle, une balise de chemin est placée à l'intérieur, à l'attribut d dont j'ajoute la chaîne de chemin générée d à partir des données de vue, qui à son tour est formée à partir du tableau timePoints, où, toutes les 10 ms, un de 400 est ajouté (par largeur du conteneur) un nombre aléatoire compris entre 0 et 100. Tout est simple, dans le hook de cycle de vie créé, la méthode de mise à jour est appelée dans laquelle de nouveaux points (aléatoires) sont ajoutés au diagramme via la méthode addTime, puis la méthode getSVGTimePoints renvoie une chaîne à transmettre à PATH, via setTimeout redémarre la méthode de mise à jour
En savoir plus sur la formation de cordes pour PATH
La chaîne de PATH est formée dans la méthode getSVGTimePoints, à partir du tableau timePoints que je traite avec Reduce. Comme valeur initiale de réduire, j'utilise «M 0 0» (commencer à la coordonnée 0: 0). Plus loin dans réduire, de nouvelles paires de coordonnées relatives dX et dY seront ajoutées à la ligne. La lettre majuscule "l" est responsable du fait que les coordonnées sont relatives (le grand "L" indique les coordonnées absolues), après "l" est placé dX puis dY, séparés par des espaces. Dans ce prototype, dY = 1 (incrément de 1px), à l'avenir, le long de l'axe X, je me déplacerai avec l'incrément dX calculé à partir de la largeur du conteneur et du nombre de points qui doivent y être placés. Dans la dernière ligne de génération de PATH
path +=`L ${this.timePoints.length} 0`
J'ai forcé, à partir du dernier point, je termine la construction de la ligne sur l'axe X. Si vous devez fermer le contour, vous pouvez ajouter "Z" à la fin de la ligne, au début, je pensais que sans contour fermé, le chiffre résultant ne serait pas rempli (remplissage), mais cela s'est avéré être faux, là où il n'est pas fermé, le trait - le trait ne sera pas tracé.
getSVGTimePoints:function(){ let predY = 0 let path = this.timePoints.reduce((str, item)=>{ let dY = item - predY predY = item return str + `l 1 ${dY} ` },'M 0 0 ') path +=`L ${this.timePoints.length} 0`
Je vais continuer à apporter des modifications. Mon indicateur doit être mis à l'échelle en largeur et en hauteur afin que tous les points transmis s'insèrent dans le conteneur donné. Pour ce faire, tournez-vous vers le DOM et découvrez les dimensions du conteneur
ref - obtenir des informations sur un élément DOM
Au conteneur div (dans lequel svg est inséré), j'ajoute une classe wrapper pour passer la largeur et la hauteur à travers les styles. Et pour que svg occupe tout l'espace du conteneur, règle sa hauteur et sa largeur à 100%. RECT, à son tour, occupera également tout l'espace du conteneur et sera l'arrière-plan de PATH
<div id="app" class="wrapper" ref="loadprogresser"> <svg id="sample" version="1.1" xmlns="http://www.w3.org/2000/svg" width="100%" height="100%"> <rect x=0 y=0 width="100%" height="100%"/> <path :d="d" fill="transparent" stroke="black"/> </svg> </div>
Afin de trouver mon conteneur DIV dans la vue DOM virtuelle, j'ajoute l'attribut ref et je lui donne un nom par lequel je vais chercher ref="loadprogresser"
. Dans le hook de cycle de vie mounted
, j'appellerai la méthode getScales (), dans laquelle, avec la chaîne const {width, height} = this.$refs.loadprogresser.getBoundingClientRect()
je trouve la largeur et la hauteur de l'élément DIV après son apparition dans le DOM.
En outre, des calculs simples de l'incrément le long de l'axe X, en fonction de la largeur du conteneur et du nombre de points que nous voulons y insérer. L'échelle le long de l'axe Y est recalculée chaque fois que le maximum est trouvé dans la valeur transmise.
transformer - changer le système de coordonnées
À ce stade, je remarque que nous devons changer le système de coordonnées de sorte que la coordonnée 0: 0 commence à partir du coin inférieur gauche et que l'axe Y croisse, pas vers le bas. Vous pouvez, bien sûr, faire des calculs pour chaque point, mais SVG a un attribut de transformation qui vous permet de transformer les coordonnées.
Dans mon cas, je dois appliquer une échelle de -1 aux coordonnées Y (afin que les valeurs Y soient reculées) et déplacer l'origine à la hauteur du conteneur moins . Étant donné que la hauteur du conteneur peut être quelconque (spécifiée via les styles), nous avons dû former une ligne de transformation de coordonnées dans le crochet mounted
avec le code suivant: this.transform = `scale( 1, -1) translate(0,${-this.wrapHeight})`
Mais la transformation appliquée à PATH seule ne fonctionnera pas, pour cela, vous devez encapsuler PATH dans un groupe (balise g) auquel les transformations de coordonnées sont appliquées:
<g :transform="transform"> <path :d="d" fill="transparent" stroke="black"/> </g>
En conséquence, les coordonnées se sont inversées correctement, l'indicateur de téléchargement est devenu plus proche de la conception
Texte SVG et centrage du texte
Le texte est nécessaire pour afficher le% de charge. Le placement du texte au centre verticalement et horizontalement dans SVG est assez simple à organiser (par rapport à HTML / CSS), les attributs viennent à la rescousse (je fixe immédiatement les valeurs) dominant-baseline = "central" et text-anchor = "middle"
Le texte en SVG est affiché avec la balise correspondante:
<text x="50%" y="50%" dominant-baseline="central" text-anchor="middle">{{TextPrc}}</text>
où TextPrc est la liaison à la variable correspondante, calculée par un simple rapport du nombre attendu de points au montant transféré this.TextPrc = `${((this.Samples * 100)/this.maxSamples) | 0} %`
this.TextPrc = `${((this.Samples * 100)/this.maxSamples) | 0} %`
.
Les coordonnées du début x = "50%" y = "50%" correspondent au centre du conteneur, et les attributs de ligne de base dominante et d'ancrage de texte sont chargés de s'assurer que le texte est aligné verticalement et horizontalement.
Les choses de base sur le sujet ont été élaborées, nous devons maintenant sélectionner le prototype d'indicateur dans un composant séparé.
Affectation de l'indicateur de chargement dans un composant Vue distinct
Pour commencer, je déterminerai les données que je transférerai au composant, ce seront: maxSamples - le nombre d'échantillons à 100% de largeur, et Point - l'unité de données (point) qui sera entrée dans le tableau de points (sur la base de laquelle, après traitement, elle sera formée horaire). Les données transmises au composant par le parent, je les place dans la section accessoires
props:{ maxSamples: {
Problèmes de réactivité
La propriété calculée getPath est responsable du fait que le nouveau point passé au composant est traité, ce qui dépend de Point (et si c'est le cas, il est recalculé lorsque Point change)
Au début, j'ai fait un Point de type Number, ce qui est logique, mais ensuite tous les points n'ont pas été traités, mais seulement différents des précédents. Par exemple, si seul le nombre 10 est transféré du parent à un tel point, alors un seul point sera dessiné sur le graphique, tous les suivants seront ignorés car ils ne diffèrent pas des précédents.
Le remplacement du type Point de Number par l'objet {value: 0} a conduit au résultat souhaité - la propriété calculée getPath () traite désormais chaque point transmis, via Point.value Je passe les valeurs des points
Source du composant Progresser.vue <template> <div class="wrapper" ref="loadprogresser"> <svg class="wrapper__content" version="1.1" xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" > <g :transform="transform"> <path :d="getPath"/> </g> <text x="50%" y="50%" dominant-baseline="central" text-anchor="middle"> {{TextPrc}} </text> </svg> </div> </template> <script> export default { props:{ maxSamples: {
Appel à partir du composant parent et passage de paramètres
Pour travailler avec un composant, vous devez l'importer dans le composant parent
import Progresser from "./components/Progresser"
et déclarer dans la section
components: {Progresser }
Dans le modèle du composant parent, le composant indicateur de progression est inséré avec la construction suivante:
<progresser class="progresser" :maxSamples = "SamplesInProgresser" :Point = "Point" ></progresser>
Grâce à la classe «progreser», tout d'abord, les tailles de bloc de l'indicateur sont définies. MaxSamples (nombre maximal de points dans le graphique) de la variable parent SamplesInProgresser sont transférés aux accessoires du composant, et le point suivant (sous la forme d'un objet) de la variable Point de l'objet parent est transféré aux accessoires Point. Le point du parent est calculé dans la fonction de mise à jour et représente des nombres aléatoires croissants. Je reçois cette photo:

Source parent App.vue <template> <div> <progresser class="progresser" :maxSamples = "SamplesInProgresser" :Point = "Point" ></progresser> </div> </template> <script> import Progresser from "./components/Progresser" export default { name: 'app', data(){ return { SamplesInProgresser:400,// - Point:{value:0},//"" index:0, // - TimeM:100 // } }, created: function () { this.update() }, methods:{ update(){ if (this.index < this.SamplesInProgresser) { this.index++; this.Point = {value:(this.TimeM*Math.random() | 0)} this.TimeM *= 1.01 setTimeout(this.update, 0) } } }, components: { Progresser } } </script> <style> #app { font-family: 'Avenir', Helvetica, Arial, sans-serif; margin-top: 60px; } /* */ .progresser { width: 300px; height: 80px; } </style>
Application du composant dans le SPA
Arriver au point où tout allait bien. Et donc, j'ai des opérations asynchrones pour charger des enregistrements sur certaines identités de la base de données. Le temps d'exécution d'une opération asynchrone n'est pas connu à l'avance. Je mesurerai le temps d'exécution d'une manière triviale, en utilisant new Date (). GetTime () avant et après l'opération, et je transférerai le décalage horaire résultant au composant. Naturellement, l'indicateur sera intégré dans le bloc qui apparaîtra à l'étape de chargement, et obscurcira la table pour laquelle les données sont chargées.
async getCandidatesData(){ ... this.LoadRecords = true
Dans les données du composant parent que je prescris en ce qui concerne l'indication de charge:
data (){ return { ...
Et dans le modèle:
<div class="wait_loading" v-show="LoadRecords"> <progresser class="progresser" :maxSamples = "SamplesInProgresser" :Point = "Point" ></progresser> </div>
Conclusions
Comme prévu, rien de compliqué. Jusqu'à un certain point, vous pouvez traiter SVG comme des balises HTML standard, avec leurs propres spécificités. SVG est un outil puissant que j'utiliserai plus souvent dans mon travail pour la visualisation des données
Les références
Télécharger le code source de l'indicateurArticle sur le chemin SVG