Search code examples
svgcolorsgradientcurve

svg multiple color on circle stroke


I want to create a rainbow circle, like the picture below:

enter image description here

How can I draw the curved and multiple color stop gradient?

Here's my current code:

<svg width="500" height="500" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <defs>
    <linearGradient id="test">
    <stop offset="0%" stop-color="#f00"/>
    <stop offset="100%" stop-color="#0ff"/>
    </linearGradient>    
    </defs>    
    <circle cx="50" cy="50" r="40" fill="none" stroke="url(#test)" stroke-width="6"/>    
</svg>


Solution

  • This approach won't work. SVG doesn't have conical gradients. To simulate the effect, you would have to fake it with a large number of small line segments. Or some similar technique.

    Update:

    Here is an example. I approximate the 360deg of hue with six paths. Each path contains an arc which covers 60deg of the circle. I use a linear gradient to interpolate the colour from the start to the end of each path. It's not perfect (you can see some discontinuities where the coloursmeet ) but it would possibly fool most people. You could increase the accuracy by using more than six segments.

        <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="-10 -10 220 220">
          <defs>
            <linearGradient id="redyel" gradientUnits="objectBoundingBox" x1="0" y1="0" x2="1" y2="1">
                <stop offset="0%" stop-color="#ff0000"/>   
                <stop offset="100%" stop-color="#ffff00"/>   
            </linearGradient>
            <linearGradient id="yelgre" gradientUnits="objectBoundingBox" x1="0" y1="0" x2="0" y2="1">
                <stop offset="0%" stop-color="#ffff00"/>   
                <stop offset="100%" stop-color="#00ff00"/>   
            </linearGradient>
            <linearGradient id="grecya" gradientUnits="objectBoundingBox" x1="1" y1="0" x2="0" y2="1">
                <stop offset="0%" stop-color="#00ff00"/>   
                <stop offset="100%" stop-color="#00ffff"/>   
            </linearGradient>
            <linearGradient id="cyablu" gradientUnits="objectBoundingBox" x1="1" y1="1" x2="0" y2="0">
                <stop offset="0%" stop-color="#00ffff"/>   
                <stop offset="100%" stop-color="#0000ff"/>   
            </linearGradient>
            <linearGradient id="blumag" gradientUnits="objectBoundingBox" x1="0" y1="1" x2="0" y2="0">
                <stop offset="0%" stop-color="#0000ff"/>   
                <stop offset="100%" stop-color="#ff00ff"/>   
            </linearGradient>
            <linearGradient id="magred" gradientUnits="objectBoundingBox" x1="0" y1="1" x2="1" y2="0">
                <stop offset="0%" stop-color="#ff00ff"/>   
                <stop offset="100%" stop-color="#ff0000"/>   
            </linearGradient>
          </defs>
        
          <g fill="none" stroke-width="15" transform="translate(100,100)">
            <path d="M 0,-100 A 100,100 0 0,1 86.6,-50" stroke="url(#redyel)"/>
            <path d="M 86.6,-50 A 100,100 0 0,1 86.6,50" stroke="url(#yelgre)"/>
            <path d="M 86.6,50 A 100,100 0 0,1 0,100" stroke="url(#grecya)"/>
            <path d="M 0,100 A 100,100 0 0,1 -86.6,50" stroke="url(#cyablu)"/>
            <path d="M -86.6,50 A 100,100 0 0,1 -86.6,-50" stroke="url(#blumag)"/>
            <path d="M -86.6,-50 A 100,100 0 0,1 0,-100" stroke="url(#magred)"/>
          </g>
        </svg>

    Fiddle here: http://jsfiddle.net/Weytu/

    Update 2:

    For those that want more than six segments, here is some javascript that will produce a wheel with any number of segments that you wish.

    function makeColourWheel(numSegments)
    {
        if (numSegments <= 0)
            numSegments = 6;
        if (numSegments > 360)
            numSegments = 360;
    
        var  svgns = xmlns="http://www.w3.org/2000/svg";
        var  svg = document.getElementById("colourwheel");
        var  defs = svg.getElementById("defs");
        var  paths = svg.getElementById("paths");
    
        var  radius = 100;
        var  stepAngle = 2 * Math.PI / numSegments;
    
        var  lastX = 0;
        var  lastY = -radius;
        var  lastAngle = 0;
        
        for (var i=1; i<=numSegments; i++)
        {
            var  angle = i * stepAngle;
    
            // Calculate this arc end point
            var x = radius * Math.sin(angle);
            var y = -radius * Math.cos(angle);
            // Create a path element
            var arc = document.createElementNS(svgns, "path");
            arc.setAttribute("d", "M " + lastX.toFixed(3) + "," + lastY.toFixed(3)
                                  + " A 100,100 0 0,1 " + x.toFixed(3) + "," + y.toFixed(3));
            arc.setAttribute("stroke", "url(#wheelseg" + i + ")");
            // Append it to our SVG
            paths.appendChild(arc);
            
            // Create a gradient for this segment
            var grad = document.createElementNS(svgns, "linearGradient");
            grad.setAttribute("id", "wheelseg"+i);
            grad.setAttribute("gradientUnits", "userSpaceOnUse");
            grad.setAttribute("x1", lastX.toFixed(3));
            grad.setAttribute("y1", lastY.toFixed(3));
            grad.setAttribute("x2", x.toFixed(3));
            grad.setAttribute("y2", y.toFixed(3));
            // Make the 0% stop for this gradient
            var stop = document.createElementNS(svgns, "stop");
            stop.setAttribute("offset", "0%");
            hue = Math.round(lastAngle * 360 / Math.PI / 2);
            stop.setAttribute("stop-color", "hsl(" + hue + ",100%,50%)");
            grad.appendChild(stop);
            // Make the 100% stop for this gradient
            stop = document.createElementNS(svgns, "stop");
            stop.setAttribute("offset", "100%");
            hue = Math.round(angle * 360 / Math.PI / 2);
            stop.setAttribute("stop-color", "hsl(" + hue + ",100%,50%)");
            grad.appendChild(stop);
            // Add the gradient to the SVG
            defs.appendChild(grad);
    
            // Update lastx/y
            lastX = x;
            lastY = y;
            lastAngle = angle;
        }
    }
    
    
    makeColourWheel(60);
    <svg id="colourwheel" xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="-10 -10 220 220">
      <defs id="defs">
      </defs>
    
      <g id="paths" fill="none" stroke-width="15" transform="translate(100,100)">
      </g>
    </svg>