d3.js 为饼图传播标签 [英] d3.js spreading labels for pie charts

查看:25
本文介绍了d3.js 为饼图传播标签的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 d3.js - 我这里有一个饼图.但问题是当切片很小时 - 标签重叠.展开标签的最佳方式是什么.

I'm using d3.js - I have a pie chart here. The problem though is when the slices are small - the labels overlap. What is the best way of spreading out the labels.

http://jsfiddle.net/BxLHd/16/

这是标签的代码.我很好奇 - 是否可以用 d3 模拟 3d 饼图?

Here is the code for the labels. I am curious - is it possible to mock a 3d pie chart with d3?

                        //draw labels                       
                        valueLabels = label_group.selectAll("text.value").data(filteredData)
                        valueLabels.enter().append("svg:text")
                                .attr("class", "value")
                                .attr("transform", function(d) {
                                    return "translate(" + Math.cos(((d.startAngle+d.endAngle - Math.PI)/2)) * (that.r + that.textOffset) + "," + Math.sin((d.startAngle+d.endAngle - Math.PI)/2) * (that.r + that.textOffset) + ")";
                                })
                                .attr("dy", function(d){
                                        if ((d.startAngle+d.endAngle)/2 > Math.PI/2 && (d.startAngle+d.endAngle)/2 < Math.PI*1.5 ) {
                                                return 5;
                                        } else {
                                                return -7;
                                        }
                                })
                                .attr("text-anchor", function(d){
                                        if ( (d.startAngle+d.endAngle)/2 < Math.PI ){
                                                return "beginning";
                                        } else {
                                                return "end";
                                        }
                                }).text(function(d){
                                        //if value is greater than threshold show percentage
                                        if(d.value > threshold){
                                            var percentage = (d.value/that.totalOctets)*100;
                                            return percentage.toFixed(2)+"%";
                                        }
                                });

                        valueLabels.transition().duration(this.tweenDuration).attrTween("transform", this.textTween);
                        valueLabels.exit().remove();

推荐答案

正如@The Old County 发现的那样,我之前发布的答案在 firefox 中失败,因为它依赖于 SVG 方法 .getIntersectionList()查找冲突,该方法尚未在 Firefox 中实现.

As @The Old County discovered, the previous answer I posted fails in firefox because it relies on the SVG method .getIntersectionList() to find conflicts, and that method hasn't been implemented yet in Firefox.

这只是意味着我们必须跟踪标签位置并自己测试冲突.使用 d3,检查布局冲突的最有效方法是使用 quadtree 数据结构来存储位置,这样您就不必检查每个标签是否重叠,只需检查可视化相似区域中的标签.

That just means we have to keep track of label positions and test for conflicts ourselves. With d3, the most efficient way to check for layout conflicts involves using a quadtree data structure to store positions, that way you don't have to check every label for overlap, just those in a similar area of the visualization.

上一个答案的第二部分代码被替换为:

The second part of the code from the previous answer gets replaced with:

        /* check whether the default position 
           overlaps any other labels*/
        var conflicts = [];
        labelLayout.visit(function(node, x1, y1, x2, y2){
            //recurse down the tree, adding any overlapping labels
            //to the conflicts array

            //node is the node in the quadtree, 
            //node.point is the value that we added to the tree
            //x1,y1,x2,y2 are the bounds of the rectangle that
            //this node covers

            if (  (x1 > d.r + maxLabelWidth/2) 
                    //left edge of node is to the right of right edge of label
                ||(x2 < d.l - maxLabelWidth/2) 
                    //right edge of node is to the left of left edge of label
                ||(y1 > d.b + maxLabelHeight/2)
                    //top (minY) edge of node is greater than the bottom of label
                ||(y2 < d.t - maxLabelHeight/2 ) )
                    //bottom (maxY) edge of node is less than the top of label

                  return true; //don't bother visiting children or checking this node

            var p = node.point;
            var v = false, h = false;
            if ( p ) { //p is defined, i.e., there is a value stored in this node
                h =  ( ((p.l > d.l) && (p.l <= d.r))
                   || ((p.r > d.l) && (p.r <= d.r)) 
                   || ((p.l < d.l)&&(p.r >=d.r) ) ); //horizontal conflict

                v =  ( ((p.t > d.t) && (p.t <= d.b))
                   || ((p.b > d.t) && (p.b <= d.b))  
                   || ((p.t < d.t)&&(p.b >=d.b) ) ); //vertical conflict

                if (h&&v)
                    conflicts.push(p); //add to conflict list
            }

        });

        if (conflicts.length) {
            console.log(d, " conflicts with ", conflicts);  
            var rightEdge = d3.max(conflicts, function(d2) {
                return d2.r;
            });

            d.l = rightEdge;
            d.x = d.l + bbox.width / 2 + 5;
            d.r = d.l + bbox.width + 10;
        }
        else console.log("no conflicts for ", d);

        /* add this label to the quadtree, so it will show up as a conflict
           for future labels.  */
        labelLayout.add( d );
        var maxLabelWidth = Math.max(maxLabelWidth, bbox.width+10);
        var maxLabelHeight = Math.max(maxLabelHeight, bbox.height+10);

请注意,我已将标签边缘的参数名称更改为 l/r/b/t(左/右/下/上),以确保一切合乎逻辑.

Note that I've changed the parameter names for the edges of the label to l/r/b/t (left/right/bottom/top) to keep everything logical in my mind.

在这里直播小提琴:http://jsfiddle.net/Qh9X5/1249/

这样做的另一个好处是,您可以在实际设置位置之前,根据标签的最终位置检查冲突.这意味着在确定所有标签的位置后,您可以使用转换将标签移动到位.

An added benefit of doing it this way is that you can check for conflicts based on the final position of the labels, before actually setting the position. Which means that you can use transitions for moving the labels into position after figuring out the positions for all the labels.

这篇关于d3.js 为饼图传播标签的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆