I need to make a circular progress indicator with a color gradient. I also need the \'ends\' of the progress circle to be rounded. This image has everything Im trying to ach
Your original code nearly worked. The problem was that the stroke color of the progress circle was being overridden by the stroke: blue;
in the CSS. Removing this allows the gradient to apply to the circle's stroke, as desired.
var control = document.getElementById('control');
var progressValue = document.querySelector('.progress__value');
var RADIUS = 54;
var CIRCUMFERENCE = 2 * Math.PI * RADIUS;
function progress(value) {
var progress = value / 100;
var dashoffset = CIRCUMFERENCE * (1 - progress);
// console.log('progress:', value + '%', '|', 'offset:', dashoffset)
progressValue.style.strokeDashoffset = dashoffset;
}
control.addEventListener('input', function(event) {
progress(event.target.valueAsNumber);
});
progressValue.style.strokeDasharray = CIRCUMFERENCE;
progress(60);
.demo {
flex-direction: column;
display: flex;
width: 120px;
}
.progress {
transform: rotate(-90deg);
}
.progress__meter,
.progress__value {
fill: none;
}
.progress__meter {
stroke: grey;
}
.progress__value {
/* stroke: blue; */
stroke-linecap: round;
}
<div class="demo">
<svg class="progress" width="120" height="120" viewBox="0 0 120 120">
<defs>
<linearGradient id="linearColors" x1="1" y1="0" x2="0" y2="1">
<stop offset="5%" stop-color="#01E400"></stop>
<stop offset="25%" stop-color="#FEFF01"></stop>
<stop offset="40%" stop-color="#FF7E00"></stop>
<stop offset="60%" stop-color="#FB0300"></stop>
<stop offset="80%" stop-color="#9B004A"></stop>
<stop offset="100%" stop-color="#7D0022"></stop>
</linearGradient>
</defs>
<circle class="progress__meter" cx="60" cy="60" r="54" stroke-width="12" />
<circle class="progress__value" cx="60" cy="60" r="54" stroke-width="12" stroke="url(#linearColors)" />
</svg>
<input id="control" type="range" value="60" />
</div>
Instead of using a gradient you can give the illusion of a gradient by using 100 circles each with a different fill. I'm using the fill-opacity
attribute to set the element either fully opaque or fully transparent.
I hope it helps.
const SVG_NS = 'http://www.w3.org/2000/svg';
const CIRCUMFERENCE = base.getTotalLength()
const UNIT = CIRCUMFERENCE / 100;
let circles=[];//the array of circles
//create 100 circles each with a different fill color to create the illusion of a gradient
for(let i = 0; i<100; i++){
let pos = base.getPointAtLength(i*UNIT);
let o = {cx:pos.x,cy:pos.y,r:5.5,'fill-opacity':0,fill:`hsl(220,100%,${50 + (100-i)/2}%)`}
circles.push(drawCircle(o, progress__value));
}
progress();
control.addEventListener('input', progress);
function progress(){
let val = control.valueAsNumber;
for(let i = 0; i<circles.length; i++){
if(i<=val){
circles[i].setAttributeNS(null,'fill-opacity',1)
}else{
circles[i].setAttributeNS(null,'fill-opacity',0)
}
}
}
// a function to create a circle
function drawCircle(o, parent) {
var circle = document.createElementNS(SVG_NS, 'circle');
for (var name in o) {
if (o.hasOwnProperty(name)) {
circle.setAttributeNS(null, name, o[name]);
}
}
parent.appendChild(circle);
return circle;
}
svg{border:solid}
.demo {
flex-direction: column;
display: flex;
width: 120px;
}
.progress__meter{
fill: none;
}
.progress__meter {
stroke: grey;
}
<div class="demo">
<svg class="progress" viewBox="-2 -2 124 124">
<path class="progress__meter" id="base" d="M60,6A54,54 0 0 1 60,114A54,54 0 0 1 60,6z" stroke-width="12" />
<g id="progress__value"></g>
</svg>
<input id="control" type="range" value="60" />
</div>