1. 程式人生 > >【D3.js資料視覺化系列教程】(二十二)--互動圖表之提示條

【D3.js資料視覺化系列教程】(二十二)--互動圖表之提示條

//鍵值對資料集 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"); });