我有这个传说:
如您所见,每个图例条目的宽度相同。相反,我希望每个图例条目的宽度根据条目的符号和文本的宽度而变化。最终,我希望前导条目文本的末尾与后面条目符号的开头之间的距离相同。换句话说,我想要'OA'和加号之间的距离与'OI'和钻石以及'RARC'和正方形之间的距离相同。我需要这个基于像素(字符串长度是不够的)。我一直在尝试各种各样的东西,但没有成功。
这是我的代码:
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
(或legendRect
或legend
)的宽度来代替(I * (.15 * width))
。我无法弄清楚如何做到这一点。
您可以看到我在代码后面使用以下内容获取legendRectE
的宽度:var node: SVGElement = <SVGElement>legendRectE.node()
,然后是node.getBoundingClientRect().width
。
node.getBoundingClientRect().width
给我一个宽度值,你可以看到它现在正在使用,但当我使用同样的方法来确定我提到的翻译的值时,它会窒息;当我使用legendRect
或legend
代替legendRectE
时,我只能获得'0'。
我以为我能够编辑这样的转换函数:
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
})
;
显然,我错了。有什么想法/建议吗?
P.S。我正在使用d3 v3.5。
答案 0 :(得分:3)
挑战在于(据我所知)在最初附加元素时难以确定变换,因为宽度未知。但是,您可以返回并计算每个图例条目的全部附加后的宽度,然后相应地重新定位图例条目。
下面的代码段将所有内容置于彼此之外,然后使用svg
计算每个图例的g
宽度getBBox
。然后,使用d3.sum计算在其之前附加的每个元素的宽度(因此应该在其左侧)并相应地将translate值设置为这些宽度的总和。
它可能可能会被清理一下,它有点快。如果在元素正确定位之前存在滞后,透明地附加它们然后在它们被定位后将它们淡入可能是一个优雅的(在视觉上,不那么编程)解决方案(或者最初将它们附加到视图框之外)。
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)";
})
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.min.js"></script>
&#13;
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)";
})
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
&#13;