d3.js 在绘制文本之前计算文本的宽度

yeotifhr  于 12个月前  发布在  其他
关注(0)|答案(4)|浏览(177)

我想显示一个rect,旁边有一个text标签。rect的宽度应该延伸到svg容器的宽度,减去文本的宽度,这是动态的,可以是任何可变的长度。
JSFiddle

var text = 'Foobar';
var textWidth = 50; //how to calculate this?
var plotWidth = 400;
var barWidth = plotWidth-textWidth;

var plot = d3.select(container)
        .insert("svg")
        .attr('width', plotWidth)
        .attr('height', 50);

plot.append("rect")
    .style("fill", "steelblue")
    .attr("x", 0)
    .attr("width", barWidth)
    .attr("y", 0)
    .attr("height", 50);

plot.append("text")
    .attr("x", barWidth)
    .attr("y", 28)
    .text(text);

在绘制之前,如何使用D3计算文本的宽度?或者,我如何以其他方式定位和调整取决于可变长度文本的尺寸的元素?

r6hnlfcb

r6hnlfcb1#

我知道你问了关于D3的问题,但这可能是解决你问题的一个原生解决方案。
HTML5 canvas 2D context有一些内置的功能来测量文本。你也许可以利用它来测量SVG等其他API的文本。如果它不是100%准确,那么它肯定与正确答案成比例。

var BrowserText = (function () {
    var canvas = document.createElement('canvas'),
        context = canvas.getContext('2d');

    /**
     * Measures the rendered width of arbitrary text given the font size and font face
     * @param {string} text The text to measure
     * @param {number} fontSize The font size in pixels
     * @param {string} fontFace The font face ("Arial", "Helvetica", etc.)
     * @returns {number} The width of the text
     **/
    function getWidth(text, fontSize, fontFace) {
        context.font = fontSize + 'px ' + fontFace;
        return context.measureText(text).width;
    }

    return {
        getWidth: getWidth
    };
})();

// Then call it like this:
console.log(BrowserText.getWidth('hello world', 22, 'Arial')); // 105.166015625
console.log(BrowserText.getWidth('hello world', 22)); // 100.8154296875
5uzkadbs

5uzkadbs2#

我在一个复杂的图表中遇到了类似的问题,元素和文本之间有很多交互,这需要在显示任何元素之前知道文本宽度**。
我采取了创建一个虚拟文本来获取其宽度并立即删除它。注意each中函数的最后一行代码。

var textData = ['a', 'b', 'c']    // your text here

var textWidth = []

svg.append('g')
    .selectAll('.dummyText')     // declare a new CSS class 'dummyText'
    .data(textData)
    .enter()                     // create new element
    .append("text")              // add element to class
    .attr("font-family", "sans-serif")
    .attr("font-size", "14px")
    //.attr("opacity", 0.0)      // not really necessary
    .text(function(d) { return d})
    .each(function(d,i) {
        var thisWidth = this.getComputedTextLength()
        textWidth.push(thisWidth)
        this.remove() // remove them just after displaying them
    })

console.log(textWidth) // this array contains the on-screen width of each text element
aurhwmvo

aurhwmvo3#

下面是一个基于getBBox().widthgetComputedTextLength()的工作示例:

编辑:由于性能问题,将答案更新为使用getComputedTextLength(参见评论)

http://jsfiddle.net/henbox/jzkj29nv/27/

var text_element = plot.select("text");
var textWidth = text_element.node().getComputedTextLength()

我还切换到使用text-anchor: end; CSS的文本,所以你不需要计算文本的开始位置(只是在最后传递)

svmlkihl

svmlkihl4#

也可以在d3 selection.attr中使用getComputedTextLength()getBBox()
下面是一个基于text内容大小计算xy坐标的最小示例:

d3.select('svg').append('text')
  .text('some text')
    .attr('x', function() {return this.getComputedTextLength();})
    .attr('y', (d, i, nodes) => nodes[i].getBBox().height);
svg {
  border: 1px dashed black;
}

svg text {
  fill: black;
  text-anchor: start;
  dominant-baseline: hanging;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="200" height="100"></svg>

请注意,使用了两种函数样式,以说明以下内容:
在普通的function ()中,可以使用nodes[i]this访问当前节点。
但是,在arrow functions=>中,我们必须使用nodes[i],因为使用this会引发错误:

this.getComputedTextLength is not a function

另见d3-selection issue 97和this SO answer

相关问题