javascript - 如何使用 d3.js 创建水平图例
我一直在尝试使用 d3.js 为我的图表创建一个水平图例。我在使用动态标签时一直难以正确设置 x 轴的间距。
问题是标签的宽度不一致,这里有一个完整的示例,这是我用来计算 x 位置的函数:
function legendXPosition(data, position, avgFontWidth){
if(position == 0){
return 0;
} else {
var xPostiion = 0;
for(i = 0; i < position; i++){
xPostiion += (data[i].length * avgFontWidth);
}
return xPostiion;
}
}
有人有什么建议可以改进这个吗?
答案#1
我建议参考这个问题:SVG 获取文本元素的宽度
像你现在这样渲染第一个图例条目。存储此条目,或分配 ID,以便你可以通过选择查找它们。
在渲染后续条目时,获取前一个 'text' 元素及 x 偏移量。使用前一个文本元素的精确宽度计算新的图例条目偏移量
var myNewXOffset = myPreviousXOffset + myPreviousText.getBBox().width
答案#2
修改了代码以获得以下结果
你的标签宽度不一致的原因有两个因素。这两个因素都与你计算 x 位置的函数有关:
- avgFontWidth 参数
它应该更小。你在示例中将其值分配为 15,但是实际的 averageFontWidth 远小于 15,它在 5 到 7 之间。你的图例标签之间的额外间距来自这个不准确的平均字体宽度值。
你的 avgFontWidth 只是一个平均值。实际上,字体的宽度是变化的。例如,下图中的 r 比 P 窄得多。
因此,当字体家族以某种方式设置时,五个字母的单词 rrrrr 可能比 PPPPP 短得多。总之,你不应该使用 avgFontWidth。正如 cmonkey 所建议的,你应该使用 SVG 获取文本元素宽度。
xPostiion += (data[i].length * avgFontWidth)的逻辑 上述公式旨在计算文本的长度,但还应加上标签的宽度,xPostiion应该是
xPostiion += (data[i].length * avgFontWidth) + widthOftheLabel修改后的代码:
function drawlinegraph(data, startDateString, endDateString, maxValue, minValue, colour,
divID, labels) {
var m = {
top: 60,
right: 0,
bottom: 35,
left: 80
},
w = 770 - m.left - m.right,
h = 180 - m.top - m.bottom,
dateFormat = "%Y-%m-%d";
labels = ["ada", "adas", "asdasdasd", "sd"];
var parseDate = d3.time.format(dateFormat).parse;
var startDate = parseDate(startDateString);
var endDate = parseDate(endDateString);
// Define the x scale
var x = d3.time.scale()
.domain([startDate, endDate])
.range([0, w]);
// Format x-axis labels
x.tickFormat(d3.time.format(dateFormat));
// Define the y scale
var y = d3.scale.linear()
.domain([minValue, maxValue])
.range([h, 0]);
var graph = d3.select(divID).append("svg:svg")
.attr("width", w + m.right + m.left)
.attr("height", h + m.top + m.bottom)
.append("svg:g")
.attr("transform", "translate(" + m.left + "," + m.top + ")");
// create x-axis
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(4)
.tickSize(-h)
.tickFormat(d3.time.format("%Y/%m"));
// Add the x-axis.
graph.append("svg:g")
.attr("class", "x axis")
.attr("transform", "translate(0," + h + ")")
.call(xAxis);
// Overide the default behaviour of d3 axis labels
d3.selectAll(".x.axis g text")[0].forEach(function(e) {
e.attributes[0].value = 8; // y
});
// create y-axis
var yAxisLeft = d3.svg.axis()
.scale(y)
.ticks(12)
.orient("left")
.tickSize(-w);
// Add the y-axis to the left
graph.append("svg:g")
.attr("class", "y axis")
.attr("transform", "translate(0,0)")
.call(yAxisLeft);
var i = 0;
$.each(data, function(key, value) {
value.forEach(function(d) {
d.date = parseDate(d.date);
});
var line = d3.svg.line()
.x(function(d) {
return x(d.date);
})
.y(function(d) {
return y(d.value);
});
graph.append("path")
.datum(value)
.attr("d", line)
.attr("class", "line")
.attr("stroke", colour[i]);
i++;
});
var legend = graph.append("g")
.attr("class", "legend")
.attr("height", 100)
.attr("width", 100)
.attr('transform', 'translate(-5,' + (h + 35) + ')');
legend.selectAll('rect')
.data(labels)
.enter()
.append("rect")
.attr("x", function(d, i) {
var xPost = legendXPosition(labels, i, 6);
return xPost;
})
.attr("y", -6)
.attr("width", 20)
.attr("height", 5)
.style("fill", function(d, i) {
var color = colour[i];
return color;
});
legend.selectAll('text')
.data(labels)
.enter()
.append("text")
.attr("x", function(d, i) {
var xPost = legendXPositionText(labels, i, 22, 6);
return xPost;
})
.attr("y", -1)
.text(function(d) {
return d;
});
};
function legendXPositionText(data, position, textOffset, avgFontWidth) {
return legendXPosition(data, position, avgFontWidth) + textOffset;
}
function legendXPosition(data, position, avgFontWidth) {
if (position == 0) {
return 0;
} else {
var xPostiion = 0;
for (i = 0; i < position; i++) {
xPostiion += (data[i].length * avgFontWidth + 40);
}
return xPostiion;
}
}
var benchmark_line_graph_colours = ["#524364", "#937ab1", "#ab5b02", "#faa757"],
benchmark_line_graph_data = {
"Beassa ALBI TR ZAR": [{
"date": "2012-08-31",
"value": 101.1
}, {
"date": "2012-09-28",
"value": 101.89
}, {
"date": "2012-10-31",
"value": 101.09
}],
"FTSE/JSE All Share TR ZAR": [{
"date": "2012-08-31",
"value": 99.72
}, {
"date": "2012-09-28",
"value": 101.24
}, {
"date": "2012-10-31",
"value": 105.29
}],
"STeFI Composite ZAR": [{
"date": "2012-08-31",
"value": 100.23
}, {
"date": "2012-09-28",
"value": 100.52
}, {
"date": "2012-10-31",
"value": 100.77
}],
"portfolio": [{
"date": "2012-08-31",
"value": 101.55
}, {
"date": "2012-09-28",
"value": 101.15
}, {
"date": "2012-10-31",
"value": 102.08
}]
};
drawlinegraph(benchmark_line_graph_data,
"2012-08-31",
"2012-10-31",
105.84700000000001,
99.163, benchmark_line_graph_colours,
"body");body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis text {
padding-top: 5px;
text-anchor: "right";
}
.line {
fill: none;
stroke-width: 1.5px;
}
.y.axis line,
.y.axis path {
stroke-dasharray: 2, 2;
}<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="http://d3js.org/d3.v3.js"></script>
