SVG 文本元素的边界框更准确

问题描述 投票:0回答:1

我有一个“词云”,目前正在尝试将鼠标悬停在文本元素上时制作小工具提示。然而,“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 的“绘制”部分上时显示?

javascript html css svg d3.js
1个回答
0
投票

嗯...它并不完美,但我发现使用画布上下文并测量文本是相当准确的。这并不像我希望的那样,但至少它是一个合理的边界框。

这是我的代码,用于插入近似测量文本大小的矩形。

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);
});

然后我只能监听矩形上的事件。有时单词的一部分仍然在边界框之外,但这比什么都不做要好得多。这是结果(矩形为灰色):

参考资料:

© www.soinside.com 2019 - 2024. All rights reserved.