D3.js之餅圖動畫
阿新 • • 發佈:2019-02-04
學習D3差不多一個月了。最近看了剛看了echart,發現他裡面的餅圖初始化的時候是從無到有的開啟一圈。所以想要模仿這個動畫效果。
如果想要學習D3.js餅圖佈局可以去看這個網站:http://www.ourd3js.com
開始的想法是這樣的:先給出餅圖從無到有展開花費的所有時間,再設計餅圖每個弧所佔的比例,乘以動畫總時間,就每個弧所要執行的時間。
在用一個delaytime變數儲存每個弧之前所有弧需要展開的時間總和。這是用來延遲這個弧動畫時間,從而每個弧的無縫連線動畫。
<html> <head> <meta charset="utf-8"> <title>餅狀圖</title> </head> <style> </style> <body> <script src="../js/d3.js" charset="utf-8"></script> <script> var width = 400; var height = 400; var dataset = [30, 10, 43, 55, 13]; var svg = d3.select("body") .append("svg") .attr("width", width) .attr("height", height); var pie = d3.layout.pie().sort(null); //定義餅圖的佈局 var piedata = pie(dataset); //將資料傳給pie,就可以得到繪圖的資料 var outerRadius = 150; //外半徑 var innerRadius = 0; //內半徑,為0則中間沒有空白 var sum=0; piedata.forEach(function(d,i){ d._endAngle=d.endAngle;//儲存這個值,後面動畫要用到。 d.endAngle=d.startAngle;//讓每個弧的弧度都是0 d.duration=2001*(d.data/d3.sum(dataset)); d.delaytime=sum; sum+=d.duration; }) var arc = d3.svg.arc() //弧生成器 .innerRadius(innerRadius) //設定內半徑 .outerRadius(outerRadius); //設定外半徑 var color = d3.scale.category10(); var arcs = svg.selectAll("g") //先新增五個分組元素,用來存放一段弧的相關元素 .data(piedata) .sort(d3.ascending) .enter() .append("g") .attr("transform", function(d,i){ return "translate(" + (width / 2) + "," + (width / 2) + ")"; }); arcs.append("path") //給每個分組元素g新增一個路徑 .attr("fill", function(d, i) { return color(i); }) .attr("d", function(d, i) { return arc(d); }) .transition() .duration(function(d,i){ return d.duration; }) .ease("linear") .delay(function(d,i){ console.log(d.delaytime) return d.delaytime; }) .attr("d", function(d, i) { d.endAngle=d._endAngle; // d.endAngle=d._endAngle; // arc.outerRadius(outerRadius+i*10);每個邊的長度不一樣。 // console.log(d); d此時是一個物件包括data、endAngle padAngle StartAngle value console.log(d); return arc(d); //通過之前定義的弧生成器來轉化資料 }) ; </script> </body> </html>
上面就是我實現的程式碼。但是非常醜陋,並不是那種平滑展開那種效果。
然後最近看了一個API。
transition.attrTween - 在兩個屬性值之間平滑地過渡。(本人英語不是太好,感謝前人翻譯的中文API)
<html> <head> <meta charset="utf-8"> <title>餅狀圖</title> </head> <style> </style> <body> <script src="../js/d3.js" charset="utf-8"></script> <script> var width = 400; var height = 400; var dataset = [30, 10, 43, 55, 13]; // dataset.sort(function(a,b){return b-a;}); var svg = d3.select("body") .append("svg") .attr("width", width) .attr("height", height); var pie = d3.layout.pie().sort(null); //定義餅圖的佈局 var piedata = pie(dataset); //將資料傳給pie,就可以得到繪圖的資料 var outerRadius = 150; //外半徑 var innerRadius = 0; //內半徑,為0則中間沒有空白 var sum=0; piedata.forEach(function(d,i){ d._endAngle=d.endAngle; d.endAngle=d.startAngle; d.duration=2000*(d.data/d3.sum(dataset));//動畫時長2秒,計算每一個弧形所用動畫時間 d.delaytime=sum; sum+=d.duration; }) var arc = d3.svg.arc() //弧生成器 .innerRadius(innerRadius) //設定內半徑 .outerRadius(outerRadius); //設定外半徑 var color = d3.scale.category10(); var arcs = svg.selectAll("g") //先新增五個分組元素,用來存放一段弧的相關元素 .data(piedata) .enter() .append("g") .attr("transform", function(d,i){ return "translate(" + (width / 2) + "," + (width / 2) + ")"; }); arcs.append("path") //給每個分組元素g新增一個路徑 .attr("fill", function(d, i) { return color(i); }) .attr("d", function(d, i) { console.log(d); return arc(d); }) .transition() .duration(function(d,i){ return d.duration; }) .ease("linear") .delay(function(d,i){ return d.delaytime; }) .attrTween("d", tweenArc(function(d, i) { return { startAngle: d.startAngle, endAngle: d._endAngle }; })) ; function tweenArc(b) { return function(a, i) { var d = b.call(this, a, i), i = d3.interpolate(a, d); //d儲存轉換之後的資訊 //插值模式,從d.endAnle=d.startAngle到d.endAngle=d._endAngle轉換 return function(t) { return arc(i(t)); }; }; } </script> </body> </html>
效果如下:
上面看起來的效果就差不多了。
然後填餅圖新增文字。因為文字是直接顯示上面的,即使弧沒有出來文字也在上,感覺不好看,可以跟上面一樣給text設定延遲出來的動畫。
arcs.append("text") .attr("transform", function(d) { //arc.centroid計算出每個弧的中心位置 return "translate(" + arc.centroid(d) + ")"; }) .attr("text-anchor", "middle") .text(function(d) { return d.data; })
d3.selectAll("path").attr("transform",function(d,i){
var midAngle=(d.startAngle+d.endAngle)/2;
return "translate("+(1*Math.sin(midAngle))+","+(-1*Math.cos(midAngle))+")";
})
也可以給新增餅圖新增滑鼠移入移出事件
這邊我實現的是往外移動一點點,也可以實現移入的時候讓它變大,echart的餅圖就是這樣的效果
arcs.select("path").on("mouseover",function(d,i){
d3.select(this).transition()
.duration(500)
.ease("linear")
.attr("transform", function(d, i) {
var midAngle = (d.startAngle + d.endAngle) / 2;
return "translate(" + (20 * Math.sin(midAngle)) + "," + (-20 * Math.cos(midAngle)) + ")";
})
})
.on("mouseout",function(d,i){
d3.select(this).transition()
.duration(500)
.ease("linear")
.attr("transform", function(d, i) {
var midAngle=(d.startAngle+d.endAngle)/2;
return "translate("+(1*Math.sin(midAngle))+","+(-1*Math.cos(midAngle))+")";
return "translate(0)";
})
})
還有給餅圖新增一些資訊,可以檢視這位大神的部落格:http://blog.csdn.net/lzhlzz/article/details/46508041
然後也可以將這些數字放在外面,用連線連線著,大概思路就是將現在文字水平向左或向右平移一段距離(根據弧中心在左邊右邊來判斷)
然後將兩點資訊儲存下來,新增一個polyline 然後傳給point就可以了