背景
横断的にゲーミングPCを比較検討するサイトがないなと感じたことをきっかけに、散布図的にパソコンを比較検討できるサイトを作ろうと思ったのが始まりでした。 サイトのイメージやテーブル設計はスラスラと出来上がったもののjavascriptでクリック可能な散布図を作ろうと思った時に意外といいものが見つからず、最終的にたどり着いたのがd3.jsでした。 後々知ったのですがd3.jsは古くからあるライブラリでNuxtの利用事例はそこまで見当たらなかったですが、 githubのスターはとても多くとても優れたライブラリのようですね。
今回はそんなd3を利用して散布図を書いてみる方法についてまとめてみようと思います。
完成物
ゲーミングサーチ(現在は閉鎖)
サイト自体は細かい箇所を修正中(2021/10/29)なのですが、散布図自体は完成しいい感じにできています。
インストール
npm install d3 --save
build: { standalone: true, }
をnuxt.config.js
に追記するimport * as d3 from "d3";
利用したいページやコンポーネントの中でimportする
※環境によって変わる可能性がありますが、手準2を行わないと色々とエラーが出てしまうので注意してください。
設計
DOM構成
d3は
d3
.select('#chart')
.append('svg')
.attr('wid...
といった形で、 DOM構成をjavascript上で追加していくことができるライブラリとなっています。 つまり散布図の場合は「グラフを書く」と言うよりも「グラフになるように点や線を指定箇所に置いていく」といった感覚に近いものを感じました。 (x軸・y軸はそれっぽい感じに描いてくれる関数があるので全ての位置を指定していくわけではないです)
ちなみにchart.jsなどを利用していた私にとってはこのDOMで作っていくイメージが掴めず結構苦労をしたので、 最初にd3のサンプルコードを読み込んでDOMをどのように作っていくかを学ぶのがお勧めかと思います。
クリックやマウスオーバー
d3がDOMを作りイベントやattributeを好きに追加できるため、 クリックイベントなどはほぼ自由になんでも加えることができる状況でした。 いくつか案はあったのですがひとまず以下のような機能を追加しました。
- onclickイベントを各ポイントに追加(PCの詳細モーダルが表示される)
- mouseoverイベントを各ポイントに追加(名前が濃く表示される)
- 右下の会社ラベルにmouseoverイベントを追加(対象データだけが濃く出るようになっている)
三つ目は参考にしたサイトで使っていたのでそのまま入れた節はありますが、 基本的にグラフ上の情報はなるべく落として見やすさを向上させるために使っています
開発
DOM構成
基本的には以下のような関数を繋げる形で構成を作っていきます。
関数 | 説明 | 例 |
---|---|---|
.select | DOMの特定要素を取得 | .select('#chart') |
.append | 特定要素の中に要素を追加 | .append('svg') |
.attr | 特定要素にattributionを追加 | .attr('width', 300) |
.style | 特定要素にstyleを追加 | .style('fill', 'gray') |
.on | 特定要素にclickイベントなどを追加 | .on('click', function (data, elem) { console.log('test') }) |
例えば描写部分(DOM構成画像における真ん中のgまで)で言うと以下のように作ることができます。
d3
.select('#chart')
.append('svg')
.attr('width', <em>this</em>.width + <em>this</em>.margin.left + <em>this</em>.margin.right)
.attr('height', <em>this</em>.height + <em>this</em>.margin.top + <em>this</em>.margin.bottom)
.append('g')
.attr('transform', `translate(${<em>this</em>.margin.left},${<em>this</em>.margin.top})`)
クリックやマウスオーバー
イベントの追加については上記の .on
を利用することで好きな場所に入れることができます。 また attr
を使うことでclass名などもついかできるため、 クラス名をつけた上でイベント内で以下のような関数を呼び出すことで色の濃さなどを変更することができます。
d3
.select('.bubbles' + d.id).style('opacity', 1)
vueファイルの概要
<template>
<div>
<v-card flat>
<v-card-title class="text-h6 font-weight-black pb-2"
>マッピング</v-card-title
>
<v-card-text class="pa-0" align="center">
<div id="chart"></div>
</v-card-text>
...
</v-card>
</div>
<template>
<script>
import * as d3 from 'd3'
export default {
...
mounted() {
this.updateBubble(this.itemSets)
},
methods: {
updateBubble(itemSets) {
d3.selectAll('svg > *').remove()
d3.selectAll('svg').remove()
d3
.select('#chart')
.append('svg')
.attr('width', this.width + this.margin.left + this.margin.right)
.attr('height', this.height + this.margin.top + this.margin.bottom)
.append('g')
.attr('transform', `translate(${this.margin.left},${this.margin.top})`)
...
},
},
}
</script>
感想
今回Nuxtjs上でクリックなどできる散布図を作成してみました。ライブラリによってできることできないことが様々かと思いますが、 d3.jsに関してはDOMの構成から行うため設計次第でなんでもできる非常に柔軟性の高いライブラリだと感じました。 「クリックできればいいか」程度に感じていたのですが色々できることがわかったので、
- 散布図のドットを画像に変えてみる
- フィルター条件で表示するドットを変える
- ゲームの必要スペック等の閾値線を追加する
といったことを今後やっていきたいと思います。