SVG圆形描边上的多种颜色

29
我想创建一个彩虹圆圈,就像下面的图片一样: enter image description here 如何绘制弧形和多色停渐变?
这是我的当前代码:

<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>

3个回答

62

这种方法行不通。SVG没有圆锥渐变。为了模拟该效果,您需要使用大量的小线段进行伪造,或者使用类似的技术。

更新:

这里有一个示例。我用六个路径来近似360度的色调。每个路径包含一个弧,覆盖圆的60度。我使用线性渐变来插值从每个路径的起点到终点的颜色。它并不完美(你可以看到颜色交汇处有一些不连续),但可能会欺骗大多数人。您可以通过使用更多的线段来增加精度。

    <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>

这里可以试玩: http://jsfiddle.net/Weytu/

更新2:

对于那些想要超过六个部分的人,这里是一些JavaScript代码,可以生成您希望的任何数量的部分的轮子。

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>


有任何示例吗?我只想像图片中显示的那样做,大小无所谓。 - user2678106
已更新,附有示例。 - Paul LeBeau
你如何实现一个新的段落? - Jesper
您需要根据额外弧线的起点和终点计算并添加额外的弧路径。我在这里使用六边形形状的优点是,它使得计算这些坐标和梯度相当容易。 - Paul LeBeau
2
我已经更新了答案,加入了一些JS来生成轮子。 - Paul LeBeau

17
你可以使用conic-gradient来解决它:

.color-wheel {
  display: inline-block;
  padding: 25px;
  border-radius: 100%;
  background: conic-gradient(red, yellow, lime, aqua, blue, magenta, red);
  background-repeat: no-repeat;
  background-size: cover;
  background-position: center center;
  background-size: auto;
}

.color-wheel::after {
  content: '';
  display: block;
  padding: 75px;
  border-radius: 100%;
  background: #ffffff;
}
<div class="color-wheel"></div>

但目前仅在Chrome中支持。点击此处获取更多信息:https://caniuse.com/#feat=css-conic-gradients

我还构建了JavaScript/SVG解决方案,可以轻松解决该问题:

const resolution = 1;
const outerRadius = 100;
const innerRadius = 75;

function polarToCartesian(centerX, centerY, radius, angleInDegrees) {
    const angleInRadians = (angleInDegrees - 90) * Math.PI / 180.0;

    return {
        x: centerX + radius * Math.cos(angleInRadians),
        y: centerY + radius * Math.sin(angleInRadians)
    };
}

function describeArc(x, y, radius, startAngle, endAngle) {
    const start = polarToCartesian(x, y, radius, endAngle);
    const end = polarToCartesian(x, y, radius, startAngle);

    const arcSweep = endAngle - startAngle <= 180 ? '0' : '1';

    const d = [
        'M', start.x, start.y,
        'A', radius, radius, 0, arcSweep, 0, end.x, end.y,
        'L', x, y,
        'L', start.x, start.y
    ].join(' ');

    return d;
}

function generateConicGradiant(radius, resolution, target) {
    for (var i = 0; i < 360 * resolution; i++) {
        const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');

        path.setAttribute(
            "d",
            describeArc(
                radius,
                radius,
                radius,
                i / resolution,
                (i + 2) / resolution
            )
        );
        path.setAttribute('fill', 'hsl(' + (i / resolution) + ', 100%, 50%)');

        target.appendChild(path);
    } 
}

function generateOverlay(outerRadius, innerRadius, target) {
    const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');

    circle.setAttribute('cx', outerRadius);
    circle.setAttribute('cy', outerRadius);
    circle.setAttribute('r', innerRadius);
    circle.setAttribute('fill', 'white');

    target.appendChild(circle);
}

var root = document.getElementById('color-wheel');

generateConicGradiant(outerRadius, resolution, root);
generateOverlay(outerRadius, innerRadius, root);
#color-wheel {
  width: 200px;
  height: 200px;
}
<svg viewBox="0 0 200 200" version="1.1" id="color-wheel"></svg>


1
现在在Firefox 83+上得到支持了! - user5507535

1
为了改进oVi的答案:在这里,您可以将直径笔画宽度设置为确切的像素大小。

.color-wheel {
  --diameter: 160px;
  --stroke-width: 20px;
  position: relative;
  width: var(--diameter); height: var(--diameter);
}

.color-wheel > .color-circle {
  position: absolute;
  left: 0; top: 0;
  border-radius: 50%;
  width: 100%; height: 100%;
  background: conic-gradient(red, orange, yellow, lime, green, turquoise, blue, purple, red); /* four color system */
}

.color-wheel > .inner-circle {
  --inner-diameter: calc(var(--diameter) - 2 * var(--stroke-width));
  --margin: calc(-0.5 * var(--inner-diameter));
  position: absolute;
  left: 50%; top: 50%;
  width: var(--inner-diameter); height: var(--inner-diameter);
  margin-left: var(--margin); margin-top: var(--margin);
  border-radius: 50%;
  background: white;
}

.color-wheel > .overlay {
  position: absolute;
  left: 0; top: 0;
  width: 100%; height: 100%;
}
<div class="color-wheel">
  <div class="color-circle"></div>
  <div class="inner-circle"></div>
  <div class="overlay">
    hello world.
    this is a pure CSS color ring : )
  </div>
</div>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接