3

I have a small problem using Canvas API in HTML5. I have a text that I have to show on a canvas in an html page.

The text example can be "This is an Italic word". So what I have to do is display this text that I take from the database, but to make just one word from the sentence Italic. So I have to display the text like this:

"This is an Italic word"

So my code looks like this:

var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');

//text
context.fillStyle  = "#000000";
context.font = "20px Sans-Serif";
context.textBaseline = "top";
context.fillText  ("This is an ", 195, 80 );

context.font = "italic 20px Sans-Serif";
context.fillText  ("Italic", 285, 80 );
context.font = "20px Sans-Serif";
context.fillText  ("text", 330, 80 );

So this code is not actually dynamic and I have all the time to know the correct pixel where to start the rest of the sentence. Does anybody have an idea how can I solve this issue dynamically and clean ?

KyleMit
  • 30,350
  • 66
  • 462
  • 664

2 Answers2

5

To achieve some kind of flexibility, you have to decide of a convention inside your text that will tell that the style changed.
And also, you'll have to use measureText to be able to fillText separate 'runs' of the text, each run using the right style, measureText(thisRun).width will give you the size in pixels of the current run.
Then what you need is to draw separate text runs, each in its own style, then move on the 'cursor' based on the return value of measureText.

For a quick example, i took as styling convention "§r" = regular text, "§i" = italic, "§b" = bold, "§l" = lighter, so the string :

var text = "This is an §iItalic§r, a §bbold§r, and a §llighter§r text";

will output as :

enter image description here

fiddle is here :

http://jsfiddle.net/gamealchemist/32QXk/6/

The code is :

var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');

// marker used in the text to mention style change
var styleMarker = '§';

// table code style --> font style
var styleCodeToStyle = {
    r: '',
    i: 'italic',
    b: 'bold',
    l: 'lighter'
};

// example text
var text = "This is an §iItalic§r, a §bbold§r, and a §llighter§r text";

// example draw
drawStyledText(text, 20, 20, 'Sans-Serif', 20);

// example text 2 : 

var text2 = "This is a text that has separate styling data";
var boldedWords = [ 3, 5, 8 ];
var italicWords = [ 2, 4 , 7];

var words = text2.split(" ");
var newText ='';

for (var i=0; i<words.length; i++) {
  var thisWord = words[i];
  if (boldedWords.indexOf(i)!=-1) 
      newText += '§b' + thisWord + '§r ';
   else if (italicWords.indexOf(i)!=-1) 
      newText += '§i' + thisWord + '§r ';
    else 
       newText += thisWord + ' ';
}

drawStyledText(newText, 20, 60, 'Sans-Serif', 20);


function drawStyledText(text, x, y, font, fontSize) {
    // start with regular style
    var fontCodeStyle = 'r';
    do {
        // set context font
        context.font = buildFont(font, fontSize, fontCodeStyle);
        // find longest run of text for current style
        var ind = text.indexOf(styleMarker);
        // take all text if no more marker
        if (ind == -1) ind = text.length;
        // fillText current run
        var run = text.substring(0, ind);
        context.fillText(run, x, y);
        // return if ended
        if (ind == text.length) return;
        // move forward
        x += context.measureText(run).width;
        // update current style
        fontCodeStyle = text[ind + 1];
        // keep only remaining part of text
        text = text.substring(ind + 2);
    } while (text.length > 0)
}


function buildFont(font, fontSize, fontCodeStyle) {
    var style = styleCodeToStyle[fontCodeStyle];
    return style + ' ' + fontSize + 'px' + ' ' + font;
}

enter image description here

GameAlchemist
  • 18,995
  • 7
  • 36
  • 59
  • This is a good solution. But in my case it is a little bit harder to place those styling convention. Because I have in a table from the database sentences and in another table the index of the word which should be bold. So I really like your solution, but I think it doesn't suit entirely my problem. Thank you! – The user without number Jun 11 '14 at 15:08
  • Ok, thanks, but you didn't mention this constraint. It took a few seconds to adapt the code, see my edit and the updated fiddle. – GameAlchemist Jun 11 '14 at 15:21
0

Canvas can measure the width of text in the current font using context.measureText

A Demo: http://jsfiddle.net/m1erickson/S99Zv/

enter image description here

So you can create a function that draws your text and returns the length of text it just drew:

function drawText(text,style,x,y){

    if(ctx.font!==style){ctx.font=style;}

    ctx.fillText(text,x,y);

    return(ctx.measureText(text).width);

}

Then just accumulate the return values to know the x,y of your next block of text:

var style1="20px Sans-Serif";
var style2="italic 20px Sans-Serif";

var accumWidth=30;

accumWidth+=drawText("This is an ",style1,accumWidth,50);
accumWidth+=drawText("italic ",style2,accumWidth,50);
accumWidth+=drawText("word",style1,accumWidth,50);
markE
  • 102,905
  • 11
  • 164
  • 176