【D3.js資料視覺化系列教程】(二十二)--互動圖表之提示條
阿新 • • 發佈:2019-01-26
//鍵值對資料集
var dataset = [
{key:0,value:5},
{key:1,value:10},
{key:2,value:13},
{key:3,value:19},
{key:4,value:21},
{key:5,value:25},
{key:6,value:22},
{key:7,value:18},
{key:8,value:15},
{key:9,value:13},
{key:10,value:11},
{key:11,value:12},
{key:12,value:15},
{key:13,value:20},
{key:14,value:18},
{key:15 ,value:17},
{key:16,value:16},
{key:17,value:18},
{key:18,value:23},
{key:19,value:25}];
//設定SVG的高寬
var w=600;
var h=250;
var barPadding = 1;
//定義序數比例尺
var xScale=d3.scale.ordinal()//序數比例尺
.domain(d3.range(dataset.length))
.rangeRoundBands([0,w],0.05);
// 更新資料引用,包含下面所有關於要使用到d.value的地方
var yScale=d3.scale.linear()//y仍然是線性比例尺
.domain([0,d3.max(dataset,function(d){
return d.value;
})])
.range([0,h]);
// 定義鍵函式(簡潔),以備資料繫結到元素的時候使用
//把所有.data(dataset)改成.data(dataset,key)
var key=function(d){
return d.key;
};
//值函式
var value=function(d){
return d.value;
};
//條排序函式
var sortOrders=false ;
var sortBars=function(){
sortOrders=!sortOrders;//(3)每點選一次排序方向改變
svg.selectAll("rect")
.sort(function(a,b){
if(sortOrders){
//對資料集升序排序
return d3.ascending(a.value,b.value);//這個地方注意是鍵值對所以要加上值的引用b.value
}else{
//對資料集降序排序
return d3.descending(a.value,b.value);
}
})
.transition()
.duration(1000)
.attr("x",function(d,i){//對排序之後的橫座標重排
return xScale(i);
});
};
//建立SVG元素
var svg = d3.select("body")//選中DOM中的目標元素
.append("svg")//為目標元素附加上一個svg子元素
.attr("width", w)//設定這個svg的寬
.attr("height", h);//設定這個svg的高
//為SVG新增條形
svg.selectAll("rect")//選中空元素,表示即將建立這樣的元素
.data(dataset,key)//對此後的方法都執行dataset.length遍
.enter()//資料元素值比前面選中的DOM元素多就建立一個新的DOM元素
.append("rect")//取得enter的佔位元素,並把rect追加到對應的DOM中
.attr("x", function(d, i) {//設定橫座標,從0開始每次右移元素寬那麼長(w / dataset.length)
//return i * (w / dataset.length);
return xScale(i);//這裡使用序數比例尺,自己去找剛才劃分好的檔位
})
.attr("y", function(d) {//設定縱座標,縱座標正方向是從上往下的,所以條有多長就要設定起點是相對於h再向上移動條長
return h - yScale(d.value);
})
//.attr("width", w / dataset.length - barPadding)//設定元素寬,留出間隙寬barPadding。
.attr("width", xScale.rangeBand())//這裡xScale比例尺已經設定間距了所以直接用
.attr("height", function(d) {
return yScale(d.value);
})
.attr("fill", function(d) {//設定RGB顏色與數值的關係
return "rgb(0, 0, " + (d.value * 10) + ")";
})
//點選排序
.on("click",function(){
sortBars();
})
//(3)更新提示條的值和位置
.on("mouseover",function(d){
//取得提示顯示的位置
var xPosition=parseFloat(d3.select(this).attr("x"))+xScale.rangeBand()/2;
var yPosition=parseFloat(d3.select(this).attr("y"))/2+h/2;
d3.select("#tooltip")
.style("left",xPosition+"px")
.style("top",yPosition+"px")
.select("#value")
.text(d.value);
})
//移除提示條
.on("mouseout",function(){
//(4)新增隱藏類
d3.select("#tooltip").classed("hidden",true);//ID 選擇的語法:"#tooltip"
})
;
//為條加上數值
svg.selectAll("text")
.data(dataset,key)
.enter()
.append("text")
.text(function(d) {
return d.value;
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return xScale(i)+xScale.rangeBand()/2;
})
.attr("y", function(d) {
return h - yScale(d.value) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size",function(d) {
return xScale.rangeBand()/2;
})
.attr("fill", "white");
//刪除一條、新增一條
d3.select("p")
.on("click",function(){
//根據ID確定點選的是哪個標籤
var paragraphID=d3.select(this).attr("id") ;
console.log(paragraphID);
//新增刪除組合起來
if(paragraphID=="add"){
//資料集最後新增數值
var maxValue=75;
var newNumber =Math.floor(Math.random()*maxValue);//0-24的整數
//根據最後一個key新增一個值
var lastKeyValue=dataset[dataset.length-1].key;
dataset.push({
key:lastKeyValue+1,
value:newNumber
});
//更新X軸比例尺
xScale.domain(d3.range(dataset.length));
//選擇所有條
var bars=svg.selectAll("rect")
.data(dataset,key); //繫結資料到元素集,返回更新的元素集
var texts=svg.selectAll("text")
.data(dataset,key);
//新增條形元素到最右邊
bars.enter()
.append("rect")
.attr("x",w);//在SVG最右邊,不可見
//
texts.enter()
.append("text");
//更新新矩形到可見範圍內
//並在這個時候根據資料集為每個條設定對應的屬性
bars.transition()
.duration(500)
.attr("x", function(d, i) {
return xScale(i) ;
})//每個X對應到它相應的檔位上
.attr("y", function(d) {
return h - yScale(d.value) ;
})
.attr("width", xScale.rangeBand())//這裡xScale比例尺已經設定間距了所以直接用
.attr("height", function(d) {
return yScale(d.value);
})
.attr("fill", function(d) {//設定RGB顏色與數值的關係
return "rgb(0, 0, " + (d.value * 10) + ")";
});
//
texts.transition()
.duration(500)
.text(function(d) {
return d.value;
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return xScale(i)+xScale.rangeBand()/2;
})
.attr("y", function(d) {
return h - yScale(d.value) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", "12px")
.attr("fill", "red");
} else if(paragraphID=="remove"){
//刪除的操作
//選擇所有條
dataset.shift();
//更新X軸比例尺
xScale.domain(d3.range(dataset.length));
var bars=svg.selectAll("rect")
.data(dataset,key);
//從左側退出
bars.exit()
.transition()
.duration(500)
.attr("x", -xScale.rangeBand())//w-xScale.rangeBand()間隙寬其實其他負數也行
.remove();
}
});
// 更新條形數長短的程式碼,需要一個button標籤配合
//特別注意:這裡選中的元素必須在d3選擇器之前,或許要先載入完了元素才能被選中
d3.select("button")
.on("click",function(){
// 新資料集,隨機陣列
var numValues=dataset.length;
dataset=[];
var maxValue=75;
var newNumber;
for(var i=0;i<numValues;i++){
newNumber=Math.floor(Math.random()*maxValue);//0-24的整數
//根據i新增一個值
dataset.push({
key:i,
value:newNumber
});
}
// 更新比例尺,免使縱座標超出範圍
yScale.domain([0,d3.max(dataset,value)]);//只要更新定義域就行了,對映到的值域不變
//更新所有的矩形
svg.selectAll("rect")
.data(dataset,key)
.transition()// 加上過渡動畫
.delay(function(d,i){
return i/dataset.length*1000;
})//指定過度什麼時間開始,可以用函式控制每一條的動畫時間,這樣就可得到鋼琴版的效果
.duration(2000)// 加上動畫的持續時間,以毫秒計算
.ease("linear")// 緩動函式:有circle(加速)elastic(伸縮),linear(勻速),bounce(彈跳)
.attr("y",function(d){
return h-yScale(d.value);
})
.attr("height",function(d){
return yScale(d.value);
});
// 更新條上的數值
svg.selectAll("text")
.data(dataset,key)
.text(function(d) {
return d.value;
})
.attr("text-anchor", "middle")
.attr("x", function(d, i) {
return xScale(i)+xScale.rangeBand()/2;
})
.attr("y", function(d) {
return h - yScale(d.value) + 14;
})
.attr("font-family", "sans-serif")
.attr("font-size", "12px")
.attr("fill", "red");
});