【问题标题】:d3: position text element dependent on length of element befored3:根据之前元素的长度定位文本元素
【发布时间】:2013-12-12 00:58:44
【问题描述】:

我被困在 d3(或一般的 JavaScript)中。

我想用 d3 创造一个传奇。这9个项目的位置应该相互依赖。更具体地说:

这是我的简化数组: var dataset = ["Africa","Asia", "Caribbean", "Central America", "Europe", "Middle East", "North America", "Oceania", "South America"];

在 x 轴上,我想将下一个文本进一步(向右)绘制 40 像素,然后最后一个文本标签结束。我的意图是每次在圆圈之间都有相同的空间。所以下一个文本总是取决于最后一个国家名称的长度。

我试过这个:

.attr("x", function(d, i) {return i * 40 + d[i-1].length + 7;})

但控制台显示 d[i-1] 未定义。

我错过了什么?你会如何解决这个问题?

提前非常感谢!非常感激你的帮助!

伊娃

更新: 实际上,我要绘制的图例不仅包括文字,还包括小圆圈。

这是数组(将 x_pos 硬编码为 d[2]:var dataset = [ ["非洲", "#4B7985", 5], ["亚洲", "#58AB86", 55], ["加勒比", "#63A4B5", 100], ["中美洲", "#818181", 165]、[“欧洲”、“#E9726C”、255]、[“中东”、“#E3AC73”、310]、[“北美”、“#B65856”、383]、[“大洋洲”、“ #287E5C", 470], ["南美洲", "#AC8358", 530] ];

如何根据国家名称的长度绘制圆圈并获得相同的圆圈间距?

【问题讨论】:

    标签: javascript d3.js


    【解决方案1】:

    您可以在画布上绘制文本元素以获取边界框。然后根据最后一个元素的宽度调整位置:

    svg.selectAll("text")
      .data(data).enter()
      .append("text")
      .attr("x", function(d) {
        return x_pos;
      })
      .attr("y", 50)
      .style("display", "none")
      .text(function(d) { return d; });
    
    svg.selectAll("text")
      .style("display", "block")
      .attr("x", function(d) {
        var c_pos = x_pos;
        x_pos = x_pos + this.getBBox().width + distance;
        return c_pos;
      });
    

    完整示例:https://vida.io/documents/C5CSjbWLhoJ8rQmhF

    【讨论】:

    • 非常感谢福!这完美! :) 但是,我只讲了故事的一部分,又偶然发现了一个问题。实际上,我要绘制的图例不仅包括文字,还包括小圆圈。这是数组(硬编码 x_pos 为 d[2]:var dataset = [ ["Africa", "#4B7985", 5], ["Asia", "#58AB86", 55], ["Caribbean", "#63A4B5", 100], ["中美洲", "#818181", 165], ["欧洲", "#E9726C", 255], ["中东", "#E3AC73", 310], [ "北美", "#B65856", 383], ["大洋洲", "#287E5C", 470], ["南美", "#AC8358", 530] ];
    • 那么,现在怎么办?我不能用“这个”。不再根据国家名称的长度绘制圆圈,对吗?所以,这就是为什么我想出 d[i-1] (或 rahter dataset[0][i-1] 在这种情况下??)。
    • 您可以将文本稍微移动一下,以便为图例留出空间。类似于:x_pos = x_pos + this.getBBox().width + distance + legend_space。而图例pos是:x_pos = x_pos + this.getBBox().width + distance;
    • 感谢您的回复 Phuoc Do!我试过了,显然我得到了圆圈框的宽度,因为“这个”现在指的是圆圈。如何告诉 d3 使用文本宽度?
    • 我明白了。这有点棘手。我用文本前面的圆圈更新了我的示例。
    【解决方案2】:

    这就是我的做法。

    //This will be your array of legends
    var legendItems = [] 
    
    var legendCount = legendItems.length;
    var legendSectionWidth = width / (legendCount+1);
    //This will be your "y" value going forward. You can assign anything you want. I have used the following if else case, you should replace it with your logic to calculate y.
    var vert = 0;
    if(legendPosition == "bottom"){
        if(showAxes)
            vert = height + legendVerticalPad + containerHeight*0.04;
        else
            vert = height + legendVerticalPad + containerHeight*0.02;
    }
    
    for(var i = 0; i < legendCount; i++){
        var text = svg.append('text')
            .attr('x', (i+1)*legendSectionWidth)
            .attr('y', vert)
            .attr('class', 'legend-text '+legendItems[i])
            .style('text-anchor', 'middle')
            .style('dominant-baseline', 'central')
            .text(function() {
                return legendItems[i];
            });
    
        var len = text[0][0].getComputedTextLength();
    
    // you could use circles here, just change the width n height to rx and x and y to cx and cy// you could use circles here, just change the width n height to rx and x and y to cx and cy`enter code here`
    //The value 5 is your choice, i use 5px between my rect and text
        svg.append('rect') 
            .attr('x', (i+1)*legendSectionWidth - len/2 - 5 - legendRectSize)
            .attr('y', vert - legendRectSize/2)
            .attr('width', legendRectSize)
            .attr('height', legendRectSize)
            .attr('class', function () { return 'legend '+ legendItems[i];} )
            .attr('label', function()  {
                return legendItems[i]; 
            })
    }
    

    结果是这样的

    以下图像证明图例(矩形和文本的组合)与每个图例的距离相等,并且位于所提供宽度的中心。有了这个逻辑,无论你需要多少个图例,所有图例都将彼此等距放置并显示在屏幕中间

    我希望这会有所帮助。

    【讨论】:

      【解决方案3】:

      首先,d 指的是一个单独的绑定数据点,而i 指的是它在数据集中的索引。要查看之前的数据点,您需要引用原始数据集,而不是提供的数据点。

      假设你有:

      var dataset = ["Africa","Asia", "Caribbean", "Central America", "Europe", "Middle East", "North America", "Oceania", "South America"];
      
      d3.select('.foo').data(dataset)....
      

      您可能希望将位置处理程序中的 d[i - 1] 引用更改为 dataset[i - 1]

      修复后,您的第一个元素仍会爆炸,因为它位于 dataset[0]。在这种情况下,dataset[i - 1]dataset[-1]

      您可以将您的退货声明更改为:

      return i ? (i * 40 + dataset[i-1].length + 7) : i;
      

      【讨论】:

      • 是的,我忘了说这很可能是个问题。但是我试图用 if 语句来消除它,但它没有帮助。哦,天哪,我很抱歉,但我是 JS 的新手。是 ?和:咖啡脚本,或者我可以像你写的那样复制/粘贴它吗?
      • 不用担心。这只是一个三元声明。您可以剪切和粘贴的标准 javascript。基本上它说如果我是真的(1个或更多)然后做数学。否则返回 i。查看此链接了解更多信息 :) javascript.about.com/od/byexample/a/ternary-example.htm
      • 啊,我明白了。感谢您的澄清。并为您的建议!不幸的是,它也不起作用。我试过: if (i == 0) { return 5; } else if (i > 0) { return i * 60 + d[i - 1].length + 7; } 在匿名函数中。但我仍然得到“d[i - 1] 未定义”。 d[i - 1] 可能有什么问题?
      • D'oh,这真是愚蠢。我完全专注于索引错误,但我没有注意到您的原始数组(一维)。 D 只是指数组中的元素。您可能希望使用 dataset 来引用您提供给 d3 的整体数据集。
      • Gopherkhan,谢谢!使用“数据集”而不是“d”它可以工作!但结果并不如预期。我似乎在数学上有一个错误......
      猜你喜欢
      • 1970-01-01
      • 2020-12-13
      • 1970-01-01
      • 2020-12-08
      • 1970-01-01
      • 2014-06-02
      • 2021-12-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多