初めてのCanvasでtwitterの発言数を折れ線グラフにしてみるのと、jsdo.itを使ってみた
jsdo.itというサイトを知ったので、使ってみようと思い、ついでに触ったことないCanvasに触れてみた。
参考にしたサイト
- 今更聞けないcanvasの基礎の基礎 | tech.kayac.com - KAYAC engineers' blog
- これ見て楽しそうだなと思いcanvas触ってみた
- 加速度センサーを使いたかったんだけど、Safariで使えなくて断念
- ASCII.jp:HTML5先取り!CanvasならFlash不要で絵が描ける (1/3)
- 折れ線グラフを含めたcanvasの解説サイト
- このサイトのコードをほぼ丸っとコピーしてきました
- Canvasリファレンス - HTML5.JP
- Basic usage - Trash | MDN
- mozillaのcanvas解説サイト。<canvas style="width:300px height:150px">とやっても、サイズが300x150なので「アレレ?」と思いたどり着いた
初めにHTML
ということでcanvasの作るためにまずはHTMLを書きます。
これは簡単。canvasタグを書くだけです。
<canvas width='500' height='300'></canvas>
style属性で指定すると要素のサイズはその指定通りになるのだけど、canvasの秒が領域が300x150のデフォルトのままなのでwidthとheightで指定する。
描画部分のJavaScript
折れ線グラフの描画部分のJavaScriptですが、下のような配列を想定してます。
var data = [ {label: 0, value: 10}, {label: 10, value: 20}, {label: 20, value: 30}, {label: 30, value: 40}, {label: 40, value: 50} ];
labalはx軸の目盛りに書かれる内容。valueがその時のyの値。
x軸は等間隔でプロットされます。
で、実際に描画するdraw関数は以下。
初めのADJはcanvasの調整分です。この辺は適当。
細かい部分は後述。
function draw(data) { var ADJ = 20; // canvasのマージン var canvas = document.getElementsByTagName('canvas')[0]; // canvasに対応してなければ抜ける if (! canvas.getContext) return; var ctx = canvas.getContext('2d'); // x,yの最小値、最大値のcanvasの座標 var point = { x: { min: ADJ, max: canvas.width - ADJ }, y: { min: canvas.height - ADJ, max: ADJ } }; // 軸の長さ var axisLengthOf = { x: point.x.max - point.x.min, y: point.y.min - point.y.max, }; // x軸とy軸を描く ctx.beginPath(); // 線の色を黒にする ctx.strokeStyle = "#000"; // x ctx.moveTo(point.x.min, point.y.min); ctx.lineTo(point.x.max, point.y.min); // y ctx.moveTo(point.x.min, point.y.min); ctx.lineTo(point.x.min, point.y.max); ctx.stroke(); // 折れ線グラフを描く // yの縮尺を変えるために最大値を取得 var dataMax = 0; for (var i=0, l=data.length; i<l; ++i) { if (data[i].value > dataMax) { dataMax = data[i].value; } } ctx.beginPath(); // 線の色を赤にする ctx.strokeStyle = "#f00"; for (var i=0, l=data.length; i<l; ++i) { // x: 配列の要素数で等分になるように // y: valuesの最大の値がcanvasを超えても描画できるように var x = point.x.min + (axisLengthOf.x / l * i); var y = point.y.min - (data[i].value / dataMax * axisLengthOf.y); // 目盛り ctx.fillText(data[i].label, x, point.y.min); // 点 ctx.arc(x, y, 1, 0, Math.PI * 2, false); // text ctx.fillText(data[i].value, x, y); } ctx.stroke(); }
canvasを使うための出だし
まず、canvasを使うためにcanvas要素を取得します。
で、canvas要素にgetContextがない場合はそもそもcanvasに対応していないブラウザなので何もせず抜けます。
最後のcanvas.getContext('2d')は「2次元描画のためのAPIを取得」かな。
この辺はいつも同じ使い回しできる場所です。
var canvas = document.getElementsByTagName('canvas')[0]; // canvasに対応してなければ抜ける if (! canvas.getContext) return; var ctx = canvas.getContext('2d');
x軸、y軸の基準点など
次に折れ線グラフを描画するためにx軸、y軸についての情報を定義してます。
canvasは(というよりもブラウザは)左上が0,0なので折れ線グラフを描画する時わかりにくいなと思い、
x軸、y軸の交点が基準になるようにしたかったので定義しました。
// x,yの最小値、最大値のcanvasの座標 var point = { x: { min: ADJ, max: canvas.width - ADJ }, y: { min: canvas.height - ADJ, max: ADJ } }; // 軸の長さ var axisLengthOf = { x: point.x.max - point.x.min, y: point.y.min - point.y.max, };
x軸、y軸の描画
ここから描画の話。
基本的には「beginPathしてstroke」すれば線が引けるようです。
その間に「色決め」「どこから線を引く」「どこまで線を引く」をやってるだけですね。
「moveToで左上を基準とした座標に移動、lineToでその座標まで線を引く」という流れなので、
サンプルとか見ると「moveToで左上から近い座標へ移動、そこから下まで線を引き(y軸)、さらに右は時まで線を引く(x軸)」って感じでやってたりするのですが、
x軸とy軸の交点から引いた方が感覚的にわかりやすいなと思ったので、
毎回moveTo(point.x.min, point.y.min)から線を引いてます。
// x軸とy軸を描く ctx.beginPath(); // 線の色を黒にする ctx.strokeStyle = "#000"; // x ctx.moveTo(point.x.min, point.y.min); ctx.lineTo(point.x.max, point.y.min); // y ctx.moveTo(point.x.min, point.y.min); ctx.lineTo(point.x.min, point.y.max); ctx.stroke();
折れ線グラフの描画
最後に折れ線グラフの描画。
まず、canvasのheight以上の値をそのまま描画できないため、最大値を基準に縮尺を変えます。
というこで配列の最大値を取得。
// 折れ線グラフを描く // yの縮尺を変えるために最大値を取得 var dataMax = 0; for (var i=0, l=data.length; i<l; ++i) { if (data[i].value > dataMax) { dataMax = data[i].value; } }
その後は折れ線グラフ。
x軸、y軸同様、「beginPathしてstroke」。
配列を回すforの中でやってることは、
- xとyの座標を求める。
- labelプロパティをy軸上に描画
- arcを使って点の描画(一つ前の点からの線を描画)準備
- その地点にvalueプロパティを描画
という感じ。
fillTextは実行時に描画されて、点と線に付いてはstroke時点で描画されると。
ctx.beginPath(); // 線の色を赤にする ctx.strokeStyle = "#f00"; for (var i=0, l=data.length; i<l; ++i) { // x: 配列の要素数で等分になるように // y: valuesの最大の値がcanvasを超えても描画できるように var x = point.x.min + (axisLengthOf.x / l * i); var y = point.y.min - (data[i].value / dataMax * axisLengthOf.y); // 目盛り ctx.fillText(data[i].label, x, point.y.min); // 点 ctx.arc(x, y, 1, 0, Math.PI * 2, false); // text ctx.fillText(data[i].value, x, y); } ctx.stroke();
グラフの描画についてはこんな感じで、あとはonload時にでも配列を渡せば折れ線グラフが表示されます。
せっかくなのでtwitterへの投稿数をtwilogから取得して表示してみる
とりあえず折れ線グラフを表示できたけど、このままjsdo.itに貼り付けてもイマイチなので、
twitterの何かでも貼り付けてみようかなとAPIを確認。
イマイチ良さそうなのがなかったのでtwilogのRSSを使って最近の投稿数を表示させてみることにしました。
RSSが外部ドメインなのでGoogle AJAX Feed APIを使う
クライアントだけでどうにかしようとするとRSSの取得がでないため、
Google AJAX Feed API への登録からGoogle AJAX Feed APIの登録をしてtwilogのRSSをJavaScriptで扱えるようにする。
APIキーが貰えるので、
<script type="text/javascript" src="http://www.google.com/jsapi?key=<APIキー>"></script>
で、Googleのライブラリを読み込み。
APIキーを取得した時にサンプルコードが表示されるので、それを参考にしながら以下のようなコード書けばOK。
var twilogUrl = 'http://twilog.org/rss-feed/lesamoureuses'; google.load("feeds", "1"); google.setOnLoadCallback(initialize); function initialize() { var feed = new google.feeds.Feed(twilogUrl); var twilog = []; feed.load(function(result) { if (result.error) return; for (var i=0, l=result.feed.entries.length; l>i; --l) { var entry = result.feed.entries[l-1]; var $content = $(entry.content); twilog.push({label: entry.title.replace(/^[^\d]*([0-9]+)[^0-9]+([0-9]+)[^\d]+/, '$1/$2'), value: $content.filter('div').length}) } draw(twilog); }); }
new google.feeds.Feedで外部RSSをjson形式に変換してらえるので、
あとはさっき作ったdraw()に渡す配列twilogを作るだけ。
entry.contentにtwitterの発言が入っているのでjQueryオブジェクト作って発言数を数えたりしてます。
jsdo.itを使ってみる
最後に今まで作ったものをjsdo.itに貼り付け。
JavaScriptのテキストエリアに今まで作ったものを貼り付け。
jsdo.itはjQueryやPrototype.jsなどの有名どころのライブラリを読み込めるのだけど、
貼り付けてる時はそこに気付かなかったので、HTMLのcanvasタグの後にscriptタグを書いて読み込むようにしました。
(その上のテキストエリアのJavaScriptはHTMLの後に追記されるので。)
以上でおしまい。
jsdo.itは手軽にJavaScriptが作れるので面白いです。