如何为简单的甜甜圈SVG圆形添加边框间距

3

我正在尝试创建一个非常简单的甜甜圈图表。

以下是工作代码:

const countries = [
{'percent': 2,colour: 'red'},
{'percent': 28,colour: 'blue'},
{'percent': 36,colour: 'yellow'},
{'percent': 34,colour: 'orange'}
];


const donutData = countries.map((country, index) => {
  return {
    stroke: country.colour,
    dashoffset: 25 - countries.slice(0, index).reduce((a, b) => a + b.percent, 0),
    dashArray: [country.percent, 100 - country.percent]
  }
});
const div = document.createElement('div');
div.innerHTML = '<svg id="donut" width="100%" height="100%" viewBox="3 3 36 36"></svg>';
document.body.appendChild(div);
document.querySelector('#donut').innerHTML= donutData.reduce((a, item) => {
  return a +
    `<circle 
   cx="21"
    cy="21"
    fill="transparent"
    r="15.91549430918954"
    stroke-width="2.3"
    stroke="${item.stroke}"
    stroke-dasharray="${item.dashArray[0]} ${item.dashArray[1]}"
    stroke-dashoffset="${item.dashoffset}"></circle>
`;
}, '')

这里的代码是正常运行的,但如果你尝试将stroke-linecap="round"添加到<circle>中,它就会变得很乱,描边会叠在一起。

你可以在这里看到。

我能理解这个问题,但不知道如何在描边之间添加一点间距以避免丑陋的叠放。

欢迎任何建议。


1
我不确定你是否在寻找这个,但也许你可以尝试从每个笔画(在dashArray中)中减去一小部分百分比:https://jsfiddle.net/3L728bmq/ - Shidersz
1
一个解决方案是在 countries 数组的末尾添加 {'percent': 0,colour: 'red'}。虽然结果可能更美观,但我不会这样做。对于图表,我会保留 stroke-linecap="butt"。图表是视觉工具,使用 stroke-linecap="round" 可能会改变数据的感知。 - enxaneta
@Shidersz,当百分比低于2.5时,那样做是行不通的。 - Milad
1
如果其中一个或多个金额小于舍入半径,应该发生什么?你想要发生什么? - Paul LeBeau
@MichaelMullany,你能给我展示一个例子吗? - Milad
显示剩余3条评论
2个回答

1

这是您想要的吗?

只需从破折号长度中减去圆形端盖的半径(每个端点一个)。

只要圆的半径不是非常小,圆形端盖应该可以整齐地相互接触。

const countries = [
{'percent': 10,colour: 'red'},
{'percent': 20,colour: 'blue'},
{'percent': 36,colour: 'yellow'},
{'percent': 34,colour: 'orange'}
];

const STROKE_WIDTH = 2.3;

const donutData = countries.map((country, index) => {
  // Subtract the radius of the round cap, twice.
  const dashLength = country.percent - STROKE_WIDTH;
  return {
    stroke: country.colour,
    dashoffset: 25 - countries.slice(0, index).reduce((a, b) => a + b.percent, 0),
    dashArray: [dashLength, 100 - dashLength]
  }
});
const div = document.createElement('div');
div.innerHTML = '<svg id="donut" width="100%" height="100%" viewBox="3 3 36 36"></svg>';
document.body.appendChild(div);
document.querySelector('#donut').innerHTML= donutData.reduce((a, item) => {
  return a +
    `<circle 
   cx="21"
    cy="21"
    fill="transparent"
    r="15.91549430918954"
    stroke-width="${STROKE_WIDTH}"
    stroke-linecap="round"
    stroke="${item.stroke}"
    stroke-dasharray="${item.dashArray[0]} ${item.dashArray[1]}"
    stroke-dashoffset="${item.dashoffset}"></circle>
`;
}, '')

更新

该版本可以相对优雅地处理短行长度。

const countries = [
{'percent': 10, colour: 'red'},
{'percent': 20, colour: 'blue'},
{'percent': 36, colour: 'yellow'},
{'percent': 33, colour: 'orange'},
{'percent': 1,  colour: 'green'},
];

const STROKE_WIDTH = 2.3;

const donutData = countries.map((country, index) => {
  let dashLength, offsetAdjust, caps;
  if (country.percent >= STROKE_WIDTH) {
    // Subtract the radius of the round cap, twice.
    dashLength = country.percent - STROKE_WIDTH;
    offsetAdjust = STROKE_WIDTH / 2;
    caps = "round";
  } else {
    dashLength = country.percent;
    offsetAdjust = 0;
    caps = "butt";
  }
  return {
    stroke: country.colour,
    dashoffset: 25 - countries.slice(0, index).reduce((a, b) => a + b.percent, 0) - offsetAdjust,
    dashArray: [dashLength, 100 - dashLength],
    caps: caps
  }
});
const div = document.createElement('div');
div.innerHTML = '<svg id="donut" width="100%" height="100%" viewBox="3 3 36 36"></svg>';
document.body.appendChild(div);
document.querySelector('#donut').innerHTML= donutData.reduce((a, item) => {
  return a +
    `<circle 
   cx="21"
    cy="21"
    fill="transparent"
    r="15.91549430918954"
    stroke-width="${STROKE_WIDTH}"
    stroke-linecap="${item.caps}"
    stroke="${item.stroke}"
    stroke-dasharray="${item.dashArray[0]} ${item.dashArray[1]}"
    stroke-dashoffset="${item.dashoffset}"></circle>
`;
}, '')


LeBaue,谢谢你,但不用了。如果你读了我问题下面的评论,你会发现这个答案非常有限,只要我改变百分比,对于任何小于2.3的百分比,它都会出错。 - Milad
你从未回答过关于如何处理非常小的百分比的问题。一个解决方案就是不对小值进行圆角处理。任何在破折号末尾添加帽子的解决方案都会改变破折号的有效长度并扭曲相对百分比。 - Paul LeBeau

0
这是如何使用路径和标记的方法。关键是要使用一个标记开始/标记结束组合。前一行看似重叠的部分实际上是作为一个标记开始粘在当前行上的。

<svg width="600px" height="400px">
  <defs>
    <marker id="round-cap-blue" viewBox="0 0 1 1" 
        markerWidth="1" markerHeight="1"
        orient="auto" refX="0.5" refY="0.5">
      <circle cx="0.5" cy="0.5" r="0.5" fill="blue"/>
    </marker>
    
        <marker id="round-cap-red" viewBox="0 0 1 1" 
        markerWidth="1" markerHeight="1"
        orient="auto" refX="0.5" refY="0.5">
      <circle cx="0.5" cy="0.5" r="0.5" fill="red"/>
    </marker>
    
            <marker id="round-cap-green" viewBox="0 0 1 1" 
        markerWidth="1" markerHeight="1"
        orient="auto" refX="0.5" refY="0.5">
      <circle cx="0.5" cy="0.5" r="0.5" fill="green"/>
    </marker>
    
    
    </defs>
  
  <g transform="translate(100,0)">
    
  <path fill="none" stroke="blue" stroke-width="30" d="M 150 150
           A 100 100 0 0 0 50 50" marker-end="url(#round-cap-blue)"/>
  
   <path fill="none" stroke="red" stroke-width="30" d="M 50 250
           A 100 100 0 0 0 150 150" marker-end="url(#round-cap-red)"/>
    
   <path fill="none" stroke="green" stroke-width="30" d="M 50 50
           A 100 100 0 0 0 50 250" marker-start="url(#round-cap-blue)" marker-end="url(#round-cap-green)"/>
    
    
  </g>
  
</svg>


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