iT邦幫忙

DAY 18
2

SVG 與 D3.js系列 第 18

D3.js 究竟搭不搭捷運與死亡率有沒有關係?(3) - 將資料繪製成折線圖

  • 分享至 

  • xImage
  •  

完整範例:http://wcc723.github.io/d3js/2014/10/17/Ironman-30-days-18/

有了資料跟一些D3.js基礎後,就可以嘗試把資料轉換成圖,雖然萬事已經具備,但要繪製成圖,還是需要費些心力。

在資料上,先前有提到高雄縣市合併有影響死亡率,所以這次還有再補上高雄縣市合併的資料。

互動

這次還之前的不同,還有加上簡單的互動,而D3js的互動和jQuery寫法觀念相當接近,只是操作DOM時把$換成了d3,如果熟悉jquery的開發者很快就能上手。

範例大概如下:

svg.selectAll('.class').on('mouseover', function(d){ 
		//do something
	}).on('mouseout', function(d){ 
		//do something
	});

剩下的部分,就直接看code應該會比較理解~

$(function() {	

var shPath = 'https://spreadsheets.google.com/feeds/list/',
	shKey = '1hX3lqWLHFuwYiQeaBL0WevleUEOBAPKzshj2fJHogsM',
	shCallback = '/public/values?alt=json-in-script&callback=?',
	shList = [
		{
			'listKey': 'od6',
		},{
			'listKey': 'ol1cvs7',
		}
		,{
			'listKey': 'objevh6',
		} //這部分是高雄縣市的合併資料
		]

var url = shPath + shKey + '/' + shList + shCallback; //合併路徑
var dataRemote = []; //建立空的陣列

//讀入每一個資料表
$.each(shList, function(i, list){
	$.getJSON( shPath + shKey + '/' + list.listKey + shCallback)
	.done(function (data) {	 //如果成功
		list.dataName = []
		var entry = data.feed.entry //只取feed entry的部分
		var title = data.feed.title.$t //資料頁的標題
		dataRemote.push({ //存回陣列
			'title': title,
			'data': entry
		}); //送回dataset
		jsonDone(); //執行驗證
	})
	.fail(function(jqxhr, textStatus, error){
		console.log('GG,沒戲唱了'); //失敗
	});

})


//驗證資料跑完沒
jsonDone = function(){
	if (shList.length != dataRemote.length){
		console.log('快好了'); //驗證未完成
	} else if (shList.length == dataRemote.length){
		console.log('好了', dataRemote); //驗證成功
		dataset = dataRemote
		runChart(); //繪製圖表
	}
};


//真的開始畫圖了
runChart = function(){

	var margin = {top: 60, right: 40, bottom: 50, left: 60};
	var w = 560 ; // 寬
	var h = 300 ; // 高
	
	console.log(dataset[0].data.length)

	datatime = []; //將時間存成陣列,作為Xscale用處
	$.each(dataset[0].data,function(i,d){
		datatime.push(d.gsx$time.$t)
	});
	var xScale = d3.scale.linear().domain([0,dataset[0].data.length]).range([0,w])

	var yScale = d3.scale.linear().domain([.2, .85]).range([h, 0]);
	//Y的Scale就不另外寫,先直接設定值

	// 增加一個line function,用來把資料轉為x, y
	var line = d3.svg.line()
		.x(function(d,i) { 
			return xScale(i + 1); //利用尺度運算資料索引,傳回x的位置
		})
		.y(function(d) { 
			return yScale(d.gsx$percent.$t); //利用尺度運算資料的值,傳回y的位置
		});

	//增加一個SVG元素
	var svg = d3.select('.demo').append('svg')
		.attr('width', w + margin.left + margin.right) //將左右補滿
		.attr('height', h + margin.top + margin.bottom) //上下補滿
		.append('g') //增加一個群組g
		.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

	// 增加x軸線,tickSize是軸線的垂直高度,-h會往上拉高
	var xAxis = d3.svg.axis().scale(xScale).ticks(5).orient('bottom').tickSize(-h).tickSubdivide(true);
	// SVG加入x軸線
	svg.append('g')
		.attr('class', 'x axis')
		.attr('transform', 'translate(0,' + h + ')')
		.call(xAxis);


	// 建立y軸線,4個刻度,數字在左
	var yAxisLeft = d3.svg.axis().scale(yScale).ticks(4).orient('left');
	// SVG加入y軸線
	svg.append('g') 
		.attr('class', 'y axis')
		.attr('transform', 'translate(0,0)')
		.call(yAxisLeft);

	$.each(dataset,function(i,d){
		svg.append('path').attr('d', line(d.data))
			.style({
				'stroke': d3.hsl((120 + 90*i), .6, .6),//d3.hsl是d3提供的顏色function
				'stroke-width':1,
			});//將每個資料繪製成折線,並且套用不同的色彩

		svg.append('g').selectAll('circle').data(d.data).enter() //增加原點
		.append('circle')
		.attr({
			'cx': function(d, i){return xScale(i + 1) },
			'cy': function(d){return yScale(d.gsx$percent.$t)}, //x,y算法都類似line function
			'r':'2px',
			'class': 'dot' //這圓點主要是要給hover使用的
		})
		.style({
			'fill': d3.hsl((120 + 90*i), .6, .6)//套用相同色彩
		})

		svg.append('g').append('text') //補上左上示意文字
			.text(d.title) 
			.style({
				'fill': d3.hsl((120 + 90*i), .6, .6),
				'transform': 'translate(8px,'+ ((i * 15) + 12) //拉開間距
					+'px)',
				'font-size':'12px' 
			})
	});

	showTips = function(id){ //這部分是要作為Hover用的資料
		date = dataset[0].data[id].gsx$time.$t //時間取其中一份的即可
		var html = '' 
		$.each(dataset, function(i, dataset){ //將資料定義,並回傳
			title = dataset.title;
			percent = dataset.data[id].gsx$percent.$t;
			html = html + '<div><span>'+title+'</span>/<span> '+percent+'%</span></div>'
		});
		html = '<div>'+date+'</div>' + html; 
		return html; //回傳整理好的html
	};

	svg.selectAll('.dot').on('mouseover', function(d){ 
	//d3 function,類似jquery,直接可以控制class
		var xPos = parseFloat(d3.select(this).attr('cx')) + margin.left //截取點的位置
		var yPos = parseFloat(d3.select(this).attr('cy')) + margin.top
		//這一個目標(d),裡面也包含了資料,所以d.gsx$id可以抓到這個點所包含的資料
		var id = d.gsx$id.$t - 1 //抓取資料id

		d3.select('#tooltip') //將div抓來用
			.style({
				'left': xPos + 'px',
				'top': yPos + 'px'
			})
			.classed('hidden', false) //移除隱藏的class
			.html(showTips(id)) //將剛剛的showTips function帶入
	}).on('mouseout', function(d){ //如果移出的話
		d3.select('#tooltip').classed('hidden', true); //補回剛剛的Class
	});	
}

});

有注意到嗎?在互動的事件上是使用一般的div,所以SVG與HTML是可以混合使用的,不過也記得要補上html部分的程式碼。

path {
	stroke: DodgerBlue;
	stroke-width: 1;
	fill: none;
}
.axis {
	font-size: 11px;
	fill: gray;
}

.x.axis line {
  stroke: lightgrey;
}

.x.axis .minor {
  stroke-opacity: .5;
}

.x.axis path {
  stroke: #fafafa;
}

.y.axis line, .y.axis path {
  fill: none;
  stroke: lightgrey;
}
.x.axis text{
	display: none;
}
.demo{
	position: relative;
}
#tooltip{
	position: absolute;
	max-width: 220px;
	padding: 10px;
	background-color: #fff;
	border-radius: 2px;
	box-shadow: 0 2px 2px rgba(0,0,0, .16);
	pointer-events: none;
	transition: opacity .2s;
	opacity: 1;
	font-size: 11px;
}
#tooltip.hidden{
	opacity: 0;
}

上一篇
D3.js 究竟搭不搭捷運與死亡率有沒有關係?(2) - Google 試算表
下一篇
D3.js 圈圈小效果
系列文
SVG 與 D3.js30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言