草庐IT

javascript - d3 v4 圆环图具有与小值重叠的文本和折线

coder 2025-01-20 原文

我正在尝试弄清楚如何排列标签以使它们不重叠。这是图表的图片

如您所见,对于非常小的值,文本标签重叠。我试图遍历每个文本元素并修改它的位置,但这似乎不起作用。你可以在这个函数的底部看到我试图获取每个文本元素的位置然后修改它。我究竟做错了什么?我已经研究了几个小时。

   _renderDonutChart() {
    let self = this;
    // console.log("Donut Chart is beginning render")
    let textOffset = 14;
    self.graph.data[0].forEach(function (d) {
        d.value = +d.value;
    })
    console.log(self.graph.data[0])
    let boxSize = (self.options.radius + self.options.padding) * 2;
    let parent = d3.select(self.ui.parent);
    //let color = d3.scaleOrdinal(['#dc8710', '#9e3400', '#f19b12']);
    let color = d3.scaleOrdinal(d3.schemeCategory20c);
    let svg = parent.append('svg')
        .attr('width', boxSize * 2)
        .attr('height', boxSize)
        .attr('transform', 'translate(-111,0)')
        .append('g')
        .attr('transform', 'translate(' + boxSize + ',' + boxSize / 2 + ')');
    svg.append('g')
        .attr('class', 'slices')
    svg.append("g")
        .attr("class", "labelName")
    svg.append("g")
        .attr("class", "labelValue")
    svg.append("g")
        .attr("class", "lines")
    svg.append("div")
        .attr("class", "progress-circle__box progress-circle__box--victorytype")
    let arc = d3.arc()
        .innerRadius(self.options.radius - self.options.border)
        .outerRadius(self.options.radius);
    let outerArc = d3.arc()
        .innerRadius((self.options.radius - self.options.border) * 1.2)
        .outerRadius((self.options.radius) * 1.2);
    let legendRectSize = self.options.radius * 0.05;
    let legendSpacing  = self.options.radius * 0.02;
    let pie = d3.pie()
        .value(function(d) { return d.value; })
        .sort(null);
    let slice = svg.select('.slices')
        .selectAll('path.slice')
        .data(pie(self.graph.data[0]))
        .enter()
        .append('path')
        .attr("class", "slice")
        .attr('d', arc)
        .attr('fill', function(d, i) {
            return color(d.data.label);
        })
        .transition().duration(1000)
        .attrTween("d", function(d) {
            this._current = this._current || 0;
            var interpolate = d3.interpolate(this._current, d);
            this._current = interpolate(0);
            return function(t) {
                return arc(interpolate(t));
            };
        })

    function midAngle(d){
        return d.startAngle + (d.endAngle - d.startAngle)/2;
    }
    let text = svg.select(".labelName").selectAll("text")
        .data(pie(self.graph.data[0]))
        .enter()
        .append("text")
        .attr('class', 'label')
        .attr("dy", ".35em")
        .attr('transform', function(d) {

            // effectively computes the centre of the slice.
            // see https://github.com/d3/d3-shape/blob/master/README.md#arc_centroid
            var pos = outerArc.centroid(d);

            // changes the point to be on left or right depending on where label is.
            pos[0] = self.options.radius * 0.97 * (midAngle(d) < Math.PI ? 1 : -1);
            return 'translate(' + pos + ')';
        })
        .style('text-anchor', function(d) {
            // if slice centre is on the left, anchor text to start, otherwise anchor to end
            return (midAngle(d)) < Math.PI ? 'start' : 'end';
        })
        .style("fill", "white")
        .text(function(d) {
            return (" " + d.data.label+": " +d.value+"");
        })
        .transition().duration(1000)
        .attrTween("transform", function(d) {
            this._current = this._current || d;
            var interpolate = d3.interpolate(this._current, d);
            this._current = interpolate(0);
            return function(t) {
                var d2 = interpolate(t);
                var pos = outerArc.centroid(d2);
                pos[0] = self.options.radius * (midAngle(d2) < Math.PI ? 1 : -1);
                return "translate("+ pos +")";
            };
        })
        .styleTween("text-anchor", function(d){
            this._current = this._current || d;
            var interpolate = d3.interpolate(this._current, d);
            this._current = interpolate(0);
            return function(t) {
                var d2 = interpolate(t);
                return midAngle(d2) < Math.PI ? "start":"end";
            };
        })
        .text(function(d) {
            return (d.data.label+": "+d.value+"%");
        })
    let polyline = svg.select(".lines").selectAll("polyline")
        .data(pie(self.graph.data[0]))
        .enter()
        .append("polyline")
        .attr('points', function(d) {
            var pos = outerArc.centroid(d);
            pos[0] = self.options.radius * 0.95 * (midAngle(d) < Math.PI ? 1 : -1);
            return [arc.centroid(d), outerArc.centroid(d), pos]
        })
        .style("fill", "none")
        .style("stroke", "white")
        .style("stroke-width", "1px");

    let prev;
    text.each(function(d, i) {
        if(i > 0) {
            let thisbb = this.getBoundingClientRect(),
                prevbb = prev.getBoundingClientRect();
            // move if they overlap
            console.log(thisbb.left);
            console.log(prevbb.right);
            if(!(thisbb.right < prevbb.left ||
                thisbb.left > prevbb.right ||
                thisbb.bottom < prevbb.top ||
                thisbb.top > prevbb.bottom)) {
                var ctx = thisbb.left + (thisbb.right - thisbb.left)/2,
                    cty = thisbb.top + (thisbb.bottom - thisbb.top)/2,
                    cpx = prevbb.left + (prevbb.right - prevbb.left)/2,
                    cpy = prevbb.top + (prevbb.bottom - prevbb.top)/2,
                    off = Math.sqrt(Math.pow(ctx - cpx, 2) + Math.pow(cty - cpy, 2))/2;
                d3.select(this).attr("transform",
                    "translate(" + Math.cos(((d.startAngle + d.endAngle - Math.PI) / 2)) *
                    (self.options.radius + textOffset + off) + "," +
                    Math.sin((d.startAngle + d.endAngle - Math.PI) / 2) *
                    (self.options.radius + textOffset + off) + ")");
            }
        }
        prev = this;
    });

    // console.log("Donut Chart is ending render")
}

最佳答案

我遇到了同样的问题。对我来说最好的解决方案是增加 Svg 区域的大小(基于当前窗口)并根据 donut 的半径向图例添加一些填充。

即:

   var margin = {top: 20, right: 100, bottom: 30, left: 40};
   var svgWidth = window.innerWidth - (window.innerWidth/4);
   var width = svgWidth,
      height = (Math.min(width) / 2) + 100,
      radius = Math.min(width, height) / 3;

   var legendRectSize = (radius * 0.08);
   var legendSpacing = (radius * 0.05);

这对我有用,直到项目数变得太高。我修改了边缘周围的标签以在切片/弧形悬停事件中显示在中心。 (数据值上有一个动画,这就是为什么他们在这个快照中是倾斜的)

我认为这可以让难以阅读的数据标签变得不那么困惑。

关于javascript - d3 v4 圆环图具有与小值重叠的文本和折线,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47401768/

有关javascript - d3 v4 圆环图具有与小值重叠的文本和折线的更多相关文章

  1. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

    我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

  2. ruby-on-rails - Rails 3.1 中具有相同形式的多个模型? - 2

    我正在使用Rails3.1并在一个论坛上工作。我有一个名为Topic的模型,每个模型都有许多Post。当用户创建新主题时,他们也应该创建第一个Post。但是,我不确定如何以相同的形式执行此操作。这是我的代码:classTopic:destroyaccepts_nested_attributes_for:postsvalidates_presence_of:titleendclassPost...但这似乎不起作用。有什么想法吗?谢谢! 最佳答案 @Pablo的回答似乎有你需要的一切。但更具体地说...首先改变你View中的这一行对此#

  3. ruby - 具有两个参数的 block - 2

    我从用户Hirolau那里找到了这段代码:defsum_to_n?(a,n)a.combination(2).find{|x,y|x+y==n}enda=[1,2,3,4,5]sum_to_n?(a,9)#=>[4,5]sum_to_n?(a,11)#=>nil我如何知道何时可以将两个参数发送到预定义方法(如find)?我不清楚,因为有时它不起作用。这是重新定义的东西吗? 最佳答案 如果您查看Enumerable#find的文档,您会发现它只接受一个block参数。您可以将它发送两次的原因是因为Ruby可以方便地让您根据它的“并行赋

  4. ruby-on-rails - 在 RSpec 中,如何以任意顺序期望具有不同参数的多条消息? - 2

    RSpec似乎按顺序匹配方法接收的消息。我不确定如何使以下代码工作:allow(a).toreceive(:f)expect(a).toreceive(:f).with(2)a.f(1)a.f(2)a.f(3)我问的原因是a.f的一些调用是由我的代码的上层控制的,所以我不能对这些方法调用添加期望。 最佳答案 RSpecspy是测试这种情况的一种方式。要监视一个方法,用allowstub,除了方法名称之外没有任何约束,调用该方法,然后expect确切的方法调用。例如:allow(a).toreceive(:f)a.f(2)a.f(1)

  5. ruby-on-rails - 具有同名的模块和类 - 2

    我有一个模块stat存在于目录结构中:lib/stat_creator/stat/在lib/stat_creator/stat.rb中,我在lib/stat_creator/stat/目录中有我需要的文件,以及:moduleStatCreatormoduleStatendend当我使用该模块时,我将这些类称为StatCreator::Stat::Foo.new现在我想要一个存在于应用程序中的根Stat类。我在app/models中制作了我的Stat类,并在routes.rb中进行了设置。但是,如果我转到Rails控制台并尝试在应用程序/模型中使用Stat类,例如:Stat.by_use

  6. ruby-on-rails - 使用 javascript 更改数据方法不会更改 ajax 调用用户的什么方法? - 2

    我遇到了一个非常奇怪的问题,我很难解决。在我看来,我有一个与data-remote="true"和data-method="delete"的链接。当我单击该链接时,我可以看到对我的Rails服务器的DELETE请求。返回的JS代码会更改此链接的属性,其中包括href和data-method。再次单击此链接后,我的服务器收到了对新href的请求,但使用的是旧的data-method,即使我已将其从DELETE到POST(它仍然发送一个DELETE请求)。但是,如果我刷新页面,HTML与"new"HTML相同(随返回的JS发生变化),但它实际上发送了正确的请求类型。这就是这个问题令我困惑的

  7. ruby-on-rails - 在具有 ActiveRecord 条件的相关模型中按字段排序 - 2

    我正在尝试按Rails相关模型中的字段进行排序。我研究的所有解决方案都没有解决如果相关模型被另一个参数过滤?元素模型classItem相关模型:classPriority我正在使用where子句检索项目:@items=Item.where('company_id=?andapproved=?',@company.id,true).all我需要按相关表格中的“位置”列进行排序。问题在于,在优先级模型中,一个项目可能会被多家公司列出。因此,这些职位取决于他们拥有的company_id。当我显示项目时,它是针对一个公司的,按公司内的职位排序。完成此任务的正确方法是什么?感谢您的帮助。PS-我

  8. ruby-on-rails - Sunspot:如何对具有不同值的多个字段进行全文查询? - 2

    我想用sunspot重现以下原始solr查询q=exact_term_text:fooORterm_textv:foo*ORalternate_text:bar*但我无法通过标准的太阳黑子界面理解这是否可能以及如何实现,因为看起来:fulltext方法似乎不接受多个文本/搜索字段参数我不知道将什么参数作为第一个参数传递给fulltext,就好像我通过了"foo"或"bar"结果不匹配如果我传递一个空参数,我得到一个q=*:*范围过滤器(例如with(:term).starting_with('foo*')(顾名思义)作为过滤器查询应用,因此不参与评分。似乎可以手动编写字符串(或者可能使

  9. ruby - 引用具有指定索引的枚举器值 - 2

    假设我有一个可枚举对象enum,现在我想获取第三个项目。我知道一种通用方法是转换成数组,然后使用索引访问,如:enum.to_a[2]但这种方式会创建一个临时数组,效率可能很低。现在我使用:enum.each_with_index{|v,i|breakvifi==2}但这非常丑陋和多余。执行此操作最有效的方法是什么? 最佳答案 你可以使用take剥离前三个元素,然后剥离last从take给你的数组中获取第三个元素:third=enum.take(3).last如果您根本不想生成任何数组,那么也许:#Ifenumisn'tanEnum

  10. ruby-on-rails - 具有未知键和强参数的 Rails 哈希 - 2

    我有一个Rails应用程序,它在名为properties的字段中存储序列化哈希。虽然哈希键是未知的,所以我不知道有什么方法可以通过强参数实现这一点。谷歌搜索时,我发现了这个:https://github.com/rails/rails/issues/9454,但我想不出具体的解决方案。基本上,我的问题是:如何配置强参数以允许使用未知键的散列?感谢大家的帮助! 最佳答案 我最近遇到了同样的问题,我使用来自https://github.com/rails/rails/issues/9454的@fxn方法解决了它对于以properties

随机推荐