1

circle我正在尝试使用in 中的元素创建饼图svg。我可以将值填充为 60%、30% 和 10%,但所有圆圈都从同一位置开始。

我怎样才能让下一个圆圈从上一个结束的地方开始?

svg { transform: rotate(-90deg); }

circle {
  stroke-width: 3;
  stroke-opacity: 1;
  fill: none; 
}

circle.stroke-yellow {
  stroke: yellow;
  stroke-dasharray: calc(2*3.14*50*60/100),calc(2*3.14*50);    
}

circle.stroke-red {
  stroke: red;
  stroke-dasharray: calc(2*3.14*50*30/100),calc(2*3.14*50);  
}

circle.stroke-blue {
  stroke: blue;
  stroke-dasharray: calc(2*3.14*50*10/100),calc(2*3.14*50);  
}
<svg xmlns="http://www.w3.org/2000/svg" height="220">
    <circle class="stroke-yellow" cy="110"  cx="110" r="50"></circle>
    <circle class="stroke-red" cy="110" cx="110" r="50"></circle>
    <circle class="stroke-blue" cy="110" cx="110" r="50"></circle>
</svg>

我在 CSS 中提到的也stroke-width不起作用。

4

2 回答 2

4

正如@enxaneta 所提到的:您需要通过更改dash-offset属性为每个饼图段提供一个偏移量。

根据您的代码示例:

svg {
  transform: rotate(-90deg);
}

circle {
  stroke-width: 3;
  stroke-opacity: 1;
  fill: none;
}

.stroke {
  stroke-width: 100;
  --circumference: 314.159
}

circle.stroke-blue {
  stroke: blue;
  stroke-dasharray: calc( var(--circumference) * 10 / 100), var(--circumference);
  stroke-dashoffset: 0;
}

circle.stroke-red {
  stroke: red;
  stroke-dasharray: calc( var(--circumference) * 30 / 100), var(--circumference);
  stroke-dashoffset: calc( 0 - var(--circumference) * 10 / 100);
}

circle.stroke-yellow {
  stroke: yellow;
  stroke-dasharray: calc( var(--circumference) * 60 / 100), var(--circumference);
  stroke-dashoffset: calc( 0 - var(--circumference) * 40 / 100);
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 220 220" height="220">
      <circle class="stroke stroke-blue stroke-10" cy="110" cx="110" r="50" />
    <circle class="stroke stroke-yellow stroke-60" cy="110"  cx="110" r="50" />
    <circle class="stroke stroke-red stroke-30" cy="110" cx="110" r="50" />
</svg>

stroke-width需要为“100”(半径*2);

缺点:

建议:

  • 通过优化 svg 几何图形来简化计算。
  • 通过 HTML/svg 属性(或 css 变量)控制您的值(例如饼图百分比、颜色)

显示 2 个略有不同的 svg 设置的示例:

body{
  font-family: arial;
  font-size:10px;
}
.icon-wrp {
  position: relative;
  display: inline-block;
  width: 200px;
  vertical-align: top;
}

.icon-wrp p{
  font-size:12px;
}
<!--simple pi -->
<div class="icon-wrp">
  <svg class="svgPieAsset" viewBox="0 0 63.6619772368 63.6619772368">
    <symbol id="slice">
      <circle transform="rotate(-90 31.8309886184 31.8309886184)" id="circle" class="percent" cx="50%" cy="50%" r="15.9154943092" fill="none" stroke-width="31.8309886184" />
    </symbol>
    <!--actual pi slices -->
    <use class="segment" href="#slice" stroke="green" stroke-dashoffset="0" stroke-dasharray="30 100" />
    <use class="segment" href="#slice" stroke="orange" stroke-dashoffset="-30" stroke-dasharray="60 100" />
    <use class="segment" href="#slice" stroke="purple" stroke-dashoffset="-90" stroke-dasharray="10 100" />
  </svg>
  <p>1. Precice geometry based on PI. <br>Should be rendered fine on all browsers.</p>
</div>


<div class="icon-wrp">
  <svg class="svgPieAsset" viewBox="0 0 100 100">
    <symbol id="slice2">
      <circle transform="rotate(-90 50 50)" id="circle" class="percent" cx="50%" cy="50%" r="25" fill="none" stroke-width="50%" pathLength="100" />
    </symbol>
    <!--actual pi slices -->
    <use class="segment" href="#slice2" stroke="green" stroke-dashoffset="0" stroke-dasharray="30 100" />
    <use class="segment" href="#slice2" stroke="orange" stroke-dashoffset="-30" stroke-dasharray="60 100" />
    <use class="segment" href="#slice2" stroke="purple" stroke-dashoffset="-90" stroke-dasharray="10 100" />
  </svg>
  <p>2. Using pathLength="100". <br>Might show a tiny gap on chromium based browsers.</p>
</div>

1. 左示例:使用精确(基于 PI)的圆形几何图形
圆形元素的所需周长应为 100 svg 单位。
因此,我们需要像这样设置理想值:

半径:15.91549430919 (100/2π)
行程宽度:31.8309886184 (2 r)
vieBox 宽度/高度:63.6619772368 (4
r)

2.正确的例子:使用pathLength="100"
PathLength允许我们通过将路径的长度计算值设置为“100”来使用任何圆形尺寸。
不幸的是,您可能会在某些浏览器(例如基于铬的浏览器)上遇到渲染不精确的情况,从而导致饼图段之间出现可见的间隙。

渲染不精确

很有可能,这个问题将在铬的未来版本中得到解决。

显示饼段
无论哪种方式,您现在都可以通过设置笔划虚线长度值轻松地显示基于百分比的饼段/切片:

示例 30% 短划线长度;抵消。0(因为它是第一段):

  <circle stroke-dashoffset="0" stroke-dasharray="30 100" cx="50%" cy="50%" r="15.9154943092" fill="none" stroke-width="31.8309886184"  />

添加饼图段:
您需要通过减去先前的虚线长度(百分比)来逐步减少虚线偏移值(因为我们需要负值):
0、-30、-90

示例:60% 短划线长度;抵消。-30

  <circle stroke-dashoffset="-30" stroke-dasharray="60 100" cx="50%" cy="50%" r="15.9154943092" fill="none" stroke-width="31.8309886184"  />

针对可重用性优化的示例(使用 css 变量)

.icon-wrp {
  position: relative;
  display: inline-block;
  width: 200px;
  vertical-align: top;
}

.chart {
  width: 1em;
  height: 1em;
  font-size: var(--chartFontSize);
}

.segment {
  stroke-dasharray: var(--percent) 100;
  stroke-dashoffset: var(--offset);
  stroke: var(--strokeColor);
}

.chartAni .segment {
  animation-name: progress;
  animation-fill-mode: forwards;
  animation-delay: 0.3s;
  animation-duration: 0.5s;
  transition: 0.3s;
  stroke-dasharray: 0 100;
}

@keyframes progress {
  from {
    stroke-dasharray: 0 100;
    stroke-dashoffset: 0;
  }
  to {
    stroke-dasharray: var(--percent) 100;
    stroke-dashoffset: var(--offset);
  }
}
<!-- pie asset – hidden -->
<svg class="svgPieAsset" style="display:none" viewBox="0 0 63.6619772368 63.6619772368">
  <symbol id="slice" viewBox="0 0 63.6619772368 63.6619772368">
    <circle transform="rotate(-90 31.8309886184 31.8309886184)" id="circle" class="percent" cx="31.8309886184" cy="31.8309886184" r="15.9154943092" fill="none" stroke-width="31.8309886184" />
  </symbol>
</svg>

<!-- visible pie chart -->
<div class="icon-wrp">
  <svg id="pieChart01" class="chart chartAni" style="--chartFontSize:20vw">
    <use class="segment" href="#slice" style="--offset:-0; --percent:33.333; --strokeColor:green" />
    <use class="segment" href="#slice" style="--offset:-33.333; --percent:33.333; --strokeColor:purple" />
    <use class="segment" href="#slice" style="--offset:-66.666; --percent:33.333; --strokeColor:gray" />
  </svg>
</div>

编辑:圆环图示例
对于圆环图或圆形仪表 - 只需根据您的需要调整笔画宽度。

.icon-wrp {
  position: relative;
  display: inline-block;
  width: 200px;
  vertical-align: top;
}

.chart {
  width: 1em;
  height: 1em;
  font-size: var(--chartFontSize);
}

.segment {
  stroke-dasharray: var(--percent) 100;
  stroke-dashoffset: var(--offset);
  stroke: var(--strokeColor);
}
.chartAni .segment {
  animation-name: progress;
  animation-fill-mode: forwards;
  animation-delay: 0.3s;
  animation-duration: 0.5s;
  transition: 0.3s;
  stroke-dasharray: 0 100;
}

@keyframes progress {
  from {
    stroke-dasharray: 0 100;
    stroke-dashoffset: 0;
  }
  to {
    stroke-dasharray: var(--percent) 100;
    stroke-dashoffset: var(--offset);
  }
}
<!-- pie asset – hidden -->
<svg class="svgPieAsset" style="display:none;" >
  <symbol id="slice" viewBox="0 0 33 33">
    <circle  id="circle" class="percent" cx="50%" cy="50%" r="15.9154943092" fill="none" stroke-width="0.95"  />
  </symbol>
</svg>

<!-- visible pie chart -->
<div class="icon-wrp">
  <svg id="pieChart01" class="chart chartAni" style="--chartFontSize:20vw;  transform:rotate(-90deg);">
    <use class="segment" href="#slice" style="--offset:0; --percent:10; --strokeColor:blue" />
    <use class="segment" href="#slice" style="--offset:-10; --percent:30; --strokeColor:red" />
    <use class="segment" href="#slice" style="--offset:-40; --percent:60; --strokeColor:yellow" />
  </svg>
</div>

于 2022-01-10T22:19:33.157 回答
3

如果herrstrietzel 的回答由于某种原因不能解决您的问题:

曾几何时,我开始写一篇博文,演示如何在 React 中生成简单的 SVG 圆环/饼图。它不完整,但它包含计算在图表中绘制每个段的路径所需的所有信息。

帖子本身以 React 为中心,但方法论不需要 React。

下面的代码片段是使用该博客文章中的演示生成的。

:root {
  --color1: #6761a8;
  --color2: #009ddc;
  --color3: #f26430;
}

svg {
  max-width: 180px;
}

path:nth-child(3n + 1) {
  fill: var(--color1);
}

path:nth-child(3n + 2) {
  fill: var(--color2);
}

path:nth-child(3n + 3) {
  fill: var(--color3);
}
<svg viewBox="0 0 100 100">
   <path d="M50.99977962889557 22.51817981476399 L50.99993333466665 0.009999666671113516 A50 50 0 1 1 21.909411013411578 91.36325434956197 L34.92449717574351 72.99954813894905 A27.5 27.5 0 1 0 50.99977962889557 22.51817981476399"></path>
   <path d="M33.293128455589205 71.84331575559345 L20.27779148719977 90.20684420744341 A50 50 0 0 1 19.110270928347777 10.683023540969941 L32.65908657059322 28.656553196968876 A27.5 27.5 0 0 0 33.293128455589205 71.84331575559345"></path>
   <path d="M34.25580929035654 27.45292793069627 L20.707239127704607 9.479213229769087 A50 50 0 0 1 49.000066665333264 0.009999666671113516 L49.00022037110441 22.51817981476399 A27.5 27.5 0 0 0 34.25580929035654 27.45292793069627"></path>
</svg>


计算路径

每个<path>代表图表的一个段(切片)。

要绘制线段,您需要计算 4 个角的坐标并将它们与线和弧连接起来。

计算坐标

给定一个角度、一个半径和一个中心点,您可以通过以下公式计算 (x, y) 坐标:

function getCoordinate(angleInDegrees, radius, center = 50) {
  // degrees to radians;
  const radians = angleInDegrees * (Math.PI / 180);

  const x = center - Math.cos(radians) * radius
  const y = center - Math.sin(radians) * radius;

  return [x, y];
}

因此,对于外半径为 50,内半径为 20 的 90° 线段,您可以通过以下方式获得角坐标:

const radiusOuter = 50;
const radiusInner = 20;
const angleStart = 0;
const angleEnd = 90;

const [x1, y1] = getCoordinate(angleStart, radiusInner); // starting angle on inner radius
const [x2, y2] = getCoordinate(angleStart, radiusOuter); // starting angle on outer radius
const [x3, y3] = getCoordinate(angleEnd, radiusOuter); // ending angle on outer radius
const [x4, y4] = getCoordinate(angleEnd, radiusInner); // ending angle on inner radius

甜甜圈段上的坐标位置

使用 SVG 路径命令连接坐标:

可以在 MDN中找到有关下面使用的每个路径命令的详细信息。

const largeArc = 0; // percent > 0.5 ? 1 : 0;
const sweepOuter = 1;
const sweepInner = 0;

const commands = [
  // move to start angle coordinate, inner radius (1)
  `M${x1} ${y1}`,

  // line to start angle coordinate, outer radius (2)
  `L${x2} ${y2}`,

  // arc to end angle coordinate, outer radius (3)
  `A${radiusOuter} ${radiusOuter} 0 ${largeArc} ${sweepOuter} ${x3} ${y3}`,

  // line to end angle coordinate, inner radius (4)
  `L${x4} ${y4}`,

  // arc back to start angle coordinate, inner radius (1)
  `A${radiusInner} ${radiusInner} 0 ${largeArc} ${sweepInner} ${x1} ${y1}`
];

将其放入 SVG 并添加一点 css,你就得到了你的片段:

svg {
  width: 250px;
  border: 1px solid grey;
}

path {
  fill: tomato;
}
<svg viewBox="0 0 100 100">
  <path d="
    M30 50
    L0 50
    A50 50 0 0 1 50 0
    L50 30
    A20 20 0 0 0 30 50
  "/>
</svg>

重复其他部分。

于 2022-01-11T00:42:06.693 回答