Vue + SVGでシンプルなドーナツチャートコンポーネント

シンプルなドーナツチャートが必要になったのでvue+svgで作りました

# 成果物

<p class="codepen" data-height="265" data-theme-id="0" data-default-tab="js,result" data-user="sqrt3" data-slug-hash="vPweYW" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid black; margin: 1em 0; padding: 1em;" data-pen-title="donut-chart vue component">
  <span>See the Pen <a href="https://codepen.io/sqrt3/pen/vPweYW/">
  donut-chart vue component</a> by sqrt3 (<a href="https://codepen.io/sqrt3">@sqrt3</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

# 内容

```slim:template.pug
.donutChart(:style="{ width: `${width}px`, height: `${height}px` }")
    svg.donutChart_circleWrapper(:width="width" :height="height" viewBox="0 0 42 42")
      circle(v-if="border" :cx="cxy" :cy="cxy" :r="radius" fill="transparent" stroke="grey" :stroke-width="borderStrokeWidth")
      circle.donutChart_circle(
        v-for="(e, i) in chartData"
        :cx="cxy"
        :cy="cxy"
        :r="radius"
        fill="transparent"
        :stroke="e.color"
        :stroke-width="strokeWidth"
        :data-fill="e.value")
    svg(:width="width" :height="height" viewBox="0 0 42 42")
      text.donutChart_text(x="50%" y="50%" text-anchor="middle" dominant-baseline="central") {{ text }}
```

```sass:css.styl
.donutChart
  position relative

  &_circleWrapper
    position absolute
    transform-origin center
    transform rotate(-90deg)
    transform-box fill-box

  &_circle
    transform-box fill-box

  &_text
    font-size 0.4em
```

```javascript:script.js
export default DonutChart {
  props: {
    value: {
      type: Array,
      required: true
    },
    width: {
      type: Number,
      default: 100
    },
    height: {
      type: Number,
      default: 100
    },
    strokeWidth: {
      type: Number,
      default: 6
    },
    text: String,
    border: {
      type: Boolean,
      default: false
    }
  },
  data () {
    return {
      radius: 15.91549430918954,
      cxy: 21      
    }
  },
  computed: {
    totalValue () {
      return this.value.map(e => e.value).reduce((prev, e) => prev + e)
    },
    criteriaValue () {
      return 100 / this.totalValue
    },
    chartData () {
      return this.value.filter(e => (e.value > 0)).map(e => {
        return {
          value: e.value * this.criteriaValue,
          color: e.color
        }
      })
    },
    borderStrokeWidth () {
      return this.strokeWidth + 0.5
    }
  },
  mounted () {
    this.draw()
  },
  methods: {
    draw () {
      const elms = this.$el.querySelectorAll(".donutChart_circle")
      let offset = 0
      elms.forEach((e, i) => {
        const val = parseFloat(e.getAttribute('data-fill'))
        const dasharray = [val, 100 - val]
        e.setAttribute("stroke-dasharray", dasharray)
        e.setAttribute("stroke-dashoffset", offset * -1)
        offset = offset + val
      })
    }
  }                 
})
```

stroke-dasharrayで点線の間隔をうまく調整してstroke-dashoffsetでずらして描画していく感じですね
数値を出したいところですが大変そうなのでとりあえずここまで

# 参考

[Scratch-made SVG Donut & Pie Charts in HTML5](https://medium.com/@heyoka/scratch-made-svg-donut-pie-charts-in-html5-2c587e935d72
)