Getting the size of text without using the boundin

2019-02-15 21:07发布

问题:

I am trying to create some buttons with text in javascript using the Rahpael library. I would like to know the size of the styled text, before drawing to avoid that so I can create proper background (the button). Also I would like to avoid drawing the text outside of the canvas/paper (the position of the text is the position of its center).

I can use Raphaels getBBox() method, but I have to create (draw) the text in first place to do this. So I draw text to get the size to be able to draw it on the right position. This is ugly. So I am searching for some general method to estimate the metrics of styled text for given font, font-size, family ...

There is a possibility to do this using the HTML5 canvas http://www.html5canvastutorials.com/tutorials/html5-canvas-text-metrics/ but Raphael do not use canvas. Is there any possibility to get the text metrics using Raphael or plain Javascript?

回答1:

I know it seems sloppy, but you should have no problem drawing the text, measuring it, then drawing the button and moving the label. To be safe, just draw it off the screen:

var paper = Raphael(0, 0, 500, 500);

var text = paper.text(-100, -100, "My name is Chris");

//outputs 80 12
console.log(text.getBBox().width, text.getBBox().height);

If this REALLY offends your sensibilities, however -- and I would understand! -- you can easily generate an object to remember the width off each character for a given font:

var paper = Raphael(0, 0, 500, 500),
    alphabet = "abcdefghijklmnopqrstuvwxyz";
    font = "Arial",
    charLengths = {},
    ascii_lower_bound = 32,
    ascii_upper_bound = 126;

document.getElementById("widths").style.fontFamily = font;

for (var c = ascii_lower_bound; c <= ascii_upper_bound; c += 1) {
    var letter = String.fromCharCode(c);    
    var L = paper.text(-50, -50, letter).attr("font-family", font);
    charLengths[letter] = L.getBBox().width;
}

//output

for (var key in charLengths) if (charLengths.hasOwnProperty(key)) {
    var row = document.createElement("tr");
    row.innerHTML = "<td>" + key + "</td><td>" + charLengths[key] + "</td>";    
    document.getElementById("widths").appendChild(row);    
}


回答2:

There are a couple of ways to slice up this cat. Two obvious ones come readily to mind.

Out-of-view Ruler Technique

This one's the easiest. Just create a text element outside of the canvas's viewBox, populate it with your font information and text, and measure it.

// set it up -- put the variable somewhere globally or contextually accessible, as necessary
var textRuler = paper.text( -10000, -10000, '' ).attr( { fill: 'none', stroke: 'none' } );

function getTextWidth( text, fontFamily, fontSize )
{
    textResult.attr( { text: text, 'font-family': fontFamily, 'font-size': fontSize } );
    var bbox = textResult.getBBox();
    return bbox.width;
}

It's not elegant by any stretch of the imagination. But it will do you want with relatively little overhead and no complexity.

Cufonized Font

If you were willing to consider using a cufonized font, you could calculate the size of a given text string without needing to mess with the DOM at all. In fact, this is probably approximately what the canvas's elements measureText method does behind the scenes. Given an imported font, you would simply do something like this (consider this protocode!)

//  font should be the result of a call to paper.[getFont][2] for a cufonized font
function getCufonWidth( text, font, fontSize )
{
    var textLength = text.length, cufonWidth = 0;
    for ( var i = 0; i < textLength; i++ )
    {
        var thisChar = text[i];
        if ( ! font.glyphs[thisChar] || ! font.glyphs[thisChar].w )
            continue;  //  skip missing glyphs and/or 0-width entities
        cufonWidth += font.glyphs[thisChar].w / font.face['units-per-em'] * fontSize;
    }
    return cufonWidth;
}

I really like working with cufonized fonts -- in terms of their capacity for being animated in interesting ways, they are far more useful than text. But this second approach may be more additional complexity than you need or want.