I trying to do speedometer by filling stroke with linear gradient. When stop offset is 0%, white line appears at the beginning of the path. How can i get rid of it? In additional, i'm not sure that filling stroke with linear gradient is the best option to fill svg path. Do you have better ideas?
<svg width="196" height="65" viewBox="0 0 196 65" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="fillSpeedometer" x1="0" y1="0.5" x2="1" y2="0.5">
<stop offset="0%" stop-opacity="1" stop-color="white" />
<stop offset="0%" stop-opacity="1" stop-color="white" />
<stop offset="0%" stop-opacity="1" stop-color="grey" />
<stop offset="100%" stop-opacity="1" stop-color="grey" />
</linearGradient>
</defs>
<path d="M2 63C3.65579 42.6667 19.0187 2 67.2238 2C115.429 2 171.827 2 194 2" stroke="url(#fillSpeedometer)" stroke-width="3" stroke-linecap="round" />
</svg>
CodePudding user response:
A common approach would be to render your current value/speed by setting/changing the stroke-dasharray
attribute.
Let's say your "speed limit" is 300 km/h.
Apply the pathLength
attribute to your path.
This way you could easily display a current speed e.g 50 km/h like so:
stroke-dasharray="150 300"
The second dash-array value would be the total speed (or max range value).
This will ensure the dashed stroke to have a large enough gap (so you wont't see repeating dash patterns).
Example: using stroke-daharray
let svgGauges = document.querySelectorAll(".gaugeProgress");
let progress = document.querySelector("#progress");
progress.addEventListener("change", function(e) {
let percent = e.target.value;
if (svgGauges.length) {
svgGauges.forEach(function(gauge, i) {
let currentGauge = svgGauges[i];
let dashGap = gauge.getAttribute("stroke-dasharray").split(" ")[1];
gauge.setAttribute("stroke-dasharray", percent " " dashGap);
});
}
});
#progress,
.speedometer {
display: inline-block;
width: 50%
}
.gaugeProgress {
transition: 0.3s stroke-dasharray;
}
<h3>Speed (km/h)</h3>
<p><input id="progress" type="range" min="0" max="300" step="10" value="150" />
</p>
<svg viewBox="0 0 196 65" fill="none" xmlns="http://www.w3.org/2000/svg">
<!-- remove stroke-linecap if stroke dash is 0 -->
<style>
[stroke-dasharray^="0 "] {
stroke-linecap: unset;
}
</style>
<defs>
<linearGradient id="fillSpeedometer">
<stop offset="0%" stop-opacity="1" stop-color="orange" />
<stop offset="100%" stop-opacity="1" stop-color="red" />
</linearGradient>
</defs>
<path id="gauge" pathLength="300" d="M2 63C3.65579 42.6667 19.0187 2 67.2238 2C115.429 2 171.827 2 194 2" stroke-width="3" />
<use href="#gauge" stroke="#ccc" stroke-linecap="round" />
<use href="#gauge" stroke="url(#fillSpeedometer)" stroke-dasharray="50 300" stroke-linecap="round" />
</svg>
If you're using rounded line caps you will need an additional css rule to prevent a visible stroke at value=0. Since stroke-linecap="round" (or rect) will extend your stroke dash, you need to disable this stroke-linecap value like this:
[stroke-dasharray^="0 "] {
stroke-linecap: unset;
}
This concept is basically the same as used for many gauge/piechart/donut chart designs ... as well as svg line animations.
See also gauge related questions on SO.