Tænk chartet som tre dele
Det er nemmere at forstå doughnut-chartet, hvis man splitter det op i tre lag:
- ringen selv
- markøren, der bevæger sig rundt på kanten
- tallet i midten
Del 1: conic-gradient til selve ringen
Først bygges ringen med lagdelte backgrounds og conic-gradient(). Et transparent border-spor giver plads til den ydre ring, mens padding-box og border-box bruges til at holde center og ring adskilt.
Det vigtige her er ikke kun gradienten, men også at chartet bliver bygget som en rigtig komponent med en tydelig indre flade og en ydre visuel kant.
Del 2: offset-path til markøren
Dernæst kan en lille markør følge kanten med offset-path og offset-distance. Her bliver procenten ikke bare vist som farve på ringen, men også omsat til en faktisk position langs kanten.
Det stærke mønster er, at den samme procentværdi kan bruges flere steder på én gang: både til ringens fyldning og til markørens placering.
Del 3: tallet i midten med counter()
Teksten i midten bliver ikke skrevet direkte med en custom property. I stedet bruges en CSS-counter:
&::after { counter-reset: val round(var(--animatedNumber)); content: counter(val) "%";}counter-reset opretter en counter med navnet val, og counter(val) læser den igen i content. Det smarte er, at tallet så kan vises i et pseudo-element uden ekstra markup.
Grunden til round() er, at værdien i counter-reset skal være et helt tal, før det bliver vist.
Del 4: sæt det hele sammen
I den samlede version bruges @property --animatedNumber til at animere tallet glidende. Det bliver omsat til --percentage med calc(var(--animatedNumber) * 1%), som så driver både conic-gradient, markør og talvisning.
::before bruges til markøren på kanten, og ::after bruges til procenttallet i midten. Det gør komponenten mere selvstændig, fordi markør og label ikke kræver ekstra HTML.
Ift. counter-reset, så bliver --animatedNumber animeret som et almindeligt <number> og kan derfor ligge på fx 37.4 eller 82.9 undervejs. Med round() bliver den flydende værdi gjort til et integer, som counteren kan vise.
Det giver et godt lille mønster: lad en typed custom property stå for animationen, brug den som procent i grafik-delen, og konvertér den til et helt tal dér, hvor teksten skal vises.