修复 SVG 圆的线性渐变

Fix linear gradient of SVG circle

我有如下圆形进度条。除了渐变,一切都很好。圆实际上是2个弧。当脚本绘制第一条弧时,渐变为红色->蓝色。所以圆圈的起点是红色的。尾巴是蓝色的。但是当绘制第二个弧时,渐变的开始切换为蓝色,我不知道如何修复它。我不希望它改变颜色。我希望渐变始终相同

function update(percentage) {

  var width = 160,
    height = 160,
    cx = width / 2,
    cy = height / 2,
    start_angle = 0,
    barsize = 10;

  var r = Math.min(cx, cy) - barsize / 2;

  if (percentage === 100) {
    percentage -= 0.0001;
  }

  var end_angle = start_angle + percentage * Math.PI * 2 / 100;

  var x1 = cx + r * Math.sin(start_angle),
    y1 = cy - r * Math.cos(start_angle),
    x2 = cx + r * Math.sin(end_angle),
    y2 = cy - r * Math.cos(end_angle);

  // This is a flag for angles larger than than a half circle
  // It is required by the SVG arc drawing component
  var big = 0;
  if (end_angle - start_angle > Math.PI) big = 1;

  // This string holds the path details
  var d = "M" + x1 + "," + y1 + // Start at (x1,y1)
    " A" + r + "," + r + // Draw an arc of radius r
    " 0 " + big + " 1 " + // Arc details...
    x2 + "," + y2;



  document.getElementById('path').setAttribute('d', d);

}

function animate(start, finish) {
  setTimeout(function() {
    update(start);

    console.log(document.getElementsByClassName('progress__content'))
    let element = document.getElementsByClassName('progress__content')[0];
    element.textContent = start + '%';


    start += 1;
    if (start <= finish) {
      animate(start, finish);
    } else {
      return;
    }
  }, 10);
}


function go() {
  animate(0, 100);
}
.progress {
  position: absolute;
  top: 50%;
  left: 50%;
  margin-top: -100px;
  margin-left: 100px;
  width: 200px;
  height: 200px;
}

.progress__content {
  position: absolute;
  left: 50%;
  top: 50%;
  margin-left: -50px;
  margin-top: -23px;
  font-family: Helvetica;
  font-size: 40px;
  width: 103px;
  height: 47px;
  text-align: center;
}

body {
  background: #f1f1f1;
}
<button onclick="go()">Click me</button>

<div class="progress clip-svg">
  <div class="progress__content">0%</div>
  <svg width="160" height="160">
            <defs>
                <linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="0%">
                    <stop stop-color="#EE3028" offset="0" />
                    <stop stop-color="#067BC2" offset="1" />
                </linearGradient>
            </defs>
            
            <ellipse rx="75" ry="75" cx="80" cy="80" stroke="#f2f2f2" fill="none" stroke-width="10"></ellipse>
            <g>
                <path id="path" stroke-width="10" stroke="url(#gradient)" fill="none" d="">
                </path>
            </g>
        </svg>
</div>

渐变的变化是因为你在整个路径上定义了渐变,随着路径对象的向下和向左生长,渐变的停止位置也不断被重新定义。解决方案是将 gradientUnits 更改为 userSpaceOnUse - 因此渐变是相对于绘图 surface/viewBox 相对于对象定义的。下面可能的实现(我不完全确定你的目标是什么配色方案 - 但调整停止位置和颜色直到你得到你想要的)。

function update(percentage) {

  var width = 160,
    height = 160,
    cx = width / 2,
    cy = height / 2,
    start_angle = 0,
    barsize = 10;

  var r = Math.min(cx, cy) - barsize / 2;

  if (percentage === 100) {
    percentage -= 0.0001;
  }

  var end_angle = start_angle + percentage * Math.PI * 2 / 100;

  var x1 = cx + r * Math.sin(start_angle),
    y1 = cy - r * Math.cos(start_angle),
    x2 = cx + r * Math.sin(end_angle),
    y2 = cy - r * Math.cos(end_angle);

  // This is a flag for angles larger than than a half circle
  // It is required by the SVG arc drawing component
  var big = 0;
  if (end_angle - start_angle > Math.PI) big = 1;

  // This string holds the path details
  var d = "M" + x1 + "," + y1 + // Start at (x1,y1)
    " A" + r + "," + r + // Draw an arc of radius r
    " 0 " + big + " 1 " + // Arc details...
    x2 + "," + y2;



  document.getElementById('path').setAttribute('d', d);

}

function animate(start, finish) {
  setTimeout(function() {
    update(start);

    console.log(document.getElementsByClassName('progress__content'))
    let element = document.getElementsByClassName('progress__content')[0];
    element.textContent = start + '%';


    start += 1;
    if (start <= finish) {
      animate(start, finish);
    } else {
      return;
    }
  }, 10);
}


function go() {
  animate(0, 100);
}
.progress {
  position: absolute;
  top: 50%;
  left: 50%;
  margin-top: -100px;
  margin-left: 100px;
  width: 200px;
  height: 200px;
}

.progress__content {
  position: absolute;
  left: 50%;
  top: 50%;
  margin-left: -50px;
  margin-top: -23px;
  font-family: Helvetica;
  font-size: 40px;
  width: 103px;
  height: 47px;
  text-align: center;
}

body {
  background: #f1f1f1;
}
<button onclick="go()">Click me</button>

<div class="progress clip-svg">
  <div class="progress__content">0%</div>
  <svg width="160" height="160">
            <defs>
                <linearGradient id="gradient" gradientUnits="userSpaceOnUse" x1="0" y1="0" x2="160" y2="0">
                    <stop stop-color="#EE3028" offset="0" />
                    <stop stop-color="#067BC2" offset="160" />
                </linearGradient>
            </defs>
            
            <ellipse rx="75" ry="75" cx="80" cy="80" stroke="#f2f2f2" fill="none" stroke-width="10"></ellipse>
            <g>
                <path id="path" stroke-width="10" stroke="url(#gradient)" fill="none" d="">
                </path>
            </g>
        </svg>
</div>