Home > Back-end >  Filling Non-linear Svg Path Stroke
Filling Non-linear Svg Path Stroke

Time:02-16

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>

Preview

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.

  • Related