Einfaches interaktives Tortendiagramm mit CSS-Variablen und Houdini-Magie

Avatar of Ana Tudor
Ana Tudor am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Die Idee dazu kam mir, als ich auf dieses interaktive SVG-Tortendiagramm stieß. Während der SVG-Code so kompakt wie möglich ist (ein einziges <circle>-Element!), ist die Verwendung von Strichen zur Erstellung von Tortendiagrammsegmenten problematisch, da wir unter Rendering-Problemen unter Windows für Firefox und Edge leiden. Außerdem können wir 2018 mit viel weniger JavaScript viel mehr erreichen!

Ich habe es geschafft, das folgende Ergebnis mit einem einzigen HTML-Element für das Diagramm und sehr wenig JavaScript zu erzielen. Die Zukunft sollte die Notwendigkeit von JavaScript vollständig eliminieren, aber dazu später mehr.

Animated gif. Shows how the final pie chart behaves. We have a value label over our pie chart and three radio buttons below it. Each radio button selects a year and sets the pie chart to show the value for the selected year. When changing the year and, consequently the value, the pie chart animates from its previous state to the current one.
Das endgültige Ergebnis des Tortendiagramms.

Einige von Ihnen erinnern sich vielleicht an Lea Verou's Missing Slice-Vortrag – meine Lösung basiert auf ihrer Technik. Dieser Artikel zerlegt, wie das alles funktioniert, und zeigt, was wir in Bezug auf graceful degradation tun können und wie diese Technik sonst noch eingesetzt werden kann.

Das HTML

Wir verwenden Pug, um das HTML aus einem data-Objekt zu generieren, das einheitenlose Prozentwerte für die letzten drei Jahre enthält.

- var data = { 2016: 20, 2017: 26, 2018: 29 }

Wir platzieren alle unsere Elemente in einem .wrap-Element. Dann durchlaufen wir unser data-Objekt und erstellen für jede seiner Eigenschaften einen Radio-input mit einem entsprechenden label. Danach fügen wir ein .pie-Element hinzu.

- var darr = [], val;

.wrap
  - for(var p in data) {
    - if(!val) val = data[p];
    input(id=`o${p}` name='o' type='radio' checked=val == data[p])
    label(for=`o${p}`) #{p}
    - darr.push(`${data[p]}% for year ${p}`)
  - }
  .pie(aria-label=`Value as pie chart. ${darr.join(', ')}.` 
       role='graphics-document group')

Der obige Pug-Code wird zu folgendem HTML kompiliert:

<style><div class="wrap">
  <input id="o2016" name="o" type="radio" checked="checked"/>
  <label for="o2016">2016</label>
  <input id="o2017" name="o" type="radio"/>
  <label for="o2017">2017</label>
  <input id="o2018" name="o" type="radio"/>
  <label for="o2018">2018</label>
  <div class="pie" aria-label="Value as pie chart. 20% for year 2016, 26% for year 2017, 29% for year 2018." role="graphics-document group"></div>
</div>

Beachten Sie, dass wir auch sichergestellt haben, dass nur der erste Radio-input ausgewählt ist.

Übergabe benutzerdefinierter Eigenschaften an das CSS

Ich mag es normalerweise nicht, Stile in HTML zu platzieren, aber in diesem speziellen Fall ist es eine sehr nützliche Möglichkeit, benutzerdefinierte Eigenschaftswerte an das CSS zu übergeben und sicherzustellen, dass wir nur an einer Stelle etwas ändern müssen, wenn wir Datenpunkte ändern müssen – dem Pug-Code. Das CSS bleibt dasselbe.

Der Trick besteht darin, auf dem .pie-Element für jeden Radio-input, der ausgewählt sein könnte, ein einheitenloses Prozentzeichen --p zu setzen.

style
  - for(var p in data) {
    | #o#{p}:checked ~ .pie { --p: #{data[p]} }
  - }

Wir verwenden dieses Prozentzeichen für einen conic-gradient() auf dem .pie-Element, nachdem wir sichergestellt haben, dass keine seiner Dimensionen (einschließlich border und padding) 0 ist.

$d: 20rem;

.wrap { width: $d; }

.pie {
  padding: 50%;
  background: conic-gradient(#ab3e5b calc(var(--p)*1%), #ef746f 0%);
}

Beachten Sie, dass dies native conic-gradient()-Unterstützung erfordert, da das Polyfill nicht mit CSS-Variablen funktioniert. Derzeit beschränkt dies die Unterstützung auf Blink-Browser mit aktiviertem Flag Experimental Web Platform features, aber die Dinge werden sich wahrscheinlich verbessern.

Screenshot showing the Experimental Web Platform Features flag being enabled in Chrome.
Das Flag Experimental Web Platform features ist in Chrome aktiviert.

Update: Chrome 69+ unterstützt conic-gradient() nun auch nativ ohne das Flag.

Wir haben jetzt ein funktionierendes Grundgerüst unserer Demo – die Auswahl eines anderen Jahres über die Radiobuttons führt zu einem anderen conic-gradient()!

Animated gif. Shows how picking a different year via the radio buttons changes the chart to show the value for the selected year.
Die grundlegende Funktionalität, die wir gesucht haben (Live-Demo, nur Blink-Browser).

Anzeige des Werts

Der nächste Schritt ist die tatsächliche Anzeige des aktuellen Werts, und das tun wir über ein Pseudo-Element. Leider können zahlenbasierte CSS-Variablen nicht für den Wert der content-Eigenschaft verwendet werden. Daher umgehen wir dies mit dem counter()-Hack.

.pie:after {
  counter-reset: p var(--p);
  content: counter(p) '%';
}

Wir haben auch die Eigenschaften color und font-size angepasst, damit unser Pseudo-Element etwas besser sichtbar ist.

Screenshot. Shows the chart with the value for the current year (the one selected by the checked radio button) overlayed on top.
Anzeige des Werts auf dem Diagramm (Live-Demo, nur Blink-Browser).

Verfeinerung

Wir wollen keine abrupten Änderungen zwischen den Werten, daher glätten wir die Übergänge mit Hilfe einer CSS-transition. Bevor wir die --p-Variable überblenden oder animieren können, müssen wir sie in JavaScript registrieren.

CSS.registerProperty({
  name: '--p', 
  syntax: '<integer>',
  initialValue: 0, 
  inherits: true
});

Beachten Sie, dass die Verwendung von <number> anstelle von <integer> dazu führt, dass der angezeigte Wert während des transition auf 0 fällt, da unser Zähler eine Ganzzahl benötigt. Danke an Lea Verou, die mir geholfen hat, das herauszufinden!

Beachten Sie auch, dass die explizite Einstellung von inherits zwingend erforderlich ist. Dies war bis vor kurzem nicht der Fall.

Das ist alles JavaScript, was wir für diese Demo benötigen. Zukünftig wird nicht einmal mehr so viel benötigt, da wir benutzerdefinierte Eigenschaften aus CSS registrieren können.

Damit ist die Funktionalität abgeschlossen, wir können eine transition zu unserem .pie-Element hinzufügen.

.pie {
  /* same styles as before */
  transition: --p .5s;
}

Und das war's mit der Funktionalität! Alles erledigt mit einem Element, einer benutzerdefinierten Variable und einer Prise Houdini-Magie!

Animated gif. Shows how picking a different year via the radio buttons smoothly changes both the actual chart and the number value displayed on top of it to represent the value for the newly selected year.
Interaktives Tortendiagramm (Live-Demo, nur Blink-Browser mit Flag).

Schickmacher-Details

Während unsere Demo funktionsfähig ist, sieht sie zu diesem Zeitpunkt alles andere als hübsch aus. Also kümmern wir uns auch darum!

Das Tortendiagramm zu einem Tortendiagramm machen!

Da die Anwesenheit von :after die height des übergeordneten .pie-Elements erhöht hat, positionieren wir es absolut. Und da wir möchten, dass unser .pie-Element eher wie eine echte Torte aussieht, machen wir es mit border-radius: 50% rund.

Screenshot. Shows the chart now being perfectly round like a pie.
Aufrunden unseres Tortendiagramms (Live-Demo, nur Blink-Browser, Übergang benötigt Flag).

Wir wollen auch den Prozentwert in der Mitte des dunklen Tortensegments anzeigen.

Um dies zu tun, positionieren wir es zuerst genau in der Mitte des .pie-Elements. Standardmäßig wird das :after-Pseudo-Element nach dem Inhalt seines übergeordneten Elements angezeigt. Da .pie in diesem Fall keinen Inhalt hat, befindet sich die obere linke Ecke des :after-Pseudo-Elements in der oberen linken Ecke des content-box des übergeordneten Elements. Hier ist die content-box eine 0x0-Box in der Mitte der padding-box. Denken Sie daran, dass wir das padding von .pie auf 50% gesetzt haben – ein Wert, der sich sowohl in horizontaler als auch in vertikaler Richtung auf die Breite des übergeordneten Elements bezieht!

Das bedeutet, dass sich die obere linke Ecke von :after in der Mitte seines übergeordneten Elements befindet. Ein translate(-50%, -50%) verschiebt es also nach links um die Hälfte seiner eigenen width und nach oben um die Hälfte seiner eigenen height, sodass sich sein Mittelpunkt mit dem von .pie überschneidet.

Denken Sie daran, dass %-basierte Übersetzungen relativ zu den Dimensionen *des Elements sind, auf das sie angewendet werden*, entlang der entsprechenden Achse. Mit anderen Worten, eine %-basierte Übersetzung entlang der x-Achse bezieht sich auf die width des Elements, eine %-basierte Übersetzung entlang der y-Achse bezieht sich auf seine height und eine %-basierte Übersetzung entlang der z-Achse bezieht sich auf seine Tiefe, die immer 0 ist, da alle Elemente flache zweidimensionale Boxen mit 0 Tiefe entlang der dritten Achse sind.

Screenshot. Shows the chart now being perfectly round like a pie, with the percentage as text positioned right in the middle.
Positionierung des Wertlabels in der Mitte des Tortendiagramms (Live-Demo, nur Blink-Browser, Übergang benötigt Flag).

Als Nächstes rotieren wir den Wert so, dass die positive Hälfte seiner x-Achse das dunkle Segment in zwei gleiche Hälften teilt, und übersetzen ihn dann um den halben Radius des Tortendiagramms entlang dieser nun rotierten x-Achse.

Siehe den Pen von thebabydino (@thebabydino) auf CodePen.

Wir müssen herausfinden, wie viel wir das :after-Pseudo-Element drehen müssen, damit seine x-Achse das dunkle Segment in zwei gleiche Hälften teilt. Lassen Sie uns das aufschlüsseln!

Anfänglich ist die x-Achse horizontal und zeigt nach rechts. Um sie in die gewünschte Richtung zu bringen, müssen wir sie zunächst drehen, damit sie nach oben zeigt und entlang des Startrandes des Segments verläuft. Dann muss sie im Uhrzeigersinn um die Hälfte eines Segments gedreht werden.

Um die Achse nach oben zeigen zu lassen, müssen wir sie um -90deg drehen. Das Minuszeichen ist darauf zurückzuführen, dass positive Werte eine Uhrzeigerichtung haben und wir uns in die andere Richtung bewegen.

Siehe den Pen von thebabydino (@thebabydino) auf CodePen.

Als Nächstes müssen wir sie um die Hälfte eines Segments drehen.

Siehe den Pen von thebabydino (@thebabydino) auf CodePen.

Aber wie viel ist die Hälfte eines Segments?

Nun, wir wissen bereits, welchen Prozentsatz des Tortendiagramms dieses Segment darstellt: das ist unsere benutzerdefinierte Eigenschaft --p. Wenn wir diesen Wert durch 100 teilen und dann mit 360deg (oder 1turn, es spielt keine Rolle, welche Einheit verwendet wird) multiplizieren, erhalten wir den Zentralwinkel unseres dunklen Segments.

Nach der -90deg-Drehung müssen wir :after im Uhrzeigersinn (positiv) um die Hälfte dieses Zentralwinkels drehen.

Das bedeutet, wir wenden die folgende transform-Kette an:

translate(-50%, -50%) rotate(calc(.5*var(--p)/100*1turn - 90deg)) translate(.25*$d);

Die letzte Übersetzung ist um ein Viertel von $d, was die width des Wrappers ist und uns auch den Durchmesser von .pie gibt. (Da die content-box von .pie eine 0x0-Box ist, hat sie keine border und ihr linker und rechter padding betragen jeweils 50% der Breite des übergeordneten Wrappers.) Der Radius von .pie ist die Hälfte seines Durchmessers, was bedeutet, dass die Hälfte des Radius ein Viertel des Durchmessers ($d) ist.

Jetzt ist das Wertlabel dort positioniert, wo wir es haben wollen.

Screenshot. Shows the chart now being perfectly round like a pie, with the percentage as text positioned right in the middle of the slice of pie it represents. It is however rotated such that its x axis is along the radial line splitting this slice into two equal halves.
Positionierung des Wertlabels in der Mitte des Segments (Live-Demo, nur Blink-Browser, Übergang benötigt Flag).

Es gibt jedoch immer noch ein Problem: Wir möchten nicht, dass es gedreht ist, da dies bei bestimmten Winkeln sehr seltsam und nackenbrecherisch aussehen kann. Um dies zu beheben, setzen wir die Drehung am Ende zurück. Um es uns leichter zu machen, speichern wir den Drehwinkel in einer CSS-Variable namens --a.

--a: calc(.5*var(--p)/100*1turn - 90deg);
transform: 
  translate(-50%, -50%) 
  rotate(var(--a)) 
  translate(.25*$d) 
  rotate(calc(-1*var(--a)));

Viel besser!

Screenshot. Shows the chart now being perfectly round like a pie, with the percentage as text positioned horizontally right in the middle of the slice of pie it represents.
Positionierung des Wertlabels in der Mitte des Segments, nun horizontal (Live-Demo, nur Blink-Browser, Übergang benötigt Flag).

Layout

Wir wollen die gesamte Anordnung in der Mitte des Bildschirms haben, also lösen wir das mit einem kleinen, sauberen Grid-Trick.

body {
  display: grid;
  place-items: center center;
  margin: 0;
  min-height: 100vh
}

Okay, das platziert das gesamte .wrap-Element in der Mitte.

Screenshot. Shows the whole assembly in the middle of the page.
Positionierung der gesamten Anordnung in der Mitte (Live-Demo, nur Blink-Browser, Übergang benötigt Flag).

Der nächste Schritt ist, das Tortendiagramm über den Radio-Buttons zu platzieren. Das machen wir mit einem Flexbox-Layout auf dem .wrap-Element.

.wrap {
  display: flex;
  flex-wrap: wrap-reverse;
  justify-content: center;
  width: $d;
}
Screenshot. Shows the whole assembly in the middle of the page, with the pie chart now above the radio buttons.
Platzierung des Tortendiagramms über den Radio-Buttons (Live-Demo, nur Blink-Browser, Übergang benötigt Flag).

Styling der Radio-Buttons

…oder genauer gesagt, wir stylen die Labels der Radio-Buttons, denn das Erste, was wir tun, ist, die Radio-Inputs zu verstecken.

[type='radio'] {
  position: absolute;
  left: -100vw;
}
Screenshot. Shows the whole assembly in the middle of the page, with the pie chart above the radio button labels. The radio buttons are now hidden and it's difficult to distinguish the labels from one another.
Nachdem die Radio-Buttons versteckt wurden (Live-Demo, nur Blink-Browser, Übergang benötigt Flag).

Da uns dies sehr hässliche und schwer zu unterscheidende Labels hinterlässt, geben wir jedem etwas margin und padding, damit sie nicht so gequetscht aussehen, sowie Hintergründe, damit ihre klickbaren Bereiche klar hervorgehoben sind. Wir können sogar Box- und Textschatten für einige 3D-Effekte hinzufügen. Und natürlich können wir einen separaten Fall erstellen, wenn ihre entsprechenden Eingaben :checked sind.

$c: #ecf081 #b3cc57;

[type='radio'] {
   /* same as before */

  + label {
    margin: 3em .5em .5em;
    padding: .25em .5em;
    border-radius: 5px;
    box-shadow: 1px 1px nth($c, 2);
    background: nth($c, 1);
    font-size: 1.25em;
    text-shadow: 1px 1px #fff;
    cursor: pointer;
  }
	
  &:checked {
    + label {
      box-shadow: inset -1px -1px nth($c, 1);
      background: nth($c, 2);
      color: #fff;
      text-shadow: 1px 1px #000;
    }
  }
}

Wir haben auch die font-size etwas erhöht und ein border-radius gesetzt, um die Ecken abzurunden.

Screenshot. Shows the whole assembly in the middle of the page, with the pie chart above the radio button labels. The radio buttons are now hidden, but the labels have now been styled to be clearly distinguishable from one another.
Nach dem Styling der Radio-Button-Labels (Live-Demo, nur Blink-Browser, Übergang benötigt Flag).

Abschließende Schickmacher-Details

Wir setzen einen background für das body, passen die font des gesamten Elements an und fügen eine transition für die Radio-Labels hinzu.

Screenshot. Shows the final result with the pie chart above the radio button labels, styled to be clearly distinguishable from one another and a sans-serif font and vivid background.
Das endgültige Tortendiagramm (Live-Demo, nur Blink-Browser, Übergang benötigt Flag).

Graceful Degradation

Während unsere Demo in Blink-Browsern jetzt gut aussieht, sieht sie in allen anderen Browsern schrecklich aus – und das sind die meisten Browser!

Zuerst packen wir unsere Arbeit in einen @supports-Block, der auf native conic-gradient()-Unterstützung prüft, damit Browser, die diese unterstützen, das Tortendiagramm rendern. Dies beinhaltet unser conic-gradient(), das padding, das dem Tortendiagramm gleiche horizontale und vertikale Dimensionen verleiht, das border-radius, das das Tortendiagramm kreisförmig macht, und die transform-Kette, die das Wertlabel in der Mitte des Tortensegments positioniert.

.pie {	
  @supports (background: conic-gradient(tan, red)) {
    padding: 50%;
    border-radius: 50%;
    background: conic-gradient(var(--stop-list));
    --a: calc(.5*var(--p)/100*1turn - 90deg);
    --pos: rotate(var(--a)) 
           translate(#{.25*$d}) 
           rotate(calc(-1*var(--a)));
    }
  }
}

Nun konstruieren wir ein Balkendiagramm als Fallback für alle anderen Browser mit linear-gradient(). Wir wollen, dass sich unser Balken über das .wrap-Element erstreckt, sodass der horizontale padding weiterhin 50% beträgt, aber vertikal als schmaler Balken. Wir wollen immer noch, dass das Diagramm hoch genug ist, um das Wertlabel aufzunehmen. Das bedeutet, wir wählen einen kleineren vertikalen padding. Wir verringern auch den border-radius, da 50% uns eine Ellipse ergeben würden und wir ein Rechteck mit leicht abgerundeten Ecken benötigen.

Der Fallback ersetzt auch conic-gradient() durch einen von links nach rechts verlaufenden linear-gradient(). Da sowohl der linear-gradient(), der das Fallback-Balkendiagramm erstellt, als auch der conic-gradient(), der das Tortendiagramm erstellt, die gleiche Stoppliste verwenden, können wir sie in einer CSS-Variable (--stop-list) speichern, damit sie nicht einmal im kompilierten CSS wiederholt werden muss.

Schließlich wollen wir im Fallback das Wertlabel in der Mitte des Balkens haben, da wir keine Tortensegmente mehr haben. Das bedeutet, wir speichern alle Positionierungen nach der Zentrierung in einer CSS-Variable (--pos), deren Wert im Fall ohne conic-gradient()-Unterstützung nichts ist und andernfalls die vorherige transform-Kette.

.pie {
  padding: 1.5em 50%;
  border-radius: 5px;
  --stop-list: #ab3e5b calc(var(--p)*1%), #ef746f 0%;
  background: linear-gradient(90deg, var(--stop-list));
  /* same as before */
	
  &:after {
    transform: translate(-50%, -50%) var(--pos, #{unquote(' ')});
    /* same as before */
  }
	
  @supports (background: conic-gradient(tan, red)) {
    padding: 50%;
    border-radius: 50%;
    background: conic-gradient(var(--stop-list));
    --a: calc(.5*var(--p)/100*1turn - 90deg);
    --pos: rotate(var(--a)) 
           translate(#{.25*$d}) 
           rotate(calc(-1*var(--a)));
    }
  }
}

Wir wechseln auch zu einem Flexbox-Layout auf dem body (da das Grid-Layout, so clever es auch sein mag, in Edge fehlerhaft ist).

body {
  display: flex;
  align-items: center;
  justify-content: center;
  /* same as before */
}

Dies ergibt einen Fallback als Balkendiagramm für Browser, die conic-gradient() nicht unterstützen.

Screenshot. Shows the fallback for the final result in browsers not supporting conic-gradient().
Der Fallback für das endgültige Tortendiagramm (Live-Demo).

Alles responsiv machen

Das einzige Problem, das wir noch haben, ist, dass es nicht mehr gut aussieht, wenn die Viewport-Breite kleiner ist als der Durchmesser des Tortendiagramms.

CSS-Variablen und Media Queries zur Rettung!

Wir setzen den Durchmesser auf eine CSS-Variable (--d), die verwendet wird, um die Dimensionen des Tortendiagramms und die Position des Wertlabels in der Mitte unseres Segments festzulegen.

.wrap {
  --d: #{$d};
  width: var(--d);
  /* same as before */

  @media (max-width: $d) { --d: 95vw }
}

Unterhalb bestimmter Viewport-Breiten verringern wir auch die font-size, die margin für unsere <label>-Elemente, und wir positionieren das Wertlabel nicht mehr in der Mitte des dunklen Tortensegments, sondern in der Mitte des Tortendiagramms selbst.

.wrap {
 /* same as before */

@media (max-width: 265px) { font-size: .75em; }
}

[type='radio'] {
   /* same as before */

  + label {
    /* same as before */
    
    @media (max-width: 195px) { margin-top: .25em; }
  }
}

.pie{
   /* same as before */

  @media (max-width: 160px) { --pos: #{unquote(' ')} } 
}

Dies ergibt unser Endergebnis: ein responsives Tortendiagramm in Browsern, die conic-gradient() nativ unterstützen. Und obwohl das leider derzeit nur Blink-Browser sind, haben wir einen soliden Fallback, der ein responsives Balkendiagramm für alle anderen Browser rendert. Wir animieren auch zwischen Werten – dies ist derzeit noch stärker eingeschränkt, nur auf Blink-Browser mit aktiviertem Flag für experimentelle Webplattformfunktionen.

Siehe den Pen von thebabydino (@thebabydino) auf CodePen.

Bonus: radialer Fortschritt!

Wir können dieses Konzept auch anwenden, um einen radialen Fortschrittsanzeiger wie den unten stehenden zu erstellen (inspiriert von diesem Pen).

Animated gif. Shows how the final radial progress behaves and its fallback. We have a value label inside/ over our radial/ linear progress and a button below this progress. Clicking the button generates a new random percentage to indicate the progress. When changing the percentage value, the radial progress animates from its previous state to the current one.
Der radiale Fortschritt und sein Fallback.

Die Technik ist so gut wie gleich, abgesehen davon, dass wir das Wertlabel genau in der Mitte lassen und den conic-gradient() auf das :before-Pseudo-Element setzen. Das liegt daran, dass wir eine mask verwenden, um alles außer einem dünnen äußeren Ring zu entfernen. Wenn wir den conic-gradient() und die mask auf das Element selbst setzen würden, würde die mask auch das Wertlabel im Inneren ausblenden, und wir möchten, dass dieses sichtbar ist.

Beim Klicken auf den <button> wird ein neuer Wert für unser einheitenloses Prozentzeichen (--p) zufällig generiert, und wir wechseln sanft zwischen den Werten. Wenn wir eine feste transition-duration einstellen, würde dies zu einem wirklich langsamen transition zwischen zwei nahen Werten (z.B. 47% auf 49%) und einem wirklich schnellen transition beim Wechsel zwischen Werten mit einer größeren Lücke dazwischen (z.B. 3% auf 98%) führen. Wir umgehen dies, indem wir die transition-duration vom absoluten Wert der Differenz zwischen dem vorherigen Wert von --p und seinem neu generierten Wert abhängig machen.

[id='out'] { /* radial progress element */
  transition: --p calc(var(--dp, 1)*1s) ease-out;
}
const _GEN = document.getElementById('gen'), 
             _OUT = document.getElementById('out');

_GEN.addEventListener('click', e => {
  let old_perc = ~~_OUT.style.getPropertyValue('--p'), 
      new_perc = Math.round(100*Math.random());
	
  _OUT.style.setProperty('--p', new_perc);
  _OUT.style.setProperty('--dp', .01*Math.abs(old_perc - new_perc));
  _OUT.setAttribute('aria-label', `Graphical representation of generated percentage: ${new_perc}% of 100%.`)
}, false);

Dies ergibt uns eine schöne animierte radiale Fortschrittsanzeige für Browser, die alle neuen und glänzenden Funktionen unterstützen. Wir haben einen linearen Fallback für Browser, die conic-gradient() nicht nativ unterstützen, und keine transition im Falle fehlender Houdini-Unterstützung.

Siehe den Pen von thebabydino (@thebabydino) auf CodePen.