最近因為客戶有將統計資料繪圖的需求
而開始研究D3.js 之前我大多使用 jQuery or JQWidgets的套件來繪圖
聽說使用D3前懂點 JQuery是好事,廢話不多說來切入今天的主題吧!
----------------------------------------------------------------------------------------
首先 D3是 Data Driven Document,所以data來源格式非常重要
現在大多是使用 JSON格式,且流行用Restful的模式
故我程式的範例也是使用 JSON來呼叫
而客戶又有 Mouserover & Legend的需求,這部分讓D3用起來更Complex
一開始的學習我建議可以參考
d3 Line Chart教學
而這個網址是我在JSFiddle建立的範例,修改之前的教學
http://jsfiddle.net/YenYu/hxn9L3cz/1/
以下為我的程式碼及預設的測試資料
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>MultiLine Chart</title>
<style>
#chart {
font: 16px Arial;
font-weight: 50;
background: #fff;
}
text {
color: #000;
stroke: #000;
fill: #000;
}
path {
stroke: DodgerBlue;
stroke-width: 1;
fill: none;
}
.axis line {
fill: none;
stroke: lightgray;
shape-rendering: crispEdges;
}
.x.axis path {
display: #fafafa;
}
.x.axis .minor {
stroke-opacity: .5;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
.overlay {
fill: none;
pointer-events: all;
}
.focus circle {
fill: none;
stroke: #0078ff;
}
.legend {
font-size: 16px;
font-weight: normal;
}
</style>
</head>
<body>
<div id="chart"></div>
</body>
<!--<script src="http://d3js.org/d3.v3.js"></script> -->
<script src="js/d3.min.js"></script>
<script>
var margin = {top: 50, right: 80, bottom: 50, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var parseDate = d3.time.format("%Y%m%d").parse,
bisectDate = d3.bisector(function(d) { return d.date; }).left;
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.category10();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom").orient('bottom').tickSize(-height).tickSubdivide(false);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.interpolate("linear")
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.count); });
var svg = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.json("http://192.168.73.1:2013/JQuery_Test/data/data2.txt", function(error, data) {
//color.domain(d3.keys(data,function(d){d.name}));
var unusualSummary = data.map(function(j) {
return {
name: j.name,
values: j.values.map(function(d) {
return {date: parseDate(d.date), count: d.count};
})
};
});
console.log('data: ' + JSON.stringify(data));
x.domain([
d3.min(unusualSummary, function(c) { return d3.min(c.values, function(v) { return v.date; }); }),
d3.max(unusualSummary, function(c) { return d3.max(c.values, function(v) { return v.date; }); })
]);
y.domain([
0,d3.max(unusualSummary, function(c) { return d3.max(c.values, function(v) { return parseInt(v.count); }); }) + 1
]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("x", 850)
.style("text-anchor", "middle")
.text("日期 ");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(0)")
.attr("x", 7)
.attr("y", -7)
.attr("dy", ".71em")
.style("text-anchor", "start")
.text("次數 ");
var statstic = svg.selectAll(".statstic")
.data(unusualSummary)
.enter().append("g")
.attr("class", "statstic");
var lSpace = width/data.length;
data.forEach(function(d,i) {
statstic.append('path')
.attr('d', function(d){return line(d.values,x,y);})
.style('stroke', function(d,j) {
return color(d.name);
})
.attr('stroke-width', 2)
.attr('id', 'line_'+d.name)
.attr('fill', 'none');
console.log('d name part I: ' + d.name);
statstic.append("text")
.attr("x", (lSpace/2)+i*lSpace)
.attr("y", height+30)
.style("fill", color(d.name))
.attr("class","legend")
.text(d.name+" ---").style("stroke", color(d.name));
});
var focus = svg.append("g")
.attr("class", "focus")
.style("display", "none");
for(var i=0;i<unusualSummary.length;i++){
focus.append("g")
.attr("class", "focus"+i)
.append("circle")
.attr("r", 6.5);
svg.select(".focus"+i)
.append("text")
.attr("x", 9)
.attr("dy", ".35em");
}
statstic.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.on("mouseover", function() { focus.style("display", null); })
.on("mouseout", function() { focus.style("display", "none");})
.on("mousemove", mousemove);
function mousemove() {
//alert('length: ' + unusualSummary.length);
var x0 = x.invert(d3.mouse(this)[0]);
for(var k=0;k<unusualSummary.length;k++){
var i = bisectDate(unusualSummary[k].values, x0, 1);
var d0 = unusualSummary[k].values[i - 1];
var d1 = unusualSummary[k].values[i];
var d = x0 - d0.date > d1.date - x0 ? d1 : d0;
var format = d3.time.format('%e %b');
var selectedFocus = svg.selectAll(".focus"+k);
selectedFocus.attr("transform", "translate(" + x(d.date) + "," + y(d.count) + ")");
selectedFocus.select("text").text(format(d.date)+","+d.count+"次");
}
}
});
</script>
</html>
我的Data範例如下,請自己在WebContent下建一個js的目錄放置js檔及測試檔
[
{
"name":"系統A",
"values":[{"date":"20111001", "count":"1"},
{"date":"20111002", "count":"2"},
{"date":"20111003", "count":"3"},
{"date":"20111004", "count":"4"},
{"date":"20111005", "count":"5"},
{"date":"20111006", "count":"6"}]
},
{
"name":"系統B",
"values":[{"date":"20111001", "count":"2"},
{"date":"20111002", "count":"4"},
{"date":"20111003", "count":"6"},
{"date":"20111004", "count":"8"},
{"date":"20111005", "count":"10"},
{"date":"20111006", "count":"12"}]
},
{
"name":"系統C",
"values":[{"date":"20111001", "count":"3"},
{"date":"20111002", "count":"6"},
{"date":"20111003", "count":"9"},
{"date":"20111004", "count":"12"},
{"date":"20111005", "count":"15"},
{"date":"20111006", "count":"18"}]
},
{
"name":"系統D",
"values":[{"date":"20111001", "count":"4"},
{"date":"20111002", "count":"8"},
{"date":"20111003", "count":"12"},
{"date":"20111004", "count":"16"},
{"date":"20111005", "count":"20"},
{"date":"20111006", "count":"30"}]
}
]
最後呈現的結果為