我正在做一个可视化 Web 应用程序,它使用 d3 树形图呈现数据。
当用户将鼠标悬停在树形图上时,会出现一个工具提示,将数据显示为具有许多行和列的表格,例如 5x4。
我可以正确显示每一行中的数据,但我很难将折线图显示到每行的一列中。
下面是我使用的代码:
// Tooltip
leaf.on("mousemove", function (d) {
if (d.depth < 3) return;
var name1 = data.name;
var name2 = d.parent.data.name;
var name3 = d.data.name;
var category = d.data.category;
var change = d.data.change;
var filtered = items.filter(function (item) { return item.level2 == d.parent.data.name });
var tooltip_template = '\
<h3 class="tooltip-header"> ' + name2 + ' / ' + name3 + '</h3> \
<div class="tooltip-body"> \
<table> \
';
filtered.forEach(function (item) {
tooltip_template += item.name == d.data.name
? '<tr style="color: #fff; background: ' + getColor(d.data.change) + '"> \
<td> ' + item.name + '</td> \
<td> \
<svg></svg> \
</td> \
<td class="txtright"> ' + item.value + ' 건</td> \
<td class="txtright"> ' + (item.change > 0 ? '+' : '') + item.change + ' 건</td> \
</tr>'
: '<tr> \
<td> ' + item.name + '</td> \
<td> \
<svg></svg> \
</td> \
<td class="txtright"> ' + item.value + ' 건</td> \
<td class="txtright"> ' + (item.change > 0 ? '+' : '') + item.change + ' 건</td> \
</tr>';
// set the dimensions and margins of the line graph
var linemargin = {
top: d3.event.pageY + 15,
right: d3.event.pageX + 5,
bottom: d3.event.pageY + 15,
left: d3.event.pageX + 5
}, linewidth = 100 - linemargin.left - linemargin.right, lineheight = 20 - linemargin.top - linemargin.bottom;
// Set the data for the chart
const linedata = [
{ x: 0, y: 5 },
{ x: 1, y: 9 },
{ x: 2, y: 7 },
{ x: 3, y: 5 },
{ x: 4, y: 3 },
{ x: 5, y: 4 },
{ x: 6, y: 8 },
{ x: 7, y: 6 },
{ x: 8, y: 3 },
{ x: 9, y: 2 },
];
const linechart = tooltip.selectAll("svg").append("g").enter().attr("class", "linechart").attr("transform", `translate(${linemargin.left}, ${linemargin.top})`);
const x = d3.scaleLinear().domain([0, 9]).range([0, linewidth]);
const y = d3.scaleLinear().domain([0, 10]).range([lineheight, 0]);
// Create the line generator
const line = d3
.line()
.x((d) => x(d.x))
.y((d) => y(d.y));
// Add the line to the chart
linechart.append("path").datum(linedata).attr("class", "line").attr("d", line);
})
tooltip_template += ' \
</tbody> \
</table> \
</div> \
'
tooltip
.attr('id', 'tooltip')
.attr('data-change', change)
.style("", "#")
tooltip.transition()
.style("opacity", 1);
tooltip.html(tooltip_template)
.style("left", (d3.event.pageX + 5) + "px")
.style("top", (d3.event.pageY + 15) + "px")
});
请帮忙!
潜在的核心问题是,您在
<svg>
中创建了 tooltip_template
,但从未为其分配任何维度(例如 height
和 width
)。
您可以进行一些额外的调整:
<div>
中创建工具提示并在树形图的每个节点上重复使用它<template>
创建一个 <tr>
(请参阅此处)
<tr>
都有一个嵌入的<svg>
,它采用折线图并为每个class
<table>
的动态渲染展示在mouseenter
;更新每个鼠标在 mousemove
上的位置;并将桌子隐藏在 mouseout
通过这些调整,您可以依赖 HTML 而不是字符串,并将表的计算移至调用一次而不是多次的事件处理程序。
下面的工作示例包含随机内容:
const leaf = d3.selectAll(".leaf");
leaf.on("mouseenter", function(d) {
const table = d3.select("#custom-tooltip table")
// remove any rows
table.selectAll("*").remove();
// add random 2-5 data rows with content
const rowCount = Math.floor(Math.random() * 4) + 2;
for (let r=0; r<rowCount; ++r) {
// add row
const templateRow = document.querySelector("#tooltip-datarow").content.cloneNode(true);
table.node().appendChild(templateRow);
// add line chart
const d3row = table.select("tr#datarow_x")
d3row.attr("id", `datarow_${r}`);
const linechart = d3row.select("svg.chartarea");
linechart
.append("path")
.datum(linedata()) // randomised - replace with your data
.attr("class", "line")
.attr("d", line);
// add other details
d3row.select(".label1").html(`Rand: ${Math.random().toFixed(2)}`);
d3row.select(".label2").html(`Rand: ${Math.random().toFixed(2)}`);
d3row.select(".label3").html(`Rand: ${Math.random().toFixed(2)}`);
// show tooltip
d3.select("#custom-tooltip")
.style("visibility", "visible");
}
});
leaf.on("mousemove", function(d) {
// update tooltip with mouse
d3.select("#custom-tooltip")
.style("top", (d3.event.pageY + 5) + "px")
.style("left", (d3.event.pageX + 5) + "px")
});
leaf.on("mouseout", function(d) {
// hide tooltip
d3.selectAll("#custom-tooltip")
.style("visibility", "hidden")
});
.leaf { width: 80px; height: 80px; fill: blue; }
.chartarea { width: 100px; height: 20px; }
.line { stroke: red; stroke-width: 1; fill: none; }
#custom-tooltip { position: absolute; z-index: 10; visibility: hidden; }
#custom-tooltip tr { background-color: #d6eeee; }
#tooltip-header { background-color: #eeD600; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg class="container">
<rect class="leaf"></rect>
</svg>
<div id="custom-tooltip">
<h3 id="tooltip-header">Header</h3>
<div>
<table>
</table>
</div>
</div>
<template id="tooltip-datarow">
<tr id="datarow_x">
<td class="label1">Label</td>
<td>
<svg class="chartarea"></svg>
</td>
<td class="txtright label2">123</td>
<td class="txtright label3">456</td>
</tr>
</template>
<script>
// random data for line
function linedata() {
return d3.range(10).map((_, i) => ({x: i, y: Math.floor(Math.random() * 10) + 1}));
}
// scales
const x = d3.scaleLinear().range([0, 100]).domain([0, 9]);
const y = d3.scaleLinear().range([0, 15]).domain([0, 9]);
// line generator
const line = d3
.line()
.x((d) => x(d.x))
.y((d) => y(d.y));
</script>