如何仅使用 stroke-dasharray 而非 stroke-dashoffset 绘制饼图

How can I draw a pie chart only using stroke-dasharray, not stroke-dashoffset

我正在尝试仅使用 stroke-dasharray 和旋转和平移等其他工具绘制饼图,我不允许使用 stroke-dashoffset,因为 wkhtmltopdf 0.12.5 不支持它。我试图做一些类似于下面的代码

<svg height="20%" width="20%" viewBox="0 0 20 20" style="border:1px solid gray; ">
  <circle r="10" cx="10" cy="10" fill="white" />
  <circle r="5" cx="10" cy="10" fill="bisque"
          stroke="tomato"
          stroke-width="10"
          stroke-dasharray="10.99 31.4"
          transform="rotate(-90) translate(-20)"/>
</svg>

其中 31.4 是圆的周长,10.99 是周长的 35%。这是绘制一个代表饼图 35% 的切片。我如何在不使用 stroke-dashoffset 的情况下绘制更多切片(例如,一个代表 40%,另一个代表 13%),我无法弄清楚。非常感谢大家的帮助。

不推荐以这种方式创建饼图。通过“那样”,我指的是在笔画宽度与圆的半径相匹配的地方制作圆。准确的说,笔划宽度是圆半径的两倍。

某些浏览器(或浏览器版本)和渲染库在渲染该形式的圆圈时存在错误。推荐的方法是为每个饼图段创建一个路径。

但是,假设您想继续使用此方法,那么您需要了解以下内容。

  • <circle> 元素上的笔划图案从 3 点开始呈现,并围绕圆圈顺时针进行。

    这就是为什么你在上面的例子中有 rotate(-90)。 -90 度旋转是将圆圈旋转 -90 度,以便笔画从顶部(12 点钟方向)开始。

  • 破折号中的两个数字是<length of dash> <length of gap>。然后重复该模式。

好的。让我们更新您的 SVG 以添加您请求的额外片段。

首先,我建议进行一些更改:

  1. rotate(-90) 移动到父组,这样您在计算新切片的旋转时就不必担心它了。
  2. 如果使用以旋转为中心的 rotate() 版本,您会发现它会容易得多:rotate(angle, centreX, centreY).
  3. 我们需要将填充颜色 (fill="bisque") 添加到一个单独的圆圈中。否则,您添加的每个新段的填充将与之前的段重叠。

所以我们的新起点是:

<svg height="20%" width="20%" viewBox="0 0 20 20" style="border:1px solid gray; ">
  <circle r="5" cx="10" cy="10" fill="bisque" />
  <g transform="rotate(-90, 10,10)" fill="none" stroke-width="10">
    <circle r="5" cx="10" cy="10"
            stroke="tomato"
            stroke-dasharray="10.99 31.4"/>
  </g>
</svg>

添加 40% 的细分

您需要的笔划长度为 40% of 31.4 = 12.56

要旋转它使其从第一段的末尾开始,您需要将它旋转一个等于 (10.99 / 31.4) * 360deg = 126deg 的角度。

<svg height="20%" width="20%" viewBox="0 0 20 20" style="border:1px solid gray; ">
  <circle r="5" cx="10" cy="10" fill="bisque" />
  <g transform="rotate(-90, 10,10)" fill="none" stroke-width="10">
    <circle r="5" cx="10" cy="10"
            stroke="tomato"
            stroke-dasharray="10.99 31.4"/>
    <circle r="5" cx="10" cy="10"
            stroke="goldenrod"
            stroke-dasharray="12.56 31.4"
            transform="rotate(126, 10,10)"/>
  </g>
</svg>

添加 13% 的细分

您需要的笔划长度为 13% of 31.4 = 4.082

要旋转它使其从前一段的末尾开始,您需要将前两段的长度相加,并将其转换为角度。

((10.99 + 12.56) / 31.4) * 360deg = 0.75 * 360 = 270deg`.

<svg height="20%" width="20%" viewBox="0 0 20 20" style="border:1px solid gray; ">
  <circle r="5" cx="10" cy="10" fill="bisque" />
  <g transform="rotate(-90, 10,10)" fill="none" stroke-width="10">
    <circle r="5" cx="10" cy="10"
            stroke="tomato"
            stroke-dasharray="10.99 31.4"/>
    <circle r="5" cx="10" cy="10"
            stroke="goldenrod"
            stroke-dasharray="12.56 31.4"
            transform="rotate(126, 10,10)"/>
    <circle r="5" cx="10" cy="10"
            stroke="cornflowerblue"
            stroke-dasharray="4.082 31.4"
            transform="rotate(270, 10,10)"/>
  </g>
</svg>

我将 Paul 的回答包装在自定义元素中 <svg-pie-chart>(所有现代浏览器都支持)

通过使用 unknown 元素行为,它简化了 HTML 文档中的用法:

<svg-pie-chart>
  <circle r="25%" cx="50%" cy="50%" fill="bisque" />
  <segment percent="35" stroke="tomato" />
  <segment percent="40" stroke="goldenrod" />
  <segment percent="13" stroke="cornflowerblue" />
  <circle r="2" cx="10" cy="10" fill="green" />
</svg-pie-chart>

<style>
  svg-pie-chart svg {
    width: 180px;
    background: grey;
  }
</style>
<script>
  customElements.define("svg-pie-chart", class extends HTMLElement {
    connectedCallback() {
      setTimeout(() => { // wait till all children are (unknown) Elements
        let rotate = 0;
        let svg = [...this.querySelectorAll("*")].map(el => {
          let elsvg = el.outerHTML;
          if (el.nodeName == "SEGMENT") {
            let [percent, stroke, deg = percent.value * .3142] = el.attributes;
            elsvg = `<circle r='5' cx='10' cy='10' stroke='${stroke.value}'` +
              ` stroke-dasharray='${deg} 31.42' transform="rotate(${rotate} 10 10)"/>`;
            rotate += (deg / 31.42) * 360;
          }
          return elsvg;
        });
        this.innerHTML=`<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'>` +
          `<g transform='rotate(-90 10 10)' fill='none' stroke-width='10'></g></svg>`;
        this.querySelector("g").innerHTML = svg.join``;
      })}});
</script>