はじめに
チームの1年間のいいね数をD3.jsを使ってアニメーションで可視化する
以前この記事でD3.jsを使った、いいねをアニメーションで可視化する手法を紹介しました。
今回はQiita APIを使って、LGTMを可視化するサービスをGithub Pagesを使って公開してみました。
サービス
Qiita Racer
※このヌルヌル動くグラフのことをRacing Bar Chartと呼ぶらしいので、Qiita Racerと名付けてみました。
ソース
こちらに公開しています。
https://github.com/tonio0720/QiitaRacing
使い方
URLにアクセスすると「アクセスの許可を求めています」と出るのでそのまま許可してください。
ユーザーID(最大8個)を入力し、データ取得を実行すると動きます。
※QiitaAPIの制限の都合により、総LGTM数が1000以上のユーザーは抽出できないように制御しています。
リリースするまで
リリースに至るまでのプロセスを少し紹介します。
何かの参考にしてもらえればと思います。
Reactで画面開発
https://qiita.com/tonio0720/items/0b9d670389286171af07
この記事で紹介した、Reactのテンプレートを使って開発しています。
今回はブラウザから直接Qiita APIを叩くのでサーバーは必要ありません。
ReactとD3.jsの合わせ技で動く棒グラフは作っています。
import React from 'react';
import * as d3 from 'd3';
import styles from './index.module.less';
const time = 100;
export default class RacingBarChart extends React.Component {
constructor(props) {
super(props);
this.chart = React.createRef();
this.user2Color = {};
}
componentDidMount() {
const margin = {
top: 0,
right: 40,
bottom: 30,
left: 120
};
const width = this.chart.current.parentNode.clientWidth - margin.left - margin.right;
const height = this.chart.current.parentNode.clientHeight - margin.top - margin.bottom;
this.width = width;
this.height = height;
this.svg = d3.select(this.chart.current)
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
this.yScale = d3.scaleBand()
.rangeRound([height, 0], 0.1)
.padding(0.4);
console.log(this);
}
moveToDate = (dateData) => {
const {
width,
height,
svg,
yScale,
user2Color
} = this;
return new Promise((resolve) => {
const t = d3.transition().duration(time).on('end', resolve);
const data = Object.keys(dateData).map((user) => {
const value = dateData[user] || 0;
return {
name: user,
value
};
}).sort((a, b) => d3.ascending(a.value, b.value));
const max = d3.max(data, (d) => d.value);
const xScale = d3.scaleLinear()
.range([0, width])
.domain([0, max < 10 ? 10 : max]);
yScale.domain(data.map((d) => d.name));
let xAxis = svg.select('.x.axis');
if (xAxis.empty()) {
xAxis = svg.append('g')
.attr('class', 'x axis')
.attr('transform', `translate(0,${height})`);
}
xAxis.transition(t)
.call(d3.axisBottom(xScale))
.selectAll('g');
let axis = svg.select('.y.axis');
if (axis.empty()) {
axis = svg.append('g')
.attr('class', 'y axis');
}
let barsG = svg.select('.bars-g');
if (barsG.empty()) {
barsG = svg.append('g')
.attr('class', 'bars-g');
}
const bars = barsG
.selectAll('.bar')
.data(data, function (d) {
return d.name;
});
bars.exit().remove();
const enterBars = bars.enter();
enterBars
.append('rect')
.attr('class', 'bar')
.attr('x', 0)
.merge(bars)
.style('fill', function (d) {
return user2Color[d.name] || '#333';
})
.transition(t)
.attr('height', () => yScale.bandwidth())
.attr('y', function (d) {
return yScale(d.name);
})
.attr('width', function (d) {
return xScale(d.value);
});
const labels = barsG
.selectAll('.label')
.data(data, function (d) {
return d.name;
});
labels.exit().remove();
const enterLabels = labels.enter();
enterLabels
.append('text')
.attr('class', 'label')
.attr('x', function (d) {
return 0;
})
.merge(labels)
.transition(t)
.attr('y', function (d) {
return yScale(d.name) + yScale.bandwidth() / 2 + 4;
})
.attr('x', function (d) {
return xScale(d.value) + 3;
})
.tween('text', function (d) {
const selection = d3.select(this);
const start = d3.select(this).text();
const end = d.value;
const interpolator = d3.interpolateNumber(start, end);
return function (t) { selection.text(Math.round(interpolator(t))); };
});
axis.transition(t)
.call(d3.axisLeft(yScale))
.selectAll('g');
});
}
render() {
return (
<div style={{ height: 400 }} className={styles.racingBarChart}>
<svg ref={this.chart} />
</div>
);
}
}
Sliderと再生ボタンを配置すると、動画っぽくなっていいです。
動かすとグラフも連動して動きます。
使用したフレームワーク/ライブラリ
- React
- D3.js
- Ant Design
OAuthでQiita APIのトークンを発行
OAuthを使ってトークンの発行をしています。
こちらのサイトがとても参考になりました。
https://qiita.com/nutti/items/688de20382e60286d26d
Github Pagesに公開
https://qiita.com/star__hoshi/items/490959aee12dbf528f7c
npmのgh-pages
というモジュールを使いました。
"scripts": {
...
"predeploy": "npm run build",
"deploy": "gh-pages -d build"
},
コマンドで下記を実行
npm run deploy
Github → Settings → GitHub PagesのSourceを「gh-pages Branch」に変更
所感
今回はツールを作って、Github Pagesに公開するということをやってみました。
Github Pagesは初めて使いましたが、とても便利ですね。
Qiita APIの1時間に1000リクエストの制約は少し不便ですね。
本ツールではユーザーに紐づくすべての記事を取得して、それぞれの記事のLGTM数を取得するということをやっているので、結構APIを消費します。
1ページにおける最大取得件数が100件というのもあり、あまりこういう使い方は向いていないのだなと感じました。
本ツールでは総LGTM数が1000を超えるユーザーは抽出できないように制御しています。
今後の展望について
Qiita APIだと何かと制約が多いので、日々データをDBに格納し、その結果を抽出/可視化ができるようなサービスを作りたいですね。
独自のQiitaダッシュボードとかができるとさらに面白みが増しますね。
おわりに
という感じで色々やってみましたが、やっぱり楽しいですね。
Github PagesとReactを使うとあっという間にアプリケーションがリリースできるので今後も使っていきたいです。
自分やチームの投稿を振り返る際にでも活用してもらえると嬉しいです!