0

I am trying to achieve the following svg gradient

objective gradient

The closest I can achieve is

enter image description here

I'd be very grateful if someone could help out

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.4/d3.min.js"></script>
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Speed Arc</title>
    <script src="https://d3js.org/d3.v7.min.js"></script>
    <style>
        html,
        body {
            height: 100%;
            margin: 0;
            display: flex;
            justify-content: center;
            align-items: center;
        }

        #speed-arc {
            display: flex;
            justify-content: center;
            align-items: center;
        }

        .background {
            fill: lightgray;
        }

        .path {
            fill: url(#arcBackground);
        }
    </style>
</head>

<body>
    <div id="speed-arc"></div>
    <script>
        // Set the dimensions and margins of the graph
        var width = 500,
            height = 500,
            margin = 0;

        // The radius of the pieplot is half the width or half the height (smallest one). I subtract a bit of margin.
        var radius = Math.min(width, height) / 2 - margin;

        // Append the svg object to the div called 'speed-arc'
        var svg = d3.select("#speed-arc")
            .append("svg")
            .attr("width", width)
            .attr("height", height)
            .append("g")
            .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

        // Create background arc
        var backgroundArc = d3.arc()
            .innerRadius(160)
            .outerRadius(170)
            .startAngle(-0.75 * Math.PI)
            .endAngle(0.75 * Math.PI);

        // Create foreground arc
        var arc = d3.arc()
            .innerRadius(160)
            .outerRadius(170)
            .cornerRadius(5)
            .startAngle(-0.75 * Math.PI);

        // Append background arc
        svg.append("path")
            .attr("class", "background")
            .attr("d", backgroundArc);

        // Append foreground arc
        var path = svg.append("path")
            .datum({
                endAngle: -0.75 * Math.PI
            })
            .attr("class", "path")
            .attr("d", arc)
            .attr("fill", "url(#arcBackground)");

        // Create the gradient for the arc
        var gradient = svg.append("defs")
            .append("linearGradient")
            .attr("id", "arcBackground")
            .attr("x1", "0%")
            .attr("x2", "100%")
            .attr("y1", "0%")
            .attr("y2", "0%");

        gradient.append("stop")
            .attr("offset", "0%")
            .attr("stop-color", "#F95247"); // Red

        gradient.append("stop")
            .attr("offset", "20%")
            .attr("stop-color", "#FD7A4F"); // Orange-red

        gradient.append("stop")
            .attr("offset", "50%")
            .attr("stop-color", "#F8E23D"); // Yellow

        gradient.append("stop")
            .attr("offset", "75%")
            .attr("stop-color", "#B6E359"); // Light green

        gradient.append("stop")
            .attr("offset", "100%")
            .attr("stop-color", "#15DA88"); // Green

        // Create the arc scale
        var arcScale = d3.scalePow()
            .exponent(0.3)
            .domain([0, 15])
            .range([-0.75 * Math.PI, 0.75 * Math.PI])
            .clamp(true);

        // Function to update the arc
        function updateArc(speed) {
            var arcTween = function(transition, speed) {
                transition.attrTween("d", function(d) {
                    var interpolate = d3.interpolate(d.endAngle, arcScale(speed));
                    return function(t) {
                        d.endAngle = interpolate(t);
                        return arc(d);
                    };
                });
            };

            path.transition()
                .duration(750)
                .call(arcTween, speed);
        }

        // Example usage: update the arc with a speed value
        updateArc(15);

        // You can call updateArc function with different values to animate the arc
    </script>
</body>

</html>

4
  • What exactly is the issue? Colors are slightly off? You want a thicker line? Rounded corners?
    – Mark
    Commented Jun 27 at 17:59
  • @Mark The colours are off. The gradients aren't exactly the same
    – Stu
    Commented Jun 27 at 22:47
  • You are essentially facing the old gradient on a path problem. In your code you are applying the gradient from 0% to 100% on the x-axis. This would work great for a horizontal line but for an arc it produces what you are seeing. So, how can you get a smooth gradient on an arc (or really any arbitrary path), that has been discussed a few times on stack overflow.
    – Mark
    Commented Jun 28 at 12:57
  • @Mark Thank you for pointing me in the right direction
    – Stu
    Commented Jun 28 at 16:19

1 Answer 1

1

There is no conic-gradient in SVG, so you need to construct the gradient from more linear gradients. Here, I use stroke-dasharray to create four different circle slices, each with it's own gradient.

img, svg {
  border: thin solid black;
}
<img width="300" src="https://i.sstatic.net/ysi4dE0w.png">

<svg xmlns="http://www.w3.org/2000/svg" width="300" viewBox="0 0 100 63">
  <defs>
    <linearGradient id="lg1" x1="0" y1="1" x1="1" y1="0">
      <stop offset="35%" stop-color="#FD7A4F" /><!--Orange-red-->
      <stop offset="65%" stop-color="#F95247" /><!--red-->
    </linearGradient>
    <linearGradient id="lg2" x1="0" y1="1" x1="1" y1="0">
      <stop offset="35%" stop-color="#F8E23D" /><!--Yellow-->
      <stop offset="65%" stop-color="#FD7A4F" /><!--Orange-red-->
    </linearGradient>
     <linearGradient id="lg3" x1="0" y1="1" x1="1" y1="0">
      <stop offset="35%" stop-color="#B6E359" /><!--Light green-->
      <stop offset="65%" stop-color="#F8E23D" /><!--Yellow-->
    </linearGradient>
    <linearGradient id="lg4" x1="0" y1="1" x1="1" y1="0">
      <stop offset="35%" stop-color="#15DA88" /><!--Green-->
      <stop offset="65%" stop-color="#B6E359" /><!--Light green-->
    </linearGradient>
  </defs>
  <g transform="translate(50 35)" stroke-width="5"
    fill="none" stroke-linecap="round">
    <circle transform="rotate(135)" stroke="url(#lg1)" r="29.5"
      pathLength="360" stroke-dasharray="60 360" stroke-dashoffset="-15" />
    <circle transform="rotate(195)" stroke="url(#lg2)" r="29.5"
      pathLength="360" stroke-dasharray="60 360" stroke-dashoffset="-15" />
    <circle transform="rotate(255)" stroke="url(#lg3)" r="29.5"
      pathLength="360" stroke-dasharray="60 360" stroke-dashoffset="-15" />
    <circle transform="rotate(315)" stroke="url(#lg4)" r="29.5"
      pathLength="360" stroke-dasharray="60 360" stroke-dashoffset="-15" />
  </g>
</svg>

Not the answer you're looking for? Browse other questions tagged or ask your own question.