如何更改d3图例条目间距/对齐方式 [英] How to change d3 legend entry spacing/alignment

查看:102
本文介绍了如何更改d3图例条目间距/对齐方式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这个传说:

如您所见,每个图例条目的宽度相同.相反,我希望每个图例条目的宽度根据条目的符号和文本的宽度而变化.最终,我希望开头条目的文本的结尾与后面条目的符号的开头之间的距离相同.换句话说,我希望"OA"和加号之间的距离与"OI"和钻石以及"RARC"和正方形之间的距离相同.我需要基于像素(字符串长度不足).我一直在尝试各种方法,但没有成功.

As you can see, each legend entry is the same width. Instead, I'd like each legend entry's width to vary based upon the width of the entry's symbol and text. Ultimately, I want the same distance between the ends of the leading entry's text and the start of the following entry's symbol. In other words, I'd like the same distance between 'OA' and the plus sign as between the 'OI' and the diamond and the 'RARC' and the square. I need this to be based on pixels (string lengths won't suffice). I've been trying all sorts of stuff, but haven't been successful.

这是我的代码:

        var legendData = [["OA", "yellow", "circle"], ["OI", "blue", "cross"], ["RARC", "green", "diamond"], ["CAPE", "red", "square"], ["Other", "black", "triangle-down"]];

        this.svg.selectAll('.legend').remove() //remove remnants of previous legend so new legend has clean slate...eliminates overlays during resizing

        var legend = this.svg.append('g')
            .attr("class", "legend")
            .attr("height", 0)
            .attr("width", 0)
            .attr('transform', 'translate(' + (ScatterChart.Config.margins.left + (width * .008)) + ',' + (height += .40 * ScatterChart.Config.margins.bottom) + ')');

        var legendRect = legend
            .selectAll('g')
            .data(legendData)
            ;

        var labelLength = 0
        var labelLengthPrevious = 0

        var legendRectE = legendRect.enter()
            .append("g")
            .attr("transform", function (d, i) {
                //labelLength = labelLengthPrevious //Need to figure out pixel lengths
                //labelLengthPrevious += (d[0].length) + 50
                //return 'translate(' + labelLength + ', ' + 0 + ' )';  // y is constant and x growing
                return 'translate(' + (i * (.15 * width)) + ', ' + 0 + ' )';  // y is constant and x growing
            })
            ;

        legendRectE
            .append('path')
            .attr("d", d3.svg.symbol().type((d) => {
                return d[2]
            }
            ).size((d3.min([height, width]) * ScatterChart.Config.axisFontMultiplier) * (d3.min([height, width]) * ScatterChart.Config.symbolSizeMultiplier)))
            .style("fill", function (d) {
                return d[1];
            })
            .attr('stroke', 'black')
            ;

        //This asserts legendRectE as a node...I think. I do this so I can use the width and height measurements of legendRectE.
        var node: SVGElement = <SVGElement>legendRectE.node()

        legendRectE
            .append("text")
            .attr("x", function (d) {
                return node.getBoundingClientRect().width
            })
            .attr("y", function (d) {
                return node.getBoundingClientRect().height / 2.25
            })
            .text(function (d) {
                return d[0];
            })
            .style('font-size', function () { return d3.min([height, width]) * ScatterChart.Config.axisFontMultiplier + "px" })
            ;

我认为答案与这一行有关:return 'translate(' + (i * (.15 * width)) + ', ' + 0 + ' )'; // y is constant and x growing.现在,它只是通过将索引乘以图表宽度的15%来向右移动.我认为我需要以某种方式替换legendRectE(或legendRectlegend)的宽度来代替(I * (.15 * width)).我不知道该怎么做.

I think the answer would have something to do with this line: return 'translate(' + (i * (.15 * width)) + ', ' + 0 + ' )'; // y is constant and x growing. Right now, it just shifts to the right by multiplying the index by 15% of the chart's width. I figure I need to somehow substitute the width of the legendRectE (or of legendRect or legend) in place of (I * (.15 * width)). I can't figure out how to do that.

您可以看到我稍后将在代码中使用以下代码来获取legendRectE的宽度:var node: SVGElement = <SVGElement>legendRectE.node(),然后是node.getBoundingClientRect().width.

You can see that I use the following to get the width of legendRectE later in the code: var node: SVGElement = <SVGElement>legendRectE.node(), followed by node.getBoundingClientRect().width.

node.getBoundingClientRect().width给了我现在正在使用的宽度值,但是当我使用相同的方法来确定我提到的翻译的值时,它会窒息;而当我使用legendRectlegend代替legendRectE时,我只会得到'0'.

node.getBoundingClientRect().width gives me a width value where you see it being used now, but when I use this same approach to determine a value for the translate I mentioned, it chokes; and when I use legendRect or legend instead of legendRectE I only get '0'.

我认为我可以编辑转换函数,如下所示:

I thought I'd be able to edit the transform function something like this:

    var legendRectE = legendRect.enter()
        .append("g")
        .attr("transform", function (d, i) {
             var node: SVGElement = <SVGElement>legendRectE.node()
             return 'translate(' + node.getBoundingClientRect().width + ', ' + 0 + ' )';  // y is constant and x growing
        })
        ; 

很明显,我错了.有任何想法/建议吗?

Obviously, I was wrong. Any ideas/advice?

p.s.我正在使用d3 v3.5.

p.s. I'm using d3 v3.5.

推荐答案

挑战在于,据我所知,由于宽度未知,最初添加元素时很难确定转换.但是您可以返回并计算每个图例条目的宽度,然后将其全部添加,然后相应地重新放置图例条目.

The challenge is that it is (as far as I know) difficult to determine the transform when appending elements initially as the widths are unknown. But you could go back and calculate the width of each legend entry after they are all appended and then reposition the legend entries accordingly.

下面的代码片段将所有内容放置在彼此的顶部以开始,然后使用getBBox计算每个图例gsvg宽度.然后,使用d3.sum计算在其之前附加的每个元素的宽度(因此应该在元素的左侧),并将转换值相应地设置为这些宽度的总和.

The snippet below positions everything overtop of each other to start, then calculates the svg width of each legend g using getBBox. Then, using d3.sum, calculates the width of each element that was appended before it (and thus should be to the left of it) and sets the translate value to the sum of those widths accordingly.

大概可以清理一点,有点快.如果在正确放置元素之前存在滞后,可以透明地添加它们,然后在放置之后淡入淡入,这可能是一种优雅的解决方案(从视觉上讲,在编程上不太是如此)(或者将它们最初添加到视图框之外).

It can probably be cleaned up a bit probably, it's a little quick. If there is lag before the elements are positioned correctly, appending them transparently and then fading them in after they are positioned might be an elegant (visually, less so programatically) solution (or appending them initially outside of the view box).

d3v4:

var data = ['short text','much longer text','the longest text passage','short text'];

var svg = d3.select('body')
  .append('svg')
  .attr('width',800)
  .attr('height',200);
  
var groups = svg.selectAll('g')
 .data(data)
 .enter()
 .append('g');
 
var rect = groups.append('rect')
  .attr('fill',function(d,i) { return d3.schemeCategory10[i];})
  .attr('height',30)
  .attr('width',30);

var text = groups.append('text')
  .attr('y', 20)
  .attr('x', 35)
  .text(function(d) { return d; });
  
// Now space the groups out after they have been appended:
var padding = 10;
groups.attr('transform', function(d,i) { 
  return "translate("+(d3.sum(data, function(e,j) {
    if (j < i) { return groups.nodes()[j].getBBox().width; } else return 0; }) + padding * i) + ",0)";
})

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.min.js"></script>

d3v3:

var data = ['short text','much longer text','the longest text passage','short text'];

var svg = d3.select('body')
  .append('svg')
  .attr('width',800)
  .attr('height',200);
  
var groups = svg.selectAll('g')
 .data(data)
 .enter()
 .append('g');
 
var color = ["orange","red","purple","green"];
 
var rect = groups.append('rect')
  .attr('fill',function(d,i) { return color[i];})
  .attr('height',30)
  .attr('width',30);

var text = groups.append('text')
  .attr('y', 20)
  .attr('x', 35)
  .text(function(d) { return d; });
  
// Now space the groups out after they have been appended:
var padding = 10;
groups.attr('transform', function(d,i) { 
  return "translate("+(d3.sum(data, function(e,j) {
    if (j < i) { return groups[0][j].getBBox().width; } else return 0; }) + padding * i) + ",0)";
})

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

这篇关于如何更改d3图例条目间距/对齐方式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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