「Vue.jsを使うとデータに連動して表示が更新されて便利!」とか
「d3.js使うとデータをビジュアル化出来て楽しい!」とか
「どっちもできたらもっと楽しい!」
とか思っていた自分でしたが、ここにきて根本的な疑念が湧いてきます。
この二つの相性悪くない?というかd3.js必要か?
コード全文 その1 改(d3.jsを使用しないver)
<head>
<script src="https://unpkg.com/vue"></script>
</head>
<body>
<div id="vue">
<svg :width="width" :height="height">
<circle :cx="width/2"
:cy="height/2"
:r="r"
:fill="fill">
</circle>
</svg>
<div>r: {{ r }}</div>
</div>
<script>
new Vue({
el: '#vue',
data: {
width: 500,
height: 500,
r: 75,
fill:"rgba(59, 0, 255, 0.8)"
}
})
</script>
</body>
円が表示されます。
解説 その1
①ライブラリの読み込み
<head>
<script src="https://unpkg.com/vue"></script>
</head>
d3.jsのライブラリは読み込んでいません。
②svgエリアを作成
<div id="vue">
<svg :width="width" :height="height">
<circle :cx="width/2"
:cy="height/2"
:r="r"
:fill="fill">
</circle>
</svg>
<div>r: {{ r }}</div>
</div>
svgで円を表示したいので、<svg>
の中に<circle>
タグを追加します。
(参考→ https://developer.mozilla.org/ja/docs/Web/SVG/Element/circle )
d3を使った例ではd3.appendでcircleを追加しましたが、
別にsvgはd3から生成しなければならない決まりはありません。
<circle>
で指定しないといけない必須属性は「cx」「cy」「r」なので、
それら属性それぞれを指定します。
属性はこの後作るvueインスタンスのdataと紐付けています。
※cxとcy(中心の座標)はsvgエリアのwidthとheightから計算しています。
また円の色は「fill」で指定することができますので、これも後のdataと紐付けましょう。
③vueインスタンスを作成
<script>
new Vue({
el: '#vue',
data: {
width: 500,
height: 500,
r: 75,
fill:"rgba(59, 0, 255, 0.8)"
}
})
</script>
今、svgエリアと円を表示するために必要な情報は
- widthとheight
- 半径r
- circleのfill
だけですので、それらをdataの中で記述しています。
d3.jsを使った時との違い
その1の時のコードと比べるとscript部分がすっきりしました。
それもそのはずです。わざわざd3.jsから直接に<svg>
の中身を書き換えにいったりしなくて良くなったのです。
「mounted」の中に書き慣れたd3.jsのコードを書きましょう。
などと私はのたまっていましたが、初めからd3のコードを書く必要などなかったのです。
コード全文 その2 改(d3.jsを使用しないver)
<head>
<script src="https://unpkg.com/vue"></script>
</head>
<body>
<div id="vue">
<svg :width="width" :height="height" style="border: 1px dotted">
<circle :cx="width/2"
:cy="height/2"
:r="r"
:fill="fill">
</circle>
</svg>
<div>r: {{ r }}</div>
<div><input type="range" v-model="r" min="1" max="150" step="1"></div>
</div>
<script>
new Vue({
el: '#vue',
data: {
width: 800,
height: 500,
r: 75,
fill: "rgba(59, 0, 255, 0.8)"
}
})
</script>
</body>
結果
変わらず見づらいですが、svgエリア下のレンジ入力欄で円の大きさを変更できます。
解説 その2 d3.jsを使った時との違い
今回の「その2」のコードは、先ほどの「その1 改」にinputタグを1行追加しただけです。
元のd3.jsを使ったコードと比較すると、格段にコード量が減っています。
今回の場合、元のコードに存在したmountedとwatchの下記部分は、まるまる不要だったことになります。
mounted: function(){
var svg = d3.select('#circle-002');
this.circle = svg.append('circle')
.attr('cx', this.width/2)
.attr('cy', this.height/2)
.attr('r', this.r)
.style('fill','rgba(0, 0, 0, 0.8)')
},
watch: {
r: function(newValue) {
this.circle.attr('r', newValue)
}
}
「Vue.js で d3.jsを使う」ということの意味
今回の例だと簡単すぎますが1、いや、だからこそ私も気づいたのですが、
Vue.jsとd3.jsを同時に使う場合、どうもd3.jsの出る幕がない場面が存在します。
データを元にDOMを常に最新の状態に保とうとするのがVue.jsで、
データをDOMに紐付けてグラフを描画するのがd3.jsです。
ライブラリの役割が中途半端に被っています。
そして今回のように、データを元にただ円の大きさや棒グラフの長さを変えたいなら、
間違いなくVue.js単体でデータとsvgの属性を直接紐付けた方が楽になります。
d3.jsのために余計なmountedやwatchを実装しなくて済むからです。
円を100個表示するような散布図を作る場合も、
データに応じて100本の棒グラフを描画する場合も、Vue.js単体で行った方が楽です。
円を描画するための要素数100の中心座標と半径の配列を持てば、v-forでちょうど100個の円を描画してくれます。
しかもその円は、d3.jsで作成する場合と違い、
データが更新されれば勝手に大きくなったり小さくなったり移動したりします。
データをわざわざ.exit()したり.update()したりする必要はありません。
どうするか
Vue.jsとd3.jsを同時に扱う場合、「d3.jsでしかできないこと」を考える必要があります。
~~パッと考えただけでも軸の描画はVue.jsではできませんし、~~地図の描画も無理でしょう。
<path>
を使った折れ線グラフのリアルタイムな更新もVue.jsでできるとは思えません。
→軸の描写や折れ線グラフもVueでできるっぽいです。
それぞれ強力な長所を持ち合わせたライブラリです。
しかし残念ながらピタッとはまるほど相性が良いわけではありません。
Vue.jsが得意な部分とd3.jsでしかできない部分をしっかり認識し、
さらにsvgの深い知識を元に、Vue.jsとd3.jsを結合していく必要があります。
-
そもそも今回の「その1 改」なら本来Vue.jsすら必要ありません。 ↩