effect
fig. conventional way of thinking
Use svg to draw a track
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="none" stroke-width="10" stroke="#333"></circle>
</svg>
Simply put, it draws a circle using circle. Note that the track is actually the stroke of the circle, so if the target svg size is 100, the radius of the circle is 40 and the stroke is 10.
Then, by design, the track only needs to be 3/4 of a circle:
<!-- 3/4 track before rotate -->
<!-- circumference = radius * 2 * PI = 40 * 2 * = 251.3274 -->
<!-- stroke-dasharray left = circumference * percent = 251.3274 * 0.75 = 188.4955 -->
<!-- stroke-dasharray right = circumference * (1 - percent) = 251.3274 * (1 - 0.75) = 62.8318 -->
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="none" stroke-width="10" stroke-dasharray="188.4955,62.8318" stroke="#333"></circle>
</svg>
In order to realize this track, stroke-dasharray is needed at this time.
To better understand what stroke-dasharray is doing here, draw a line first:
<svg viewBox="0 0 300 10" style="display: block;">
<line x1="0" y1="5" x2="300" y2="5" stroke-width="10" stroke="#333" stroke-dasharray="75,25"></line>
</svg>
Simply put, the above line length of 300, each draw a 75 stroke, then leave a section of 25, and so on, repeat exactly 3 times, just spread the length of 300.
The same applies to circle, except that it draws stroke around the circle, counterclockwise, as an analogous example:
The stroke-dasharray is the length, so you need to calculate the perimeter to find out how long A and E are:
Perimeter = Radius * 2 * PI = 40 * 2 * = 251.3274
A = Perimeter * 3/4 = 251.3274 * 0.75 = 188.4955
E = Perimeter * 1/4 = 251.3274 * 0.25 = 62.8318
Now you also need to use transform to rotate it 135 degrees to meet your needs:
<!-- 3/4 track after rotate 135deg -->
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="none" stroke-width="10" stroke-dasharray="188.4955,62.8318" transform="rotate(135, 50, 50)" stroke="#333"></circle>
</svg>
progress bar
Start by drawing a solid color progress bar:
body {
background: black;
}
.gauge {
position: relative;
display: inline-block;
}
.gauge > svg {
width: 200px;
height: 200px;
}
.gauge > span {
color: #fff;
position: absolute;
top: 50%;
left: 0;
width: 100%;
text-align: center;
transform: translate(0, -50%);
font-size: 2em;
}
<!-- stroke-dasharray left = circumference * 0.75 * percent = 188.4955 * 0.10 = 18.8495 -->
<!-- stroke-dasharray right = circumference * 0.75 * (1 - percent) + circumference * (1 - 0.75) = 188.4955 * (1 - 0.10) + 62.8318 = 232.4778 -->
<div class="gauge">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="none" stroke-width="10" stroke-dasharray="188.4955,62.8318" transform="rotate(135, 50, 50)" stroke="#333"></circle>
<circle cx="50" cy="50" r="40" fill="none" stroke-width="10" stroke-dasharray="18.8495,232.4778" transform="rotate(135, 50, 50)" stroke="#ffff00"></circle>
</svg>
<span>10%</span>
</div>
<!-- stroke-dasharray left = circumference * 0.75 * percent = 188.4955 * 0.50 = 94.2477 -->
<!-- stroke-dasharray right = circumference * 0.75 * (1 - percent) + circumference * (1 - 0.75) = 188.4955 * (1 - 0.50) + 62.8318 = 157.0795 -->
<div class="gauge">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="none" stroke-width="10" stroke-dasharray="188.4955,62.8318" transform="rotate(135, 50, 50)" stroke="#333"></circle>
<circle cx="50" cy="50" r="40" fill="none" stroke-width="10" stroke-dasharray="94.2477,157.0795" transform="rotate(135, 50, 50)" stroke="#ffff00"></circle>
</svg>
<span>50%</span>
</div>
<!-- stroke-dasharray left = circumference * 0.75 * percent = 188.4955 * 1.00 = 94.2477 -->
<!-- stroke-dasharray right = circumference * 0.75 * (1 - percent) + circumference * (1 - 0.75) = 188.4955 * (1 - 1.00) + 62.8318 = 157.0795 -->
<div class="gauge">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="none" stroke-width="10" stroke-dasharray="188.4955,62.8318" transform="rotate(135, 50, 50)" stroke="#333"></circle>
<circle cx="50" cy="50" r="40" fill="none" stroke-width="10" stroke-dasharray="188.4955,62.8318" transform="rotate(135, 50, 50)" stroke="#ffff00"></circle>
</svg>
<span>100%</span>
</div>
There is an important caveat, for example, the 10%, 50%, and 100% percentages in the graph are based on those 3/4 orbits, not the whole circle, so the stroke-dasharray calculation actually takes into account 3 parts:
10%
A = s1 = perimeter * 3/4 * progress = 251.3274 * 0.75 * 0.10 = 18.8495
E = s2 + s3 = round * 3/4 * (1 - progress) + round * 1/4 = 251.3274 * 0.75 * (1 - 0.10) + 251.3274 * 0.25 = 232.4778
50%
A = s1 = perimeter * 3/4 * progress = 251.3274 * 0.75 * 0.50 = 94.2477
E = s2 + s3 = round * 3/4 * (1 - progress) + round * 1/4 = 251.3274 * 0.75 * (1 - 0.50) + 251.3274 * 0.25 = 157.0796
100%
A = s1 = round * 3/4 * progress = 251.3274 * 0.75 * 1.00 = 188.4955
E = s2 + s3 = round * 3/4 * (1 - progress) + round * 1/4 = 251.3274 * 0.75 * (1 - 1.00) + 251.3274 * 0.25 = 62.8318
gradual change
The gradient goes from left to right, following the rotate of the track, and finally from top right to bottom left, which means that here the gradient does not follow the track from 0 to 100%, but only simulates a similar feeling.
<!-- progress bar with gradient -->
<!-- stroke-dasharray left = circumference * 0.75 * percent = 188.4955 * 0.30 = 94.2477 -->
<!-- stroke-dasharray right = circumference * 0.75 * (1 - percent) + circumference * (1 - 0.75) = 188.4955 * (1 - 0.30) + 62.8318 = 157.0795 -->
<div class="gauge">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="none" stroke-width="10" stroke-dasharray="188.4955,62.8318" transform="rotate(135, 50, 50)" stroke="#333"></circle>
<circle cx="50" cy="50" r="40" fill="none" stroke-width="10" stroke-dasharray="56.5486,194.7786" transform="rotate(135, 50, 50)" stroke="url(#gauge-gradient)"></circle>
</svg>
<span>30%</span>
</div>
<!-- stroke-dasharray left = circumference * 0.75 * percent = 188.4955 * 0.80 = 94.2477 -->
<!-- stroke-dasharray right = circumference * 0.75 * (1 - percent) + circumference * (1 - 0.75) = 188.4955 * (1 - 0.80) + 62.8318 = 157.0795 -->
<div class="gauge">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="none" stroke-width="10" stroke-dasharray="188.4955,62.8318" transform="rotate(135, 50, 50)" stroke="#333"></circle>
<circle cx="50" cy="50" r="40" fill="none" stroke-width="10" stroke-dasharray="150.7964,100.5308" transform="rotate(135, 50, 50)" stroke="url(#gauge-gradient)"></circle>
</svg>
<span>80%</span>
</div>
anime
Finally, in order to realize the animation in the "effect", you need to use CSS together with JS to realize it:
<!-- with animation -->
<div class="gauge">
<svg viewBox="0 0 100 100">
<circle cx="50" cy="50" r="40" fill="none" stroke-width="10" stroke-dasharray="188.4955,62.8318" transform="rotate(135, 50, 50)" stroke="#333"></circle>
<circle cx="50" cy="50" r="40" fill="none" stroke-width="10" stroke-dasharray="0,251.3274" transform="rotate(135, 50, 50)" stroke="url(#gauge-gradient3)"></circle>
</svg>
<span>100%</span>
</div>
#circle {
transition: all 1s linear;
}
(function() {
const radius = 40;
const trackPercent = 0.75
const circumference = 40 * 2 * ;
const percent = 1.00;
const strokeDasharrayLeft = circumference * trackPercent * percent
const strokeDasharrayRight = circumference * trackPercent * (1 - percent) + circumference * (1 - trackPercent)
const circle = ('#circle');
function change() {
const strokeDasharray = ('stroke-dasharray').split(',')
const left = parseFloat(strokeDasharray[0])
const right = parseFloat(strokeDasharray[1])
if (left === 0) {
('stroke-dasharray', `${strokeDasharrayLeft},${strokeDasharrayRight}`)
} else {
('stroke-dasharray', `0,251.3274`)
}
}
setTimeout(function() {
setInterval(function() {
change()
}, 1000)
change()
}, 0)
})();
The main role of JS is to dynamically calculate the stroke-dasharray, and with the CSS transition all can be realized.
Done!