1. 程式人生 > >【D3.JS資料視覺化實戰記錄】繪製動態狀態變化趨勢圖

【D3.JS資料視覺化實戰記錄】繪製動態狀態變化趨勢圖

參考d3js.org的Health&Wealth例項(http://bost.ocks.org/mike/nations/)按照時間展示節點狀態變化。

需要展示的json:

[{"calendar":"2012-01-01 12:00:00",
  "values":[{"name":"A","size":100,"traffic":"20000","cpu":12.9},
			{"name":"B","size":10,"traffic":"1000","cpu":3.8},
			{"name":"C","size":300,"traffic":"20000","cpu":30.8},
			{"name":"D","size":1000,"traffic":"10000","cpu":14}]
 },
 {"calendar":"2012-01-01 12:01:00",
  "values":[{"name":"A","size":100,"traffic":"30000","cpu":30.2},
			{"name":"B","size":10,"traffic":"2000","cpu":20},
			{"name":"C","size":300,"traffic":"30000","cpu":12.8},
			{"name":"D","size":1000,"traffic":"20000","cpu":20.2}]
 },
 {"calendar":"2012-01-01 12:02:00",
  "values":[{"name":"A","size":100,"traffic":"40000","cpu":30.2},
			{"name":"B","size":10,"traffic":"3000","cpu":20},
			{"name":"C","size":300,"traffic":"40000","cpu":12.8},
			{"name":"D","size":1000,"traffic":"30000","cpu":20.2}]
 },
 {"calendar":"2012-01-01 12:03:00",
  "values":[{"name":"A","size":100,"traffic":"50000","cpu":30.2},
			{"name":"B","size":10,"traffic":"4000","cpu":20},
			{"name":"C","size":300,"traffic":"50000","cpu":12.8},
			{"name":"D","size":1000,"traffic":"40000","cpu":20.2}]
 },
 {"calendar":"2012-01-01 12:04:00",
  "values":[{"name":"A","size":100,"traffic":"10000","cpu":30.2},
			{"name":"B","size":10,"traffic":"5000","cpu":60},
			{"name":"C","size":300,"traffic":"20000","cpu":12.8},
			{"name":"D","size":1000,"traffic":"50000","cpu":20.2}]
 },
 {"calendar":"2012-01-01 12:05:00",
  "values":[{"name":"A","size":100,"traffic":"70000","cpu":30.2},
			{"name":"B","size":10,"traffic":"6000","cpu":20},
			{"name":"C","size":300,"traffic":"70000","cpu":12.8},
			{"name":"D","size":1000,"traffic":"60000","cpu":20.2}]
 },
 {"calendar":"2012-01-01 12:06:00",
  "values":[{"name":"A","size":100,"traffic":"10000","cpu":30.2},
			{"name":"B","size":10,"traffic":"6000","cpu":20},
			{"name":"C","size":300,"traffic":"20000","cpu":12.8},
			{"name":"D","size":1000,"traffic":"60000","cpu":20.2}]
 }
]

原始碼:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<title>The States of Pools</title>
<style>
body {
	background-color: black;
	color: white;
	text-align: center;
}

#chart {
	margin-left: 0px;
	height: 706px;
}

text {
	font: 10px sans-serif;
}

.dot:hover {
	stroke: #FFF;
	stroke-width:2px;
}

.axis path,.axis line {
	fill: none;
	stroke: gray;
	shape-rendering: crispEdges;
}

.axis text {
	stroke: gray;
}

.label {
	fill: #777;
}

.calendar.label {
	font: 30px "Arial,Verdana,Sans-serif";
	fill: white;
}

.calendar.label.active {
	fill: lightgreen;
}

.overlay {
	fill: none;
	pointer-events: all;
	cursor: ew-resize;
}
</style>
<p id="chart"></p>
<script src="./jsllib/d3.min.js"></script>
<script src="./jsllib/jquery.min.js"></script>
<script>
	// Various accessors that specify the four dimensions of data to visualize.
	function x(d) {
		return d.traffic;
	}
	function y(d) {
		return d.cpu;
	}
	function radius(d) {
		return d.size;
	}
	function color(d) {
		return d.domain;
	}
	function key(d) {
		return d.name;
	}
	function visual(data){
		// Chart dimensions.
		var margin = {
			top : 39.5,
			right : 19.5,
			bottom : 39.5,
			left : 39.5}, 
			width = $(window).width() - 100 - margin.right, 
			height = $(window).height()	- 50 - margin.top - margin.bottom;
		
		//various scales, based on the minimun and maximun of each property.
		var xScale = d3.scale.log().domain([min(data,"traffic"), max(data,"traffic")]).range([ 0, width ]), 
			yScale = d3.scale.linear().domain([ 0, max(data,"cpu") ]).range([ height, 0 ]), 
			radiusScale = d3.scale.sqrt().domain([min(data,"size"), max(data,"size")]).range([ 3, 40 ]), 
			colorScale = d3.scale.category20();
	
		// The x & y axes.
		var xAxis = d3.svg.axis().orient("bottom").scale(xScale).ticks(20,d3.format(",d")),
			yAxis = d3.svg.axis().scale(yScale).orient("left");
	
		// Create the SVG container and set the origin.
		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 + ")");
	
		// Add the x-axis.
		svg.append("g")
			.attr("class", "x axis")
			.attr("transform","translate(0," + height + ")")
			.call(xAxis);
	
		// Add the y-axis.
		svg.append("g")
			.attr("class", "y axis")
			.call(yAxis);
	
		// Add an x-axis label.
		svg.append("text")
			.attr("class", "x label")
			.attr("text-anchor", "end")
			.attr("x", width)
			.attr("y", height - 6)
			.text("Traffic (BPS)");
	
		// Add a y-axis label.
		svg.append("text")
			.attr("class", "y label")
			.attr("text-anchor", "end")
			.attr("y", 6).attr("dy", ".75em")
			.attr("transform", "rotate(-90)")
			.text("CPU usage (%)");
	
		// Add the time label; the value is set on transition.
		var label = svg.append("text").attr("class", "calendar label")
						.attr("text-anchor", "end")
						.attr("y", height - 24)
						.attr("x", width)
						.text(data[0].calendar);
		
		var box = label.node().getBBox();
		var overlay = svg.append("rect")
						.attr("class", "overlay")
						.attr("x", box.x)
						.attr("y", box.y)
						.attr("width", box.width)
						.attr("height", box.height)
						.on("mouseover", enableInteraction);
		
		var timerId = 0;
		function enableInteraction(){
			var timeScale = d3.scale.linear()
								.domain([0, data.length-1])
								.range([box.x + 10, box.x + box.width - 10])
								.clamp(true);
	    	// Cancel the current transition, if any.
			if(timerId){
	    		clearInterval(timerId);
			}
			overlay.on("mouseover", mouseover)
					.on("mouseout", mouseout)
					.on("mousemove", mousemove)
					.on("touchmove", mousemove);
			function mouseover() {
				label.classed("active", true);
		    }
		    function mouseout() {
				label.classed("active", false);
		    }
			function mousemove() {
				tweenMinute(Math.round(timeScale.invert(d3.mouse(this)[0])));
			}
		}

		// Add a dot per node. Initialize the data with data[0].data, and set the colors.
		var dot = svg.append("g")
					.attr("class", "dots")
					.selectAll(".dot")
					.data(data[0].values)
					.enter()
					.append("circle")
					.attr("class","dot")
					.style("fill", function(d) {
						return colorScale(color(d));
					})
					.call(position)
					.sort(order);
		
		// Add a title.
		dot.append("title")
		    .text(function(d) { return d.name; });
		
		// Positions the dots based on data.
		function position(dot) {
			dot.attr("cx", function(d) { return xScale(x(d)); })
				.attr("cy", function(d) { return yScale(y(d)); })
				.attr("r", function(d) { return radiusScale(radius(d)); });
		}
		 
		// Defines a sort order so that the smallest dots are drawn on top.
		function order(a, b) {
			return radius(b) - radius(a);
		}
		
		// Start a transition that interpolates the data based on year.
 		var i = 0;
		timerId = setInterval(function(){
			if(i<data.length){
				tweenMinute(i);
				i++;
			}
			else{
				clearInterval(this);
			}
		},200);
		
		function tweenMinute(i) {
			dot.data(data[i].values)
				.style("fill", function(d) {
					return colorScale(color(d));
				})
				.call(position)
				.sort(order);
			label.text(data[i].calendar);	
		}
	}
	//get the minimun property of all nodes in order to scale the size and position of circles.
	function min(data, prop) {
		var length = data.length;
		var mins = [];
		for (var i = 0; i < length; i++) {
			var min = d3.min(data[i].values, function(d) {
				if (prop == "size")
					return d.size;
				else if (prop == "traffic")
					return d.traffic;
				else if (prop == "cpu")
					return d.cpu;
			});
			mins.push(min);
		}
		return d3.min(mins);
	}
	//get the maximun property of all nodes in order to scale the size and position of circles.
	function max(data, prop) {
		var length = data.length;
		var maxs = [];
		for (var i = 0; i < length; i++) {
			var max = d3.max(data[i].values, function(d) {
				if (prop == "size")
					return d.size;
				else if (prop == "traffic")
					return d.traffic;
				else if (prop == "cpu")
					return d.cpu;
			});
			maxs.push(max);
		}
		return d3.max(maxs);
	}

	// Load the data.
	d3.json("poolStates.json", function(data) {
		console.log(data);
		visual(data);
	});
</script>