Aufbau einer Donut-Diagramm mit Vue und SVG

0
10

Mmm… der verbotene donut.”

– Homer Simpson

Vor kurzem musste ich machen ein donut-Diagramm für ein reporting-dashboard bei der Arbeit. Das mock-up, die ich bekam sah so etwas wie dieses:

Meine Grafik hatte, ein paar grundlegende Anforderungen. Es benötigt, um:

  • Dynamisch berechnen Sie die Segmente basierend auf einen beliebigen Satz von Werten
  • Etiketten
  • Gut skalieren über alle Bildschirmgrößen und Geräte
  • Cross-browser-kompatibel zurück zu Internet Explorer 11
  • Zugänglich sein
  • Wiederverwendbar sein, über meine Arbeit Vue.js front-end

Ich wollte auch etwas, dass ich animieren, später, wenn ich Sie brauchte. All das Klang wie ein job für den SVG.

SVGs zugänglich sind out-of-the-box (das W3C hat einen ganzen Abschnitt auf diese) und kann gemacht werden leichter zugänglich durch zusätzlichen input. Und, weil Sie ja angetrieben durch Daten, Sie sind ein perfekter Kandidat für die dynamische Visualisierung.

Es gibt viele Artikel zu dem Thema, darunter zwei von Chris (hier und hier) und einem super kurzem von Burke Holland. Ich habe nicht D3 für dieses Projekt, da die Anwendung nicht den Aufwand der Bibliothek.

Erstellt habe ich die Grafik als Vue-Komponente für mein Projekt, aber Sie könnte genauso gut tun Sie dies mit vanilla JavaScript, HTML und CSS.

Hier ist das fertige Produkt:

Finden Sie den Stift, Vue Donut Diagramm – Final-Version von Salomone Baquis (@soluhmin) auf CodePen.

Das Rad neu erfinden Kreis

Wie jeder selbst-Achtung Entwickler, das erste, was ich Tat, war Google zu sehen, ob jemand anderes schon gemacht. Dann, wie derselbe sagte, developer, ich verschrottet die vorgefertigte Lösung zu Gunsten meiner eigenen.

Die top-Treffer für “SVG-donut-Diagramm” ist dieser Artikel, der beschreibt, wie mit stroke-dasharray und stroke-dashoffset zum zeichnen von mehreren überlagerten Kreisen und schaffen die illusion von einer einzigen segmentierten Kreis (mehr dazu in Kürze).

Ich mag das overlay-Konzept, aber fand die Neuberechnung sowohl stroke-dasharray und stroke-dashoffset Werte verwirrend. Warum nicht einen festen Strich-dasharrary Wert und dann drehen Sie jeden Kreis mit einer Transformation? Ich musste auch Beschriftungen hinzufügen, um jedes segment, das nicht in der Anleitung.

Zeichnen einer Linie

Bevor wir erstellen eine dynamische donut-chart, müssen wir zunächst verstehen, wie die SVG-Linie Zeichnung arbeitet. Wenn Sie noch nicht gelesen Jake Archibald ist hervorragend Animierte Zeichnungen in SVG. Chris hat auch eine gute übersicht.

Diese Artikel bieten die meisten Rahmen, die Sie brauchen, aber kurz, SVG hat zwei Präsentations-Attribute: stroke-dasharray und stroke-dashoffset.

stroke-dasharray definiert eine Reihe von Strichen und Lücken verwendet, um zu malen die Kontur einer Form. Es kann null, einen oder zwei Werte. Der erste Wert definiert die dash Länge; die zweite definiert die Länge der Lücke.

stroke-dashoffset, auf der anderen Seite, die definiert, in dem die Reihe von Strichen und Lücken beginnt. Wenn die stroke-dasharray und stroke-dashoffset Werte sind die Länge der Linie und gleich die gesamte Zeile sichtbar ist, weil wir sagen, das offset (wo die dash-array beginnt) zu beginnen, die am Ende der Zeile. Wenn die stroke-dasharray wird die Länge der Linie, aber die stroke-dashoffset 0 ist, dann ist die Linie unsichtbar, weil wir uns die Verrechnung der erbrachten Teil des Armaturenbretts durch seine gesamte Länge.

Chris’ Beispiel veranschaulicht dies sehr schön:

Finden Sie den Stift einfaches Beispiel für die SVG-Linie Zeichnung, Rückwärts und Vorwärts von Chris Coyier (@chriscoyier) auf CodePen.

Wie erstellen wir das Diagramm

Erstellen der donut-Diagramm der Segmente, werden wir einen separaten Kreis für jeden, überlagern sich die Kreise übereinander, dann verwenden stroke, stroke-dasharray und stroke-dashoffset zeigen nur einen Teil der Kontur des jeweiligen Kreises. Wir werden es dann drehen Sie jedes sichtbare Teil in die richtige position, wodurch die illusion einer einzigen Form. Wie wir das tun, werden wir auch die Berechnung der Koordinaten für die text-Etiketten.

Hier ist ein Beispiel, diese Drehungen und überlagerungen:

Finden Sie die Pen-Kreis Überlagerungen von Salomone Baquis (@soluhmin) auf CodePen.

Basic setup

Wir beginnen mit dem Aufbau unserer Struktur. Ich bin mit x-Vorlage für demo-Zwecke, aber ich würde empfehlen die Schaffung einer einzigen Datei Komponente für die Produktion.

<div id=”app”>
<donut-chart></donut-Diagramm>
</div>
<script type=”text/x-template” id=”donutTemplate”>
<svg height=”160″ width=”160″ viewBox=”0 0 160 160″>
<g-v-=”(Wert, index) in initialValues”>
<circle :cx=”cx” -: cy=”cy” :r=”radius” fill=”transparent” :stroke=”Farben[index]” :stroke-width=”strokeWidth” ></circle>
<text></text>
</g>
</svg>
</script>
Vue.Komponente(‘donutChart’, {
Vorlage: ‘#donutTemplate’,
Requisiten: [“initialValues”],
Daten() {
return {
chartData: [],
Farben: [“#6495ED”, “goldenrod”, “#cd5c5c”, “Distel”, “hellgrau”],
cx: 80,
cy: 80,
radius: 60,
sortedValues: [],
strokeWidth: 30,
}
}
})
neue Vue({
el: “#app”,
Daten() {
return {
Werte: [230, 308, 520, 130, 200]
}
},
});

Mit diesem wir:

  • Unsere Vue-Instanz und unsere donut-chart-Komponente, dann sagen unsere donut-Komponente zu erwarten, dass einige Werte (unser dataset) als Requisiten
  • Schaffen unsere Haupt-SVG-Formen: <Kreis> für die Segmente und <text> für die Etiketten, mit den grundmaßen, der Strichstärke und Farben definiert sind
  • Wickeln Sie diese Formen in ein <g> element, welche Gruppen Sie zusammen
  • Fügen Sie eine v-for-Schleife, um die g> – element, das wir später verwenden, um zu Durchlaufen und jeden Wert, der die Komponente erhält
  • Erstellen Sie eine leere sortedValues array, welches wir verwenden werden, um zu halten eine sortierte version unserer Daten
  • Erstellen Sie eine leere chartData-array, das unsere Haupt-Positionierung Daten

Kreis Länge

Unsere stroke-dasharray sollte die Länge des gesamten Kreis, geben uns eine einfache baseline Zahl, die wir berechnen können jede stroke-dashoffset Wert. Daran erinnern, dass die Länge eines Kreises ist, dessen Umfang und die Formel für den Kreisumfang ist 2nr (Sie erinnern sich, nicht wahr?).

Wir können diese eine berechnete Eigenschaft, die in unserer Komponente.

berechnet: {
Umfang() {
return 2 * Math.PI *.radius
}
}

…und binden Sie den Wert auf unsere Vorlagen-markup.

<svg height=”160″ width=”160″ viewBox=”0 0 160 160″>
<g-v-=”(Wert, index) in initialValues”>
<circle :cx=”cx” -: cy=”cy” :r=”radius” fill=”transparent” :stroke=”Farben[index]” :stroke-width=”strokeWidth” :stroke-dasharray=”Umfang” ></circle>
<text></text>
</g>
</svg>

In der ersten mockup, haben wir gesehen, dass die Segmente gingen vom größten zum kleinsten. Wir können ein weiteres berechnete Eigenschaft Sortieren diese. Wir speichern die sortierte version in den sortedValues array.

sortInitialValues() {
wieder dieses.sortedValues =.initialValues.sort((a,b) => b-a)
}

Schließlich, um für diese sortierten Werte zur Verfügung stehen Vue, bevor das Diagramm gerendert wird, wir wollen zum Verweis auf diese berechnete Eigenschaft aus dem bereitgestellten() lifecycle-Haken.

montiert() {
diese.sortInitialValues
}

Jetzt unsere Tabelle so Aussehen:

Finden Sie den Stift Donut Diagramm – Keine Segmente von Salomone Baquis (@soluhmin) auf CodePen.

Keine Segmente. Nur ein solid-farbigen donut. Wie HTML, SVG-Elemente dargestellt werden, in der Reihenfolge, dass Sie erscheinen im markup. Die Farbe, die angezeigt wird, ist die Konturfarbe der Letzte Kreis im SVG. Weil wir noch nicht Hinzugefügt jede stroke-dashoffset Werte noch, jeder Kreis den Strich geht, den ganzen Weg um. Wir beheben dies, indem Segmente.

Erstellen von Segmenten

Um jedes der Kreissegmente, müssen wir:

  1. Berechnen Sie den prozentualen Anteil der einzelnen Datenwert aus den Daten Werte, die wir passieren, in
  2. Multiplizieren Sie diesen Prozentsatz, um den Umfang, um die Länge des sichtbaren Hub
  3. Subtrahieren Sie die Länge vom Umfang, um die Schlaganfall-offset

Es klingt komplizierter als es ist. Beginnen wir mit einigen Hilfsfunktionen. Zuerst müssen wir insgesamt unsere Daten Werte. Wir können eine berechnete Eigenschaft, dies zu tun.

dataTotal() {
wieder dieses.sortedValues.reduce((acc, val) => acc + val)
},

Zur Berechnung des Anteils jeder Wert, den wir brauchen, um pass in Werte aus der v-for-Schleife, die wir vorhin angelegt haben, das heißt, wir müssen eine Methode hinzufügen.

Methoden: {
dataPercentage(dataVal) {
zurück dataVal /.dataTotal
}
},

Wir haben jetzt genug Informationen, um zu berechnen, die unsere Schlaganfall-offset-Werte, die zu etablieren wird, unsere Kreis-Segmente.

Wir wollen erneut um: (a) multiplizieren unsere Daten Prozentsatz von der Kreis-Umfang, um die Länge des sichtbaren Hub, und (b) abziehen dieser Länge von dem Umfang, um die Schlaganfall-offset.

Hier ist die Methode, um unsere Hub-offsets:

calculateStrokeDashOffset(dataVal, Umfang) {
const strokeDiff =.dataPercentage(dataVal) * Umfang
return Umfang – strokeDiff
},

…die wir uns binden, um unseren Kreis in HTML mit:

:stroke-dashoffset=”calculateStrokeDashOffset(Wert, Umfang)”

Und voilà! Wir sollten etwas wie diese:

Finden Sie den Stift Donut Diagramm – Keine Rotationen von Salomone Baquis (@soluhmin) auf CodePen.

Rotierende Segmente

Jetzt zum lustigen Teil. Alle Segmente beginnen bei 3 Uhr, das ist der standardmäßige Ausgangspunkt für SVG-Kreisen. Um Sie in den richtigen Ort, wir müssen drehen jedes segment in der richtigen position befindet.

Wir können dies tun, indem Sie finden jedes segment s-Verhältnis von 360 Grad und dann offset, der Betrag durch die Gesamtzahl der Abschlüsse, die vor kam.

Erste, lassen Sie ‘ s fügen Sie eine data-Eigenschaft, um zu verfolgen, offset:

angleOffset: -90,

Dann ist unsere Berechnung (dies ist eine berechnete Eigenschaft):

calculateChartData() {
diese.sortedValues.forEach((dataVal, index) => {
const data = {
Grad: dies.angleOffset,
}
diese.chartData.push(data)
diese.angleOffset =.dataPercentage(dataVal) * 360 +.angleOffset
})
},

Jede Schleife erzeugt ein neues Objekt mit einem “Grad” – Eigenschaft, drückt in unser chartValues array, das wir zuvor erstellt haben, und aktualisiert dann die angleOffset für die next-Schleife.

Aber warte, was ist mit der-90-Wert?

Gut, ein Blick zurück auf unsere ursprüngliche mockup, das erste segment erscheint in der 12-Uhr-position, oder -90 Grad von der Ausgangspunkt. Durch die Einstellung unserer angleOffset bei -90 gewährleisten wir, dass unsere größte donut-segment beginnt oben.

Zum drehen dieser Segmente im HTML, verwenden wir die Transformation Präsentation Attribut mit der drehen-Funktion. Erstellen wir einen weiteren berechnete Eigenschaft so ein, dass wir zurückkehren können, einen schönen, formatierte Zeichenfolge.

returnCircleTransformValue(index) {
zurück drehen(${dies.chartData[index].Grad}, ${dies.cx}, ${dies.cy})`
},

Die drehen-Funktion akzeptiert drei Argumente: einen Winkel der rotation und die x-und y-Koordinaten, um die die Winkel dreht. Wenn wir nicht liefern cx und cy die Koordinaten, dann ist unsere Segmente drehen sich um die gesamte SVG-Koordinatensystem.

Als Nächstes binden wir diese in unserem Kreis markup.

:transform=”returnCircleTransformValue(index)”

Und da müssen wir alle diese Berechnungen bevor das Diagramm dargestellt wird, fügen wir unsere calculateChartData berechnete Eigenschaft im montierten Haken:

montiert() {
diese.sortInitialValues
diese.calculateChartData
}

Schließlich, wenn wir wollen, dass sweet, sweet Lücke zwischen jedem segment, wir subtrahieren die beiden aus der Umfang und nutzen diese als unsere neue stroke-dasharray.

adjustedCircumference() {
wieder dieses.Umfang – 2
},
:stroke-dasharray=”adjustedCircumference”

Segmente, baby!

Finden Sie den Stift Donut Diagramm – Segmente Nur von Salomone Baquis (@soluhmin) auf CodePen.

Etiketten

Wir haben unsere Segmente, aber wir müssen uns jetzt um Etiketten zu erstellen. Dies bedeutet, dass wir brauchen, um unsere <text> – Elemente mit x-und y-Koordinaten an verschiedenen Punkten entlang der Kreis. Man könnte vermuten, dass dies erfordert Mathematik. Leider sind Sie richtig.

Glücklicherweise, dies ist nicht die Art von Mathe, wo wir anwenden müssen Echte Konzepte; das ist mehr die Art, wo wir Google Formeln und nicht zu viele Fragen zu stellen.

Laut Internet werden die Formeln zur Berechnung von x und y Punkte auf einem Kreis sind:

x = r cos(t) + a
y = r sin(t) + b

…wo r der radius, t die Winkel, und a und b sind die x-und y-Mitte Punkt offsets.

Wir haben bereits die meisten dieser: wir kennen unsere radius, wissen wir, wie wir kalkulieren unsere segment-Winkeln, und wir wissen, dass unsere center offset-Werte (cx und cy).

Es ist ein Fang, aber: in diesen Formeln ist t in *Bogenmaß*. Wir arbeiten in Grad, was bedeutet, dass wir tun müssen, um einige Konvertierungen. Wieder eine schnelle Suche wird eine Formel:

Bogenmaß = Grad * π / 180)

…die stellen wir in eine Methode:

degreesToRadians(Winkel) {
return angle * (Math.PI / 180)
},

Wir haben jetzt genug Informationen zur Ermittlung der x-und y-text-Koordinaten:

calculateTextCoords(dataVal, angleOffset) {
const Winkel = (.dataPercentage(dataVal) * 360) / 2 + angleOffset
const radians =.degreesToRadians(Winkel)

const textCoords = {
x: (.radius * Math.cos(Bogenmaß) +.cx),
y: (.radius * Math.sin(Bogenmaß) +.cy)
}
zurück textCoords
},

Zuerst berechnen wir die Winkel unserer segment durch die Multiplikation des Verhältnisses unserer Daten Wert durch 360; jedoch, wir wollen tatsächlich, dass die Hälfte davon, weil unser text-labels sind in der Mitte des Segments, nicht der Zweck. Müssen wir hinzufügen, den Winkel, offset, wie wir es beim erstellen der Segmente.

Unsere calculateTextCoords Methode kann nun verwendet werden in der calculateChartData berechnete Eigenschaft:

calculateChartData() {
diese.sortedValues.forEach((dataVal, index) => {
const { x, y } =.calculateTextCoords(dataVal, diese.angleOffset)
const data = {
Grad: dies.angleOffset,
Texte: x,
textY: y
}
diese.chartData.push(data)
diese.angleOffset =.dataPercentage(dataVal) * 360 +.angleOffset
})
},

Wir werden auch hinzufügen, eine Methode zum zurückgeben der label-string:

percentageLabel(dataVal) {
return ” ${Math.Runde(dies.dataPercentage(dataVal) * 100)}%`
},

Und in der markup:

<text :x=”chartData[index].Texte” :y=”chartData[index].textY”>{{ percentageLabel(Wert) }}</text>

Jetzt haben wir Etiketten:

Finden Sie den Stift Donut Diagramm – Unformatiert-Etiketten von Salomone Baquis (@soluhmin) auf CodePen.

Blech, also außerhalb der Mitte. Wir können dieses Problem beheben, mit dem text-Anker-Präsentation-Attribut. Je nach schriftart und-Größe, möchten Sie vielleicht einstellen, die Positionierung als gut. Check-out dx und dy.

Überarbeitete text-element:

<text text-anchor=”middle” dy=”3px” :x=”chartData[index].Texte” :y=”chartData[index].textY”>{{ percentageLabel(Wert) }}</text>

Hmm, es scheint, dass, wenn wir in kleinen Prozentsätzen, die Etiketten gehen außerhalb der Segmente. Fügen wir eine Methode um dies zu überprüfen.

segmentBigEnough(dataVal) {
return Math.Runde(dies.dataPercentage(dataVal) * 100) > 5
}
<text v-if=”segmentBigEnough(Wert)” text-anchor=”middle” dy=”3px” :x=”chartData[index].Texte” :y=”chartData[index].textY”>{{ percentageLabel(Wert) }}</text>

Nun, wir werden nur Beschriftungen hinzufügen, um Segmente, die größer als 5%.

Und dann sind wir fertig! Wir haben jetzt eine wiederverwendbare donut Diagramm-Komponente akzeptieren kann, der einen beliebigen Satz von Werten und erstellen von Segmenten. Super cool!

Das fertige Produkt:

Finden Sie den Stift, Vue Donut Diagramm – Final-Version von Salomone Baquis (@soluhmin) auf CodePen.

Die nächsten Schritte

Es gibt viele Möglichkeiten, wir können Sie modifizieren oder verbessern, nun, wo es gebaut wird. Zum Beispiel:

  • Hinzufügen von Elementen zur Verbesserung der Barrierefreiheit, wie <title> und <desc> – tags, aria-Label und aria role Attribute.
  • Erstellen von Animationen mit CSS oder Bibliotheken, wie Greensock zu erstellen, eye-catching Effekte, wenn das Diagramm in Sicht kommt.
  • Spielen mit Farbschemata.

Ich würde gerne hören, was Sie denken, über diese Umsetzung und andere Erfahrungen, die Sie gehabt haben mit SVG-charts. In den Kommentaren mitteilen!

Das Jetpack WordPress-plugin läuft auf dieser Website, indem Sie nicht nur die verwandten Beiträge unten, aber die social sharing links oben, der Sicherheit und des backups, Markdown-support, site-Suche, das Kommentar-Formular, postulieren soziale Netzwerk-verbindungen und vieles mehr!