Jqueryでグラフ表示
仕事でグラフを表示したいという要件が出てきた。
そのため、Jqueryでグラフ表示できるプラグインがないか調査することに。
んで、下記が候補として見つかったプラグイン。
グラフの見栄えがよく、様々なグラフを表示できる。 表現方法が多い。(色やプロットの形など) 特に、グラフをドラッグで変更できる点などは、素晴らしい。 APIも整備されていて、実装時に、探しやすい。
見栄えはjqplotより落ちるが、様々なグラフを表示できる。 機能的な部分が多い。ズーム、スクロール、リアルタイム描画、描画対象の変更等。 軽量のスクリプトになっているので、ソースリーディングが容易。
上記2つで作成した所、スマフォでのグラフ表示ということもあり、グラフ内部のスクロールが出来なければならないため、今回は「flot」を採用することにした。
それぞれで作成した方法を記述しておく。
jqplot
まず、WEBアプリ側で下記のようなJSONを返却するものを作成する。(今回は割愛。)
{"tempList":[["0","25"],["1","30"],["2","0"],["3","10"],["4","80"],["5","50"],["6","70"]],"rainList":[["0","18"],["1","13"],["2","12"],["3","13"],["4","9"],["5","4"],["6","3"]]}
そして、javascriptで上記JSONを返却するURLを非同期で呼び出し、結果からグラフを作成する。
JavaScriptは下記。
$.ajax({ type: 'GET', url: '/json/', dataType: 'json', async: true, success: function(data, textStatus) { var line1 = data.tempList; var line2 = data.rainList; var xticks = [[0, '12時'], [1, '13時'], [2, '14時'], [3, '15時'], [4, '16時'], [5, '17時'], [6, '18時']]; var yticks = [0, 5, 10, 15, 20]; var yticks2 = [0, 25, 50, 75, 100]; var jqplot = $.jqplot('plotGraf', [line1, line2], { legend: {show:true}, title: '2010/01/01', grid: {background:'#f3f3f3', gridLineColor:'#accf9b'}, series: [ {label:'気温', markerOptions:{style:'square'}}, {label:'降水確率', markerOptions:{style:'circle'}, yaxis:'y2axis'} ], axes: { xaxis:{ticks:xticks}, yaxis:{ticks:yticks, tickOptions:{formatString:'%d度'}}, y2axis:{ticks:yticks2, tickOptions:{formatString:'%d%'}} } }); }, error: function() { console.log('error'); } });
flot
同じく、WEBアプリ側で下記のようなJSONを返却するものを作成する。(今回は割愛。)
{"tempList":[["1293850800000","25"],["1293854400000","30"],["1293858000000","0"],["1293861600000","10"],["1293865200000","80"],["1293868800000","50"],["1293872400000","70"],["1293876000000","50"],["1293879600000","70"]],"rainList":[["1293850800000","18"],["1293854400000","13"],["1293858000000","12"],["1293861600000","13"],["1293865200000","9"],["1293868800000","4"],["1293872400000","3"],["1293876000000","3"],["1293879600000","3"]]}
そして、javascriptで上記JSONを返却するURLを非同期で呼び出し、結果からグラフを作成する。
JavaScriptは下記。
$.ajax({ type: 'GET', url: '/json/', dataType: 'json', async: true, success: function(data, textStatus) { var tempData = data['tempList']; var rainData = data['rainList']; var options = { series: { lines: { show: true }, shadowSize: 0 }, grid: { backgroundColor: '#FFFFFF' }, xaxes: [ { panRange: [tempData[0][0], tempData[tempData.length-1][0]], min: tempData[0][0], max: tempData.length < 4 ? tempData[tempData.length-1][0] : tempData[3][0], // 4つぶん出力する tickSize: 3600000, // 1日ぶんの間隔を空ける tickFormatter: function(val, axis) { var date = new Date(val); return date.getDate() + "日" + date.getHours() + "時"; } } ], yaxes: [ { panRange: false, tickFormatter: function(val, axis) { return val + '度'; } }, { panRange: false, alignTicksWithAxis: "right", position: "right", tickFormatter: function(val, axis) { return val + '%'; } } ], pan: { interactive: true } }; var plot = $.plot($("#placeholder"), [ {data: data.list, label: '気温', points: { show: true } }, {data: data.list2, label: '降水確率', points: { show: true }, yaxis: 2}, ], options); }, error: function() { console.log('error'); } });
また、flotのグラフスクロールは、スマフォには対応しておらず、少し改良が必要になる。
jquery.flot.navigation.jsの改良。
//147行目 function onDragStart(e) { // modify by ochi0218 // スマートフォンで対応出来るように修正。 // if (e.which != 1) // only accept left-click // return false; // var c = plot.getPlaceholder().css('cursor'); // if (c) // prevCursor = c; // plot.getPlaceholder().css('cursor', plot.getOptions().pan.cursor); if(!$.support.touch) { if (e.which != 1) // only accept left-click return false; var c = plot.getPlaceholder().css('cursor'); if (c) prevCursor = c; plot.getPlaceholder().css('cursor', plot.getOptions().pan.cursor); } e = (event.changedTouches && event.changedTouches.length) ? event.changedTouches[0] : e; // modify by ochi0218 prevPageX = e.pageX; prevPageY = e.pageY; } function onDrag(e) { var frameRate = plot.getOptions().pan.frameRate; if (panTimeout || !frameRate) return; // add by ochi0218 e = (event.changedTouches && event.changedTouches.length) ? event.changedTouches[0] : e; // add by ochi0218 panTimeout = setTimeout(function () { plot.pan({ left: prevPageX - e.pageX, top: prevPageY - e.pageY }); prevPageX = e.pageX; prevPageY = e.pageY; panTimeout = null; }, 1 / frameRate * 1000); } function onDragEnd(e) { if (panTimeout) { clearTimeout(panTimeout); panTimeout = null; } // add by ochi0218 e = (event.changedTouches && event.changedTouches.length) ? event.changedTouches[0] : e; // add by ochi0218 plot.getPlaceholder().css('cursor', prevCursor); // modify by ochi0218 // iphoneで正常に動作しないため、コメントアウト。 // plot.pan({ left: prevPageX - e.pageX, // top: prevPageY - e.pageY }); // modify by ochi0218 } --------------------------------------------------------------------------------- // 191行目 if (o.pan.interactive) { // modify by ochi0218 // スマートフォンで対応出来るように修正。 // eventHolder.bind("dragstart", { distance: 10 }, onDragStart); // eventHolder.bind("drag", onDrag); // eventHolder.bind("dragend", onDragEnd); if ($.support.touch) { eventHolder.bind("touchstart", { distance: 10 }, onDragStart); eventHolder.bind("touchmove", onDrag); eventHolder.bind("touchend", onDragEnd); } else { eventHolder.bind("dragstart", { distance: 10 }, onDragStart); eventHolder.bind("drag", onDrag); eventHolder.bind("dragend", onDragEnd); } // modify by ochi0218 } --------------------------------------------------------------------------------- //316行目 function shutdown(plot, eventHolder) { eventHolder.unbind(plot.getOptions().zoom.trigger, onZoomClick); eventHolder.unbind("mousewheel", onMouseWheel); // modify by ochi0218 // eventHolder.unbind("dragstart", onDragStart); // eventHolder.unbind("drag", onDrag); // eventHolder.unbind("dragend", onDragEnd); if ($.support.touch) { eventHolder.unbind("touchstart", onDragStart); eventHolder.unbind("touchmove", onDrag); eventHolder.unbind("touchend", onDragEnd); } else { eventHolder.unbind("dragstart", onDragStart); eventHolder.unbind("drag", onDrag); eventHolder.unbind("dragend", onDragEnd); } // modify by ochi0218 if (panTimeout) clearTimeout(panTimeout); } plot.hooks.bindEvents.push(bindEvents); plot.hooks.shutdown.push(shutdown); }
その結果がこれ。
スマートフォンブラウザでも、PCブラウザでも、ドラッグorスワイプで次の要素を表示させることが出来る。
やっぱり、見栄えはjqplotの方がよいね。。
flotでも、見栄えが近づけれるようにもうちょい頑張ってみよう!