简体   繁体   English

如何在D3折线图上拟合可变长度刻度标签?

[英]How to fit variable length tick labels on a D3 line chart?

Here's a JSFiddle: http://jsfiddle.net/8p2yc/ (A slightly modified example from here: http://bl.ocks.org/mbostock/3883245 ) 这是一个JSFiddle: http//jsfiddle.net/8p2yc/ (这里有一个稍微修改过的例子: http ://bl.ocks.org/mbostock/3883245)

As you can see in the JSFiddle tick labels along the y axis do not fit in the svg. 正如您在JSFiddle中看到的那样,沿y轴的刻度标签不适合svg。 I know I can increase the left margin, but the thing is I don't know what the data will be in advance. 我知道我可以增加左边距,但问题是我不知道预先提供了什么数据。 If I just make the margin very large the chart will look awkward if the numbers are short in length. 如果我只是使边距非常大,如果数字很短,图表看起来会很尴尬。

Is there a way to precompute the maximum label width when creating the chart to set the margin correctly? 有没有办法在创建图表时预先计算最大标签宽度以正确设置边距? Or perhaps there's an entirely different solution? 或许还有一个完全不同的解决方案?

var margin = {top: 20, right: 20, bottom: 30, left: 50},
    width = 400 - margin.left - margin.right,
    height = 200 - margin.top - margin.bottom;

var svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

示例图表

Thanks! 谢谢!

You can do this by appending the text for the largest label, measuring it and removing it immediately afterwards: 您可以通过附加最大标签的文本,测量它并在之后立即将其删除来完成此操作:

var maxLabel = d3.max(data, function(d) { return d.close; }),
    maxWidth;
svg.append("text").text(maxLabel)
   .each(function() { maxWidth = this.getBBox().width; })
   .remove();

Then you can use that width to do the translation of the g element: 然后您可以使用该宽度来执行g元素的转换:

svg.attr("transform", "translate(" + Math.max(margin.left, maxWidth) + "," + margin.top + ")");

Complete example here . 在这里完成示例。

Edit: Getting the maximum length of the actual labels is a bit more involved because you have to generate them (with the right formatting) and measure them all. 编辑:获取实际标签的最大长度需要更多参与,因为您必须生成它们(使用正确的格式)并测量它们。 This is a better way to do it though as you're measuring what's actually displayed. 当你测量实际显示的内容时,这是一种更好的方法。 The code is similar: 代码类似:

var maxWidth = 0;
svg.selectAll("text.foo").data(y.ticks())
   .enter().append("text").text(function(d) { return y.tickFormat()(d); })
   .each(function(d) {
     maxWidth = Math.max(this.getBBox().width + yAxis.tickSize() + yAxis.tickPadding(), maxWidth);
   })
 .remove();

I'm adding the size of the tick line and the padding between tick line and label to the width here. 我在这里添加了刻度线的大小和刻度线和标签之间的填充。 Complete example of that here . 这里有完整的例子。

A chicken-and-egg problem prevents this from being precomputed reliably, assuming you want your axes and plot to combine to fill a fixed space, and your tick labels to be generated automatically. 假设您希望轴和绘图组合以填充固定空间,并且您的刻度标签将自动生成,鸡和蛋的问题可以防止这种情况被可靠地预先计算。

The problem is that the tick labels, and therefore the space they require, depend on the scale. 问题是刻度标签以及它们所需的空间取决于规模。 A time scale, for example, may include longer month names (eg 'September') over a short domain, but shorter month names or just years over a long domain. 例如,时间尺度可以包括在较短域上的较长月份名称(例如“9月”),但是较短的月份名称或仅在长域上的年份。 But the scale, at least on the opposite axis, depends on the space left for the range, which depends on the space left over after determining the tick labels. 但是,至少在相反的轴上,比例取决于范围的剩余空间,这取决于确定刻度标签后留下的空间。

In a lot of cases, the minimum and maximum values may give you an idea of the widest tick labels, and you can use Lars' method . 在很多情况下,最小值和最大值可以让您了解最宽的刻度标签,并且可以使用Lars的方法 For example, if the domain is –50 to 10, any tick labels between those will be narrower. 例如,如果域为-50到10,则这些域之间的任何刻度标签将更窄。 But this assumes they're integers! 但这假设它们是整数! Also, be careful with ' nice ' scales; 另外,要注意' 漂亮 '的尺度; if your maximum value is 8, D3 may try to make the greatest tick label 10, which is a wider string. 如果你的最大值是8,D3可能会尝试制作最大的刻度标签10,这是一个更宽的字符串。

d3 v4 Solution for Lars' Method. d3 v4 Lars方法的解决方案。

calculateMarginForYScaleTicks() {
    let maxWidth = 0;
    let textFormatter = d3.format(",");
    let tempYScale = d3
        .scaleLinear()
        .range([0, 0])
        .domain([0, d3.max(data, d => d.value)]);

    d3
        .select("#svg")
        .selectAll("text.foo")
        .data(tempYScale.ticks())
        .enter()
        .append("text")
        .text(d => textFormatter(d))
        .each(function() {
            maxWidth = Math.max(this.getBBox().width, maxWidth);
        })
        .remove();

    return maxWidth;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM