D3.jsを使用した折れ線グラフです。
このままでは少し味気がないので、縦軸の土曜日を青・日曜日を赤・ボーダーラインとして横軸の500をオレンジに変更したいと思います。
「Onsen UI V2 JS Minimum」というテンプレートを使用します。
日付の処理にMoment.jsを使用します。
以下のようにファイルを配置します。
index.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no, viewport-fit=cover"> <meta http-equiv="Content-Security-Policy" content="default-src * data: gap: https://ssl.gstatic.com; style-src * 'unsafe-inline'; script-src * 'unsafe-inline' 'unsafe-eval'"> <script src="components/loader.js"></script> <script src="lib/onsenui/js/onsenui.min.js"></script> <link rel="stylesheet" href="components/loader.css"> <link rel="stylesheet" href="lib/onsenui/css/onsenui.css"> <link rel="stylesheet" href="lib/onsenui/css/onsen-css-components.css"> <link rel="stylesheet" href="css/style.css"> <script src="js/moment.min.js"></script> <script src="js/d3.min.js"></script> <script src="js/d3chart.js"></script> <script src="js/app.js"></script> </head> <body> <ons-page> <ons-toolbar> <div class="center">D3.js Line Chart</div> </ons-toolbar> <div id="chart"></div> </ons-page> </body> </html> |
style.css
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
#chart { width: 100%; height: 240px; } #d3-chart { background-color: #fff; } .xaxis text, .yaxis text { font-size: 12px; } .xaxis path, .yaxis path, .xaxis line, .yaxis line { fill: none; stroke: #000; stroke-width: 1px; } .tick line { opacity: 0.1; stroke-width: 1px; } .line-red { fill: none; stroke: #ff4081; stroke-width: 2px; } .point-red { fill: #ff4081; stroke: #ff4081; stroke-width: 1px; } .axis-sunday { fill: none !important; stroke: #f00 !important; opacity: 0.3 !important; } .axis-saturday { fill: none !important; stroke: #00f !important; opacity: 0.3 !important; } .axis-border { fill: none !important; stroke: #f80 !important; opacity: 0.5 !important; stroke-width: 2px !important; } |
app.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
"use strict"; ons.ready(function () { var data = [ ["2020/10/1", 623], ["2020/10/2", 537], ["2020/10/3", 564], ["2020/10/4", 395], ["2020/10/5", 271], ["2020/10/6", 497], ["2020/10/7", 505], ["2020/10/8", 622], ["2020/10/9", 594], ["2020/10/10", 669], ["2020/10/11", 432], ["2020/10/12", 273], ["2020/10/13", 494], ["2020/10/14", 549], ["2020/10/15", 703], ["2020/10/16", 635], ["2020/10/17", 579], ["2020/10/18", 457], ["2020/10/19", 315] ]; var tm = null; for (var i = 0; i < data.length; i++) { tm = moment(data[i][0], "YYYY/M/D"); data[i][0] = tm.format("YYYYMMDD") + tm.day().toString(); } window.fn.chart.createLineChart("chart", data); }); |
d3chart.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
"use strict"; //************************************************************ // module: d3chart.js // author: otak-lab // use: d3.min.js //************************************************************ if (!window.hasOwnProperty("fn")) { window.fn = {}; } window.fn.chart = {}; //************************************************************ // create line chart //************************************************************ window.fn.chart.createLineChart = function (id, data) { var elem = document.getElementById(id); if (!elem) return; if (data.length == 0) return; // margin var margin = { top: 15, right: 15, bottom: 25, left: 30 }; // width, height var w = elem.clientWidth - (margin.left + margin.right); var h = elem.clientHeight - (margin.top + margin.bottom); // min, max var min = Math.min.apply(null, data.map(function (d) { return d[1]; })); var max = Math.max.apply(null, data.map(function (d) { return d[1]; })); if (min < 0) { min += Math.floor(min * 0.05); } else if (min > 0) { min -= Math.floor(max * 0.05); } if (max > 0) { max += Math.floor(max * 0.05); } else if (max < 0) { max -= Math.floor(min * 0.05); } // y var y = d3.scaleLinear() .domain([min, max]) .nice() .range([h, 0]); // x var x = d3.scaleBand() .domain(data.map(function (d) { return d[0]; })) .range([0, w]); // adjust line x positon var pos = (x(data[1][0]) - x(data[0][0])) / 2; // axis var yAxis = d3.axisLeft(y) .tickSizeInner(-w) .tickSizeOuter(0) .tickPadding(4) .tickFormat(function (d) { return d3.format(",")(d); }) .ticks(7); var xAxis = d3.axisBottom(x) .tickValues(x.domain()) .tickSizeInner(-h) .tickSizeOuter(0) .tickPadding(8) .tickFormat(function (d, i) { var result = Number(d.substr(6, 2)).toString(); if (i == 0) result = Number(d.substr(4, 2)).toString() + "/" + result; return result; }); // line var line = d3.line() .x(function (d) { return (x(d[0]) + pos); }) .y(function (d) { return y(d[1]); }) .curve(d3.curveLinear); // remove chart d3.select("#d3-chart").remove(); // create svg var svg = d3.select(elem) .append("svg") .attr("id", "d3-chart") .attr("width", elem.clientWidth.toString() + "px") .attr("height", elem.clientHeight.toString() + "px") .append("g") .attr("transform", "translate(" + margin.left.toString() + ", " + margin.top.toString() + ")"); // create x svg.append("g") .attr("class", "xaxis") .attr("transform", "translate(0, " + h.toString() + ")") .call(xAxis); // sunday svg.selectAll(".xaxis .tick line") .filter(function (d) { return (d.substr(8, 1) == "0") ? true : false; }) .attr("class", "axis-sunday"); // saturday svg.selectAll(".xaxis .tick line") .filter(function (d) { return (d.substr(8, 1) == "6") ? true : false; }) .attr("class", "axis-saturday"); // create y svg.append("g") .attr("class", "yaxis") .call(yAxis); // border svg.selectAll(".yaxis .tick line") .filter(function (d) { return (d == 500) ? true : false; }) .attr("class", "axis-border"); // line svg.append("path") .attr("class", "line-red") .attr("d", line(data)); // point svg.selectAll("circle") .data(data) .enter() .append("circle") .attr("class", "point-red") .attr("cx", function (d) { return x(d[0]) + pos; }) .attr("cy", function (d) { return y(d[1]); }) .attr("r", "4px"); }; |
解説します。
まず、app.jsの27行で日付の処理を行っていますが、日曜日と土曜日を区別するため、日付「YYYYMMDD」の後ろに曜日を意味する0~6の数字を付け加えます。
次に、d3chart.jsの94~101行で縦軸の色を変更しています。
selectAll()で縦軸を抽出し、filter()のfunctionのdは日付+曜日が入っているので、最後尾の一文字が「0」は日曜日・「6」は土曜日の場合、trueを返し、該当する縦軸を抽出します。
そして、その縦軸に対してCSSクラスaxis-sunday・axis-saturdayを追加します。
最後に、d3chart.jsの106~109行で横軸の色を変更しています。
selectAll()で横軸を抽出し、filter()のfunctionのdは200~700の数値が入っているので、その数値が500の場合、trueを返し、該当する横軸を抽出します。
そして、その横軸に対してCSSクラスaxis-borderを追加します。
注意点は、追加するCSSクラスの値に必ず!importantを付け加えることです。
そうしないと、色が変わりません。
Monacaデバッガーの結果です。
Android版
iOS版
実は、シンプル体重管理のグラフに日曜日の縦線を表示する機能を追加する作業に、2日間もかかってしまいました。
最初は、自分の思い込みでeach()を使ってコードを書きましたが、何か違うような気がして、D3.jsのドキュメントを何度も読んで見つけたのがfilter()でした。