LoginSignup
6

More than 5 years have passed since last update.

Vue.js + SVGで折れ線グラフを描く

Last updated at Posted at 2017-12-12

自分が開発しているサービス(Vueを主に使用)で分析系の機能をつくるにあたり
「やっぱりこれ、縦の折れ線グラフが一番わかりやすい」
となり、ググる...
しかし、そのまま使えそうなパッケージは見つからず、自前でつくることを決意。

Vueの公式ページにSVGで描かれたグラフがあったのでやってみました。

サンプル

vue-line-graph.png
https://jsfiddle.net/Wave7KN/5th9k53n/

SVGとは

SVGは画像フォーマットの1つでもあり、XMLを使用したマークアップ言語でもあり、なかにはHTMLやCSSと同じような感覚で書けるものもあります。例えば、このように:hoverも使えたりします。※サンプルの点の部分にマウスを乗せてみてください。
vue-line-graph-hover.png
また、HTML同様にJavaScriptで制御できるため、今回はVue.jsを使用して折れ線グラフを描いていきます。

サンプルデータを用意する

0~100の数値を5つずつ適当に用意しました。(ぜひランダム関数使ってください。)

JavaScript|3行目~
data: {
  all_data: [
    {
      type: 'a',
      scores: [34, 46, 28, 39, 60]    
    },
    {
      type: 'b',
      scores: [47, 32, 52, 33, 52]
    },
    {
      type: 'c',
      scores: [38, 29, 42, 53, 41]
    },
    {
      type: 'd',
      scores: [52, 57, 69, 48, 46]
    }
  ],
  ratio: 3, //横幅を3倍に拡大するために使う
  row_height: 30,
},

SVGの土台を用意する

HTML|2行目~
<svg :width="100 * ratio"
  :height="svgHeight"
  :viewBox="'0 0 ' + 100 * ratio + ' ' + svgHeight"
  class="line-graph">
  <!-- ... -->
</svg>

widthheightは、画面上に表示されるサイズを表します。

viewBoxはどこからどこまでを切り取って詰め込むかを表し、viewBox="x y width height"のように4つの値を指定します。
x: viewBox左上のx座標
y: viewBox左上のy座標
width: viewBoxの幅
height: viewBoxの高さ

今回は0~100のデータを扱っていますが、そのままでは小さすぎるので3倍(ratio: 3)にして表示させます。高さは、グラフの点の数に合わせて計算して出したいと思います。

JavaScript|25行目~
svgHeight(){
  return (this.all_data[0].scores.length - 1) * this.row_height
}

all_data1番目のscoresのデータの数を参照して計算し、この :height="svgHeight" :viewBox="'0 0 ' + 100 * ratio + ' ' + svgHeight" 2つの値を入れました。

1本の折れ線グラフをグループ化し、繰り返す

vue-line-graph-group1.png

HTML|6行目~
<g v-for="data in all_data" :class="'type-' + data.type">
  <!-- ... -->
</g>

SVGのg要素は子要素を束ねるもので、いわゆる「グループ化」です。画像のような1本の折れ線グラフを1つのグループとして、v-forを活用し、4本分繰り返してもらいます。
:class="'type-' + data.type"とすることで、1本ずつに.type-a.type-bといったそれぞれ異なるclassを付与し、色を変えています。

間の線と点をグループ化し、繰り返す

vue-line-graph-group2.png

HTML|7行目~
<g v-for="(score, index) in data.scores" :key="index">
  <line v-if="index != 0"
    :x1="data.scores[index - 1] * ratio"
    :y1="(index - 1) * row_height"
    :x2="score * ratio"
    :y2="index * row_height"/>
  <circle
    :cx="score * ratio"
    :cy="index * row_height"/>
</g>

1本のグラフの中で間を結ぶ線(line)と点(circle)をグループ化し、データの数だけ繰り返します。

line要素は、始点と終点の座標を指定することで描画できます。この場合、始点は1つ前の点の位置なので、index - 1でx座標・y座標それぞれの値を求めています。v-forindexを渡すことで、自身が何番目のデータかを知ることができます。
また、1つ目のlineは不要なのでv-if="index != 0"で描画しないようにします。

circle要素は、円の中心の座標と半径を指定することで描画できます。半径rはCSSの方で指定しました。

この場合、「x2=cx」「y2=cy」となります。

まとめ

「各属性に適切な座標を入れれば描画できる」という当たり前のことに気がつけたので、大きな収穫です。それほど時間をかけずにグラフを作成することができました。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6