1. 程式人生 > >D3.js之餅圖動畫2

D3.js之餅圖動畫2

上篇只是說了餅圖的從無到有的旋轉動畫,這次來說說怎麼給餅圖新增外接註釋加連線,以及資料更新的動畫。

雖然d3.js寫的麻煩,我看了幾天echart,發現只要套套模板更改一下引數就行,但是d3可塑性很高,完全由你想象,想怎麼畫就怎麼畫。

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>頭疼</title>
</head>
<style type="text/css">
    
    button{
        position: absolute;
        margin: 10px;
    }
</style>
<body>
    <button onclick="changeData()">更換資料</button>
    <script type="text/javascript" src='../js/d3.js'></script>
    <script type="text/javascript">
        var width = 500+100;
        var height = 500;
        
        var dataset=[["標籤1",30],["標籤2",20],["標籤3",43],["標籤4",55],["標籤5",13]];
        
        var outerRadius = 150; //外半徑
            var innerRadius = 0; //內半徑,為0則中間沒有空白
        var arc = d3.svg.arc() //弧生成器
                .innerRadius(innerRadius) //設定內半徑
                .outerRadius(outerRadius); //設定外半徑
        var color = d3.scale.category20();//構造20種顏色的序數比例尺,索引值可以是字串或數字
        var pie = d3.layout.pie()   //餅圖佈局
            .sort(null)             //不排序,不寫則會從大到小,順時針排序。
            .value(function(d){  return d[1]});     //設定value值為上面的2二維陣列中的數字
        var piedata=pie(dataset);
        var svg = d3.select("body")             //新增一個svg並且設定寬高
                .append("svg")
                .attr("width", width)
                .attr("height", height);

         var arcs=svg.selectAll(".arc")             
            .data(piedata) //返回是pie(data0)
            .enter().append("g")
            .attr("class", "arc")
            .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")   //將圓心平移到svg的中心
            .append("path")
            .attr("fill", function(d, i) {
                return color(i);            //根據下標填充顏色
            })
            .attr("d", function(d, i) {
                return arc(d);              ///呼叫上面的弧生成器
            });

         var text=svg.selectAll(".text")
            .data(piedata) //返回是pie(data0)
            .enter().append("g")
            .attr("class", "text")
            .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
            .append("text")
            .style('text-anchor', function(d, i) {
                //根據文字在是左邊還是右邊,在右邊文字是start,文字預設都是start。
                return (d.startAngle + d.endAngle)/2 < Math.PI ? 'start' : 'end';
            })
            .attr('transform', function(d, i) {
                var pos = arc.centroid(d);      //centroid(d)計算弧中心
                pos[0]=outerRadius*((d.startAngle+d.endAngle)/2<Math.PI?1.4:-1.4)
                pos[1]*=2.1;                    //將文字移動到外面去。
                return 'translate(' + pos + ')';
            })
            .attr("dy",".3em")              //將文字向下便宜.3em
            .text(function(d) {             //設定文字
                return d.data[0];   
            })

         var text2=svg.selectAll(".text2")
            .data(piedata) //返回是pie(data0)
            .enter().append("g")
            .attr("class", "text")
            .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
            .append("text")
            .style('text-anchor',"middle")
            .attr('transform', function(d, i) {
                var pos = arc.centroid(d);          //將數字放在圓弧中心
                return 'translate(' + pos + ')';
            })
            .text(function(d) {
                return d.data[1];
            })
             var line = svg.selectAll(".line")      //新增文字和弧之間的連線
                .data(piedata) //返回是pie(data0)
                .enter().append("g")
                .attr("class", "line")
                .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
                .append("polyline")
                .attr('points', function(d, i) {
                    var pos1= arc.centroid(d),pos2= arc.centroid(d),pos3= arc.centroid(d);
                    pos1[0]*=2,pos1[1]*=2;
                    pos2[0]*=2.1,pos2[1]*=2.1
                    pos3[0]=outerRadius*((d.startAngle+d.endAngle)/2<Math.PI?1.4:-1.4)
                    pos3[1]*=2.1;
                    //pos1表示圓弧的中心邊緣位置,pos2是網上稍微去了一下,pos3就是將pos2平移後得到的位置
                    //三點連結在一起就成了線段。
                    return [pos1,pos2,pos3];
                })
                .style('fill', 'none')
                .style('stroke',function(d,i){
                    return color(i);
                })
                .style('stroke-width', "3px")
                .style('stroke-dasharray',"5px")

             var label=svg.selectAll('.label')      //新增右上角的標籤
                    .data(piedata)
                    .enter()
                    .append('g')
                    .attr("transform","translate("+(width-50)+","+10+")")
                    ;   
                label.append('rect')        //標籤中的矩形
                    .style('fill',function(d,i){
                        return color(i);
                    })
                    .attr('x',function(d,i){
                        return 0;
                    })
                    .attr("y",function(d,i){
                        return 10+i*30;
                    })
                    .attr('rx','5')     //rx=ry 會出現圓角
                    .attr('ry','5')
                    .attr('width',50)
                    .attr('height',20)
                    ;
                label.append('text')            //標籤中的文字
                    .attr('x',function(d,i){
                        return 25;              //因為rect寬度是50,所以把文字偏移25,在後面再將文字設定居中
                    })
                    .attr("y",function(d,i){        
                        return 15+10+i*30;
                    })
                    .text(function(d){
                        return d.data[0];
                    })
                    .style({
                        "font-size":"10px",
                        "text-anchor":"middle",
                        'fill':"white",
                        "font-weight":600
                    })
            function changeData(){
                random()
                var pie2=pie(dataset);
                piedata.forEach(function(d,i){
                    d.laststartAngle=d.startAngle;
                    d.lastendAngle=d.endAngle;
                    d.startAngle=pie2[i].startAngle;
                    d.endAngle=pie2[i].endAngle;
                })
                arcs.data(piedata)
                    .transition().duration(800)
                    .attrTween("d", tweenArc(function(d, i) {
                    return {
                        startAngle: d.laststartAngle,
                        endAngle: d.lastendAngle,
                    };
                }))
                text.data(piedata)
                    .transition().duration(800)
                    .style('text-anchor', function(d, i) {
                        //圓的中心位置在哪裡,在右邊文字是start
                        return (d.startAngle + d.endAngle)/2 < Math.PI ? 'start' : 'end';
                    })
                    .attr('transform', function(d, i) {
                        console.log(d);
                        var pos = arc.centroid(d);
                        pos[0] = outerRadius * ((d.startAngle + d.endAngle)/2 < Math.PI ? 1.4 : -1.4)
                        pos[1] *= 2;
                        return 'translate(' + pos + ')';
                    });
                text2.data(piedata)
                    .transition().duration(800)
                    .attr('transform', function(d, i) {
                        var pos = arc.centroid(d);
                        return 'translate(' + pos + ')';
                    }).text(function(d) {
                        return d.data[1];
                    });
                line.data(piedata)
                    .transition().duration(800)
                    .attr('points', function(d, i) {
                    var pos1= arc.centroid(d),pos2= arc.centroid(d),pos3= arc.centroid(d);
                    pos1[0]*=2,pos1[1]*=2;
                    pos2[0]*=2.1,pos2[1]*=2.1
                    pos3[0]=outerRadius*((d.startAngle+d.endAngle)/2<Math.PI?1.4:-1.4)
                    pos3[1]*=2.1;
                    console.log(pos1);
                    return [pos1,pos2,pos3];
                })
            }
             function random(){
                    var n=5;
                    while(n--){dataset[n][1]=Math.floor(Math.random()*40+10)}
                }
             function tweenArc(b) {
                return function(a, i) {
                    var d = b.call(this, a, i),
                        i = d3.interpolate(d, a);
                    return function(t) {
                        return arc(i(t));
                    };
                };
             }
    </script>
</body>
</html>

上面只是圖片,大家可以貼上程式碼來執行一下。

可以點選上面的更新資料來實現餅圖動畫。d3.js還是很神奇的,有好多想法,尤其是看了官網好多非常酷炫的例子。