簡體   English   中英

帶有角度/水平標簽的d3.js餅圖

[英]d3.js pie chart with angled/horizontal labels

我正在做一個餅圖模擬。 我需要嘗試匹配設計,使標簽擠出,並在切片刻度線上附加水平線。 這可能嗎? 在片段上形成黑點將是一個獎勵。

餅圖與水平標簽

http://jsfiddle.net/BxLHd/15/

這是刻度線的代碼。 是否會創建另一組相交的線?

                        //draw tick marks
                        var label_group = d3.select('#'+pieId+' .label_group');
                        lines = label_group.selectAll("line").data(filteredData);
                        lines.enter().append("svg:line")
                                .attr("x1", 0)
                                .attr("x2", 0)
                                .attr("y1", function(d){
                                    if(d.value > threshold){
                                        return -that.r-3;
                                    }else{
                                        return -that.r;
                                    }
                                })
                                .attr("y2", function(d){
                                    if(d.value > threshold){
                                        return -that.r-8;
                                    }
                                    else{                                   
                                        return -that.r;
                                    }
                                })
                                .attr("stroke", "gray")
                                .attr("transform", function(d) {
                                        return "rotate(" + (d.startAngle+d.endAngle)/2 * (180/Math.PI) + ")";
                                });

                        lines.transition()
                                .duration(this.tweenDuration)
                                .attr("transform", function(d) {
                                        return "rotate(" + (d.startAngle+d.endAngle)/2 * (180/Math.PI) + ")";
                                });

                        lines.exit().remove();

這是一個概念證明(使用一個不同於你的例子作為基礎,因為你的代碼中有很多代碼)。 這是基本方法:

  • 對於每個標簽,計算其下方的行的開始和結束。 這是通過繪制標簽並獲取其邊界框來完成的。
  • 這在指針路徑上給出兩個點,第三個是相應段的中心。 這是在計算標簽位置時計算的。
  • 這三點成為數據的一部分。 現在使用之前計算的三個點為每個數據元素繪制path
  • 在每個路徑的末尾添加一個SVG標記。

這是一步一步完成的代碼。

.attr("x", function(d) {
    var a = d.startAngle + (d.endAngle - d.startAngle)/2 - Math.PI/2;
    d.cx = Math.cos(a) * (radius - 75);
    return d.x = Math.cos(a) * (radius - 20);
})
.attr("y", function(d) {
    var a = d.startAngle + (d.endAngle - d.startAngle)/2 - Math.PI/2;
    d.cy = Math.sin(a) * (radius - 75);
    return d.y = Math.sin(a) * (radius - 20);
})

這是計算段外標簽的x和y位置。 我們還計算指針路徑的最終點的位置,在段的中心。 也就是說,在開始和結束角度之間以及內半徑和外半徑之間的中間。 這將添加到數據中。

.text(function(d) { return d.value; })
.each(function(d) {
    var bbox = this.getBBox();
    d.sx = d.x - bbox.width/2 - 2;
    d.ox = d.x + bbox.width/2 + 2;
    d.sy = d.oy = d.y + 5;
});

添加文本標簽后(在這種情況下,只是值),我們得到每個邊界框並計算路徑的剩余兩個點,就在左邊的文本下方和右下方的文本下方。

svg.selectAll("path.pointer").data(piedata).enter()
  .append("path")
  .attr("class", "pointer")
  .style("fill", "none")
  .style("stroke", "black")
  .attr("marker-end", "url(#circ)")
  .attr("d", function(d) {
    if(d.cx > d.ox) {
        return "M" + d.sx + "," + d.sy + "L" + d.ox + "," + d.oy + " " + d.cx + "," + d.cy;
    } else {
        return "M" + d.ox + "," + d.oy + "L" + d.sx + "," + d.sy + " " + d.cx + "," + d.cy;
    }
  });

現在我們可以實際添加路徑。 它們是之前計算的三個點的直接連接,最后添加了一個標記。 唯一需要注意的是,根據標簽是在圖表的左側還是右側,路徑需要從標簽的左下角或右下角開始。 這是if語句。

在這里完成演示。

以下是應該允許餅圖的多個實例的插件代碼 - 以及能夠使用一組新數據更新每個餅圖。

我願意采用增強代碼的方法。 我覺得它看起來仍然有點笨重 - 尤其是我在更新時重置選擇器的方式。 有什么建議可以簡化這個嗎?

http://jsfiddle.net/Qh9X5/1318/

$(document).ready(function() {


            (function( $ ){
                var methods = {
                    el: "",
                    init : function(options) {
                        var clone = jQuery.extend(true, {}, options["data"]);

                        methods.el = this;          
                        methods.setup(clone);
                    },
                    setup: function(dataset){               

                        this.width = 300;
                        this.height = 300;
                        this.radius = Math.min(this.width, this.height) / 2;

                        this.color = d3.scale.category20();

                        this.pie = d3.layout.pie()
                            .sort(null);

                        this.arc = d3.svg.arc()
                            .innerRadius(this.radius - 100)
                            .outerRadius(this.radius - 50);

                        this.svg = d3.select(methods.el["selector"]).append("svg")
                            .attr("width", this.width)
                            .attr("height", this.height)
                            .append("g")
                                .attr("class", "piechart")
                                .attr("transform", "translate(" + this.width / 2 + "," + this.height / 2 + ")");                

                        //this.update(dataset[0].segments); 
                    },
                    oldPieData: "",
                    pieTween: function(d, i){
                        var that = this;

                        var theOldDataInPie = methods.oldPieData;
                        // Interpolate the arcs in data space

                        var s0;
                        var e0;

                        if(theOldDataInPie[i]){
                                s0 = theOldDataInPie[i].startAngle;
                                e0 = theOldDataInPie[i].endAngle;
                        } else if (!(theOldDataInPie[i]) && theOldDataInPie[i-1]) {
                                s0 = theOldDataInPie[i-1].endAngle;
                                e0 = theOldDataInPie[i-1].endAngle;
                        } else if(!(theOldDataInPie[i-1]) && theOldDataInPie.length > 0){
                                s0 = theOldDataInPie[theOldDataInPie.length-1].endAngle;
                                e0 = theOldDataInPie[theOldDataInPie.length-1].endAngle;
                        } else {
                                s0 = 0;
                                e0 = 0;
                        }

                        var i = d3.interpolate({startAngle: s0, endAngle: e0}, {startAngle: d.startAngle, endAngle: d.endAngle});

                        return function(t) {
                                var b = i(t);
                                return methods.arc(b);
                        };
                    },
                    removePieTween: function(d, i) {                
                        var that = this;
                        s0 = 2 * Math.PI;
                        e0 = 2 * Math.PI;
                        var i = d3.interpolate({startAngle: d.startAngle, endAngle: d.endAngle}, {startAngle: s0, endAngle: e0});

                        return function(t) {
                                var b = i(t);
                                return methods.arc(b);
                        };
                    },
                    update: function(dataSet){
                        var that = this;

                        methods.el = this;
                        methods.svg = d3.select(methods.el["selector"] + " .piechart");

                        this.piedata = methods.pie(dataSet);

                        //__slices
                        this.path = methods.svg.selectAll("path.pie")
                            .data(this.piedata);

                        this.path.enter().append("path")
                            .attr("class", "pie")
                            .attr("fill", function(d, i) {
                                return methods.color(i); 
                            })
                            .transition()
                                .duration(300)
                                .attrTween("d", methods.pieTween);

                        this.path
                                .transition()
                                .duration(300)
                                .attrTween("d", methods.pieTween);

                        this.path.exit()
                                .transition()
                                .duration(300)
                                .attrTween("d", methods.removePieTween)
                                .remove();    
                        //__slices


                        //__labels  
                        var labels = methods.svg.selectAll("text")
                            .data(this.piedata);

                        labels.enter()
                            .append("text")
                            .attr("text-anchor", "middle")


                        labels
                            .attr("x", function(d) {
                                var a = d.startAngle + (d.endAngle - d.startAngle)/2 - Math.PI/2;
                                d.cx = Math.cos(a) * (methods.radius - 75);
                                return d.x = Math.cos(a) * (methods.radius - 20);
                            })
                            .attr("y", function(d) {
                                var a = d.startAngle + (d.endAngle - d.startAngle)/2 - Math.PI/2;
                                d.cy = Math.sin(a) * (methods.radius - 75);
                                return d.y = Math.sin(a) * (methods.radius - 20);
                            })
                            .text(function(d) { 
                                return d.value; 
                            })
                            .each(function(d) {
                                var bbox = this.getBBox();
                                d.sx = d.x - bbox.width/2 - 2;
                                d.ox = d.x + bbox.width/2 + 2;
                                d.sy = d.oy = d.y + 5;
                            })
                            .transition()
                                .duration(300)

                        labels
                            .transition()
                            .duration(300)      

                        labels.exit()
                            .transition()
                            .duration(300)
                        //__labels


                        //__pointers
                        methods.svg.append("defs").append("marker")
                            .attr("id", "circ")
                            .attr("markerWidth", 6)
                            .attr("markerHeight", 6)
                            .attr("refX", 3)
                            .attr("refY", 3)
                            .append("circle")
                            .attr("cx", 3)
                            .attr("cy", 3)
                            .attr("r", 3);

                        var pointers = methods.svg.selectAll("path.pointer")
                            .data(this.piedata);

                        pointers.enter()
                            .append("path")
                            .attr("class", "pointer")
                            .style("fill", "none")
                            .style("stroke", "black")
                            .attr("marker-end", "url(#circ)");

                        pointers
                            .attr("d", function(d) {
                                if(d.cx > d.ox) {
                                    return "M" + d.sx + "," + d.sy + "L" + d.ox + "," + d.oy + " " + d.cx + "," + d.cy;
                                } else {
                                    return "M" + d.ox + "," + d.oy + "L" + d.sx + "," + d.sy + " " + d.cx + "," + d.cy;
                                }
                            })
                            .transition()
                                .duration(300)

                        pointers
                            .transition()
                            .duration(300)      

                        pointers.exit()
                            .transition()
                            .duration(300)

                        //__pointers

                        this.oldPieData = this.piedata;

                    }
                };

                $.fn.piechart = function(methodOrOptions) {
                    if ( methods[methodOrOptions] ) {
                        return methods[ methodOrOptions ].apply( this, Array.prototype.slice.call( arguments, 1 ));
                    } else if ( typeof methodOrOptions === 'object' || ! methodOrOptions ) {
                        // Default to "init"
                        return methods.init.apply( this, arguments );
                    } else {
                        $.error( 'Method ' +  methodOrOptions + ' does not exist' );
                    }    
                };

            })(jQuery);



            var dataCharts = [
                {
                    "data": [
                        {
                            "segments": [
                                53245, 28479, 19697, 24037, 40245                           
                            ]
                        }
                    ]
                },
                {
                    "data": [
                        {
                            "segments": [
                                855, 79, 97, 237, 245                   
                            ]
                        }
                    ]
                },
                {
                    "data": [
                        {
                            "segments": [
                                22, 79, 97, 12, 245                 
                            ]
                        }
                    ]
                },
                {
                    "data": [
                        {
                            "segments": [
                                122, 279, 197, 312, 545                 
                            ]
                        }
                    ]
                }            
            ];

            var clone = jQuery.extend(true, {}, dataCharts);

                //__invoke concentric
                $('[data-role="piechart"]').each(function(index) {
                    var selector = "piechart"+index;

                    $(this).attr("id", selector);

                    var options = {
                        data: clone[0].data
                    }

                    $("#"+selector).piechart(options);
                    $("#"+selector).piechart('update', clone[0].data[0].segments);
                });


            $(".testers a").on( "click", function(e) {
                e.preventDefault();

                var clone = jQuery.extend(true, {}, dataCharts);

                var min = 0;
                var max = 3;

                //__invoke pie chart
                $('[data-role="piechart"]').each(function(index) {
                    pos = Math.floor(Math.random() * (max - min + 1)) + min;
                    $("#"+$(this).attr("id")).piechart('update', clone[pos].data[0].segments);
                });

            }); 

});

總結一下,我已經在jquery插件中包含了最新的代碼。 現在可以使用這些標簽開發多個餅圖。

最新代碼 - ** http://jsfiddle.net/Qh9X5/1336/ - 在退出時正確刪除標簽。

在此輸入圖像描述

$(document).ready(function() {


            (function( $ ){
                var methods = {
                    el: "",
                    init : function(options) {
                        var clone = jQuery.extend(true, {}, options["data"]);

                        methods.el = this;          
                        methods.setup(clone, options["width"], options["height"], options["r"], options["ir"]);
                    },
                    getArc: function(radius, innerradius){
                        var arc = d3.svg.arc()
                            .innerRadius(innerradius)
                            .outerRadius(radius);

                        return arc;
                    },
                    setup: function(dataset, w, h, r, ir){

                        var padding = 80;

                        this.width = w;
                        this.height = h;
                        this.radius = r
                        this.innerradius = ir;

                        this.color = d3.scale.category20();

                        this.pie = d3.layout.pie()
                            .sort(null)
                            .value(function(d) { return d.total; });

                        this.arc = this.getArc(this.radius, this.innerradius);

                        this.svg = d3.select(methods.el["selector"]).append("svg")
                            .attr("width", this.width + padding)
                            .attr("height", this.height + padding)
                            .append("g")
                                .attr("class", "piechart")
                                .attr("transform", "translate(" + ((this.width/2) + (padding/2)) + "," + ((this.height/2) + (padding/2)) + ")");                

                        this.segments = this.svg.append("g")
                                .attr("class", "segments");

                        this.labels = this.svg.append("g")
                                .attr("class", "labels");

                        this.pointers = this.svg.append("g")
                                .attr("class", "pointers");


                    },
                    oldPieData: "",
                    pieTween: function(r, ir, d, i){
                        var that = this;

                        var theOldDataInPie = methods.oldPieData;
                        // Interpolate the arcs in data space

                        var s0;
                        var e0;

                        if(theOldDataInPie[i]){
                                s0 = theOldDataInPie[i].startAngle;
                                e0 = theOldDataInPie[i].endAngle;
                        } else if (!(theOldDataInPie[i]) && theOldDataInPie[i-1]) {
                                s0 = theOldDataInPie[i-1].endAngle;
                                e0 = theOldDataInPie[i-1].endAngle;
                        } else if(!(theOldDataInPie[i-1]) && theOldDataInPie.length > 0){
                                s0 = theOldDataInPie[theOldDataInPie.length-1].endAngle;
                                e0 = theOldDataInPie[theOldDataInPie.length-1].endAngle;
                        } else {
                                s0 = 0;
                                e0 = 0;
                        }

                        var i = d3.interpolate({startAngle: s0, endAngle: e0}, {startAngle: d.startAngle, endAngle: d.endAngle});

                        return function(t) {
                                var b = i(t);
                                return methods.getArc(r, ir)(b);
                        };
                    },
                    removePieTween: function(r, ir, d, i) {             
                        var that = this;
                        s0 = 2 * Math.PI;
                        e0 = 2 * Math.PI;
                        var i = d3.interpolate({startAngle: d.startAngle, endAngle: d.endAngle}, {startAngle: s0, endAngle: e0});

                        return function(t) {
                                var b = i(t);
                                return methods.getArc(r, ir)(b);
                        };
                    },
                    update: function(dataSet){
                        var that = this;

                        methods.el = this;
                        var r = $(methods.el["selector"]).data("r");
                        var ir = $(methods.el["selector"]).data("ir");

                        methods.svg = d3.select(methods.el["selector"] + " .piechart");

                        methods.segments = d3.select(methods.el["selector"] + " .segments");
                        methods.labels = d3.select(methods.el["selector"] + " .labels");
                        methods.pointers = d3.select(methods.el["selector"] + " .pointers");

                        dataSet.forEach(function(d) {
                            d.total = +d.value;
                        });                     

                        this.piedata = methods.pie(dataSet);

                        //__slices
                        this.path = methods.segments.selectAll("path.pie")
                            .data(this.piedata);

                        this.path.enter().append("path")
                            .attr("class", "pie")
                            .attr("fill", function(d, i) {
                                return methods.color(i); 
                            })
                            .transition()
                                .duration(300)
                                .attrTween("d", function(d, i) {
                                    return methods.pieTween(r, ir, d, i); 
                                });

                        this.path
                                .transition()
                                .duration(300)
                                .attrTween("d", function(d, i) {
                                    return methods.pieTween(r, ir, d, i); 
                                });

                        this.path.exit()
                                .transition()
                                .duration(300)
                                .attrTween("d", function(d, i) {
                                    return methods.removePieTween(r, ir, d, i); 
                                })
                                .remove();    
                        //__slices


                        //__labels  
                        var labels = methods.labels.selectAll("text")
                            .data(this.piedata);

                        labels.enter()
                            .append("text")
                            .attr("text-anchor", "middle")


                        labels
                            .attr("x", function(d) {
                                var a = d.startAngle + (d.endAngle - d.startAngle)/2 - Math.PI/2;
                                d.cx = Math.cos(a) * (ir+((r-ir)/2));
                                return d.x = Math.cos(a) * (r + 20);
                            })
                            .attr("y", function(d) {
                                var a = d.startAngle + (d.endAngle - d.startAngle)/2 - Math.PI/2;
                                d.cy = Math.sin(a) * (ir+((r-ir)/2));
                                return d.y = Math.sin(a) * (r + 20);
                            })
                            .text(function(d) {
                                return d.data.label; 
                            })
                            .each(function(d) {
                                var bbox = this.getBBox();
                                d.sx = d.x - bbox.width/2 - 2;
                                d.ox = d.x + bbox.width/2 + 2;
                                d.sy = d.oy = d.y + 5;
                            })
                            .transition()
                                .duration(300)

                        labels
                            .transition()
                            .duration(300)      

                        labels.exit()
                            .transition()
                            .duration(300)
                        //__labels


                        //__pointers
                        methods.pointers.append("defs").append("marker")
                            .attr("id", "circ")
                            .attr("markerWidth", 6)
                            .attr("markerHeight", 6)
                            .attr("refX", 3)
                            .attr("refY", 3)
                            .append("circle")
                            .attr("cx", 3)
                            .attr("cy", 3)
                            .attr("r", 3);

                        var pointers = methods.pointers.selectAll("path.pointer")
                            .data(this.piedata);

                        pointers.enter()
                            .append("path")
                            .attr("class", "pointer")
                            .style("fill", "none")
                            .style("stroke", "black")
                            .attr("marker-end", "url(#circ)");

                        pointers
                            .attr("d", function(d) {
                                if(d.cx > d.ox) {
                                    return "M" + d.sx + "," + d.sy + "L" + d.ox + "," + d.oy + " " + d.cx + "," + d.cy;
                                } else {
                                    return "M" + d.ox + "," + d.oy + "L" + d.sx + "," + d.sy + " " + d.cx + "," + d.cy;
                                }
                            })
                            .transition()
                                .duration(300)

                        pointers
                            .transition()
                            .duration(300)      

                        pointers.exit()
                            .transition()
                            .duration(300)

                        //__pointers

                        this.oldPieData = this.piedata;

                    }
                };

                $.fn.piechart = function(methodOrOptions) {
                    if ( methods[methodOrOptions] ) {
                        return methods[ methodOrOptions ].apply( this, Array.prototype.slice.call( arguments, 1 ));
                    } else if ( typeof methodOrOptions === 'object' || ! methodOrOptions ) {
                        // Default to "init"
                        return methods.init.apply( this, arguments );
                    } else {
                        $.error( 'Method ' +  methodOrOptions + ' does not exist' );
                    }    
                };

            })(jQuery);



            var dataCharts = [
                {
                    "data": [
                        {
                            "segments": [
                                {
                                    "label": "apple",
                                    "value": 53245
                                },
                                {
                                    "label": "cherry",
                                    "value": 145
                                },
                                {
                                    "label": "pear",
                                    "value": 2245
                                },
                                {
                                    "label": "bananana",
                                    "value": 15325
                                }                           
                            ]
                        }
                    ]
                },
                {
                    "data": [
                        {
                            "segments": [
                                {
                                    "label": "milk",
                                    "value": 532
                                },
                                {
                                    "label": "cheese",
                                    "value": 145
                                },
                                {
                                    "label": "grapes",
                                    "value": 22
                                }
                            ]
                        }
                    ]
                },
                {
                    "data": [
                        {
                            "segments": [
                                {
                                    "label": "pineapple",
                                    "value": 1532
                                },
                                {
                                    "label": "orange",
                                    "value": 1435
                                },
                                {
                                    "label": "grapes",
                                    "value": 22
                                }               
                            ]
                        }
                    ]
                },
                {
                    "data": [
                        {
                            "segments": [
                                {
                                    "label": "lemons",
                                    "value": 133
                                },
                                {
                                    "label": "mango",
                                    "value": 435
                                },
                                {
                                    "label": "melon",
                                    "value": 2122
                                }               
                            ]
                        }
                    ]
                }            
            ];

            var clone = jQuery.extend(true, {}, dataCharts);

                //__invoke concentric
                $('[data-role="piechart"]').each(function(index) {
                    var selector = "piechart"+index;

                    $(this).attr("id", selector);

                    var options = {
                        data: clone[0].data,
                        width: $(this).data("width"),
                        height: $(this).data("height"),
                        r: $(this).data("r"),
                        ir: $(this).data("ir")
                    }

                    $("#"+selector).piechart(options);
                    $("#"+selector).piechart('update', clone[0].data[0].segments);
                });


            $(".testers a").on( "click", function(e) {
                e.preventDefault();

                var clone = jQuery.extend(true, {}, dataCharts);

                var min = 0;
                var max = 3;

                //__invoke pie chart
                $('[data-role="piechart"]').each(function(index) {
                    pos = Math.floor(Math.random() * (max - min + 1)) + min;
                    $("#"+$(this).attr("id")).piechart('update', clone[pos].data[0].segments);
                });

            }); 

});

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM