我有一个“词云”,目前正在尝试将鼠标悬停在文本元素上时制作小工具提示。然而,“mouseover”、“mousemove”和“mouseout”事件作用于文本元素的“边界框”。举个例子,在下图中,“merry”这个词占据了“last”的相当大一部分,这会导致混乱,因为当鼠标悬停在“last”上时,工具提示会显示“merry”。
我不太确定如何继续这里。我试过了
text {
pointer-events: painted;
}
/* and also */
svg {
pointer-events: painted;
}
来自 MDN 但这些不起作用 - 相同的行为仍然存在。
这就是我的工具提示代码的样子,但我认为这对于这个问题并不重要:
d3.selectAll("text")
.on("mouseover", (e) => {
tooltip
.style("opacity", 1)
.html(`<p>${e.target.textContent} used ${e.target.dataset.value} times</p>`)
.style("left", e.pageX + "px")
.style("top", e.pageY - 25 + "px");
})
.on("mousemove", (e) => {
tooltip.style("left", e.pageX + "px").style("top", e.pageY - 25 + "px");
})
.on("mouseout", () => {
tooltip.style("opacity", 0);
});
下面您自己尝试一下。将鼠标悬停在“最后”的下半部分并上下摇动鼠标。即使您的鼠标始终处于“last”状态,控制台也会来回打印“merry”和“last”。
document.querySelectorAll("text").forEach((node) => {
node.onmouseover = (e) => console.log(e.target.textContent);
})
<svg xmlns="http://www.w3.org/2000/svg" width="900" height="900">
<g transform="translate(450, 450)">
<text data-value="63" text-anchor="middle" transform="translate(34, -59) rotate(0)" style="font-size: 507px; font-family: Impact; fill: #F12046">last</text>
<text data-value="38" text-anchor="middle" transform="translate(15, 137) rotate(0)" style="font-size: 307px; font-family: Impact; fill: #009DDC">merry</text>
</g>
</svg>
如何让工具提示仅在鼠标悬停在 SVG 的“绘制”部分上时显示?
嗯...它并不完美,但我发现使用画布上下文并测量文本是相当准确的。这并不像我希望的那样,但至少它是一个合理的边界框。
这是我的代码,用于插入近似测量文本大小的矩形。
const ctx = document.createElement("canvas").getContext("2d")!;
document.querySelectorAll("text").forEach((text) => {
ctx.font = `${text.style.fontSize} ${text.style.fontFamily}`;
const metrics = ctx.measureText(text.textContent!);
const width = metrics.actualBoundingBoxRight - metrics.actualBoundingBoxLeft;
const height = metrics.actualBoundingBoxAscent + metrics.actualBoundingBoxDescent;
const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
rect.dataset.word = text.textContent!;
rect.dataset.value = text.dataset.value;
console.log(metrics);
rect.setAttributeNS(null, "x", (Number(text.dataset.x) - width / 2).toString());
rect.setAttributeNS(
null,
"y",
(Number(text.dataset.y) - height / 2 - (metrics as any).hangingBaseline / 2).toString()
);
rect.setAttributeNS(null, "width", width.toString());
rect.setAttributeNS(null, "height", (height * 1.1).toString());
rect.setAttributeNS(null, "fill", "rgba(0, 0, 0, 0.25)");
text.parentNode!.insertBefore(rect, text.nextSibling);
});
然后我只能监听矩形上的事件。有时单词的一部分仍然在边界框之外,但这比什么都不做要好得多。这是结果(矩形为灰色):
参考资料: