Letter spacing in canvas element

2019-02-02 21:02发布

The question says it all pretty much. I've been searching around and starting to worry that it's impossible.

I've got this canvas element that I'm drawing text to. I want to set the letter spacing similar to the CSS letter-spacing attribute. By that I mean increasing the amount of pixels between letters when a string is drawn.

My code for drawing the text is like so, ctx is the canvas context variable.

ctx.font = "3em sheepsans";
ctx.textBaseline = "middle";
ctx.textAlign = "center";
ctx.fillStyle = "rgb(255, 255, 255)";
ctx.fillText("Blah blah text", 1024 / 2, 768 / 2);

I've tried adding ctx.letterSpacing = "2px"; before the drawing but with no avail. Is there a way to do this just with a simple setting, or will I have to make a function to individually draw each character with the spacing in mind?

9条回答
啃猪蹄的小仙女
2楼-- · 2019-02-02 21:10

Letter spacing in canvas IS SUPPORTED, I used this

canvas = document.getElementById('canvas');
canvas.style.letterSpacing = '2px';
查看更多
该账号已被封号
3楼-- · 2019-02-02 21:13

To allow for 'letter kerning pairs' and the like, I've written the following. It should take that into account, and rough testing suggests it does. If you have any comments on it then I would point you to my question on the subject (Adding Letter Spacing in HTML Canvas)

Basically it uses measureText() to get the width of the whole string, and then removes the first character of the string and measures the width of the remaining string, and uses the difference to calculate the correct positioning - thus taking into account kerning pairs and the like. See the given link for more pseudocode.

Here's the HTML:

<canvas id="Test1" width="800px" height="200px"><p>Your browser does not support canvas.</p></canvas>

Here's the code:

this.fillTextWithSpacing = function(context, text, x, y, spacing)
{
    //Start at position (X, Y).
    //Measure wAll, the width of the entire string using measureText()
    wAll = context.measureText(text).width;

    do
    {
    //Remove the first character from the string
    char = text.substr(0, 1);
    text = text.substr(1);

    //Print the first character at position (X, Y) using fillText()
    context.fillText(char, x, y);

    //Measure wShorter, the width of the resulting shorter string using measureText().
    if (text == "")
        wShorter = 0;
    else
        wShorter = context.measureText(text).width;

    //Subtract the width of the shorter string from the width of the entire string, giving the kerned width of the character, wChar = wAll - wShorter
    wChar = wAll - wShorter;

    //Increment X by wChar + spacing
    x += wChar + spacing;

    //wAll = wShorter
    wAll = wShorter;

    //Repeat from step 3
    } while (text != "");
}

Code for demo/eyeball test:

element1 = document.getElementById("Test1");
textContext1 = element1.getContext('2d');

textContext1.font = "72px Verdana, sans-serif";
textContext1.textAlign = "left";
textContext1.textBaseline = "top";
textContext1.fillStyle = "#000000";

text = "Welcome to go WAVE";
this.fillTextWithSpacing(textContext1, text, 0, 0, 0);
textContext1.fillText(text, 0, 100);

Ideally I'd throw multiple random strings at it and do a pixel by pixel comparison. I'm also not sure how good Verdana's default kerning is, though I understand it's better than Arial - suggestions on other fonts to try gratefully accepted.

So... so far it looks good. In fact it looks perfect. Still hoping that someone will point out any flaws in the process.

In the meantime I will put this here for others to see if they are looking for a solution on this.

查看更多
戒情不戒烟
4楼-- · 2019-02-02 21:15

Not true. You can add letter-spacing property to the canvas element in css and it works perfectly. No need for complicated workarounds. I just figured it out right now in my canvas project. i.e.: canvas { width: 480px; height: 350px; margin: 30px auto 0; padding: 15px 0 0 0; background: pink; display: block; border: 2px dashed brown; letter-spacing: 0.1em; }

查看更多
\"骚年 ilove
5楼-- · 2019-02-02 21:17

I'm not sure if it should work (per specs), but in some browsers (Chrome) you can set the letter-spacing CSS property on the <canvas> element itself, and it will be applied to all text drawn on the context. (Works in Chrome v56, does not work in Firefox v51 or IE v11.)

Note that in Chrome v56 you must re-get the canvas 2d context (and re-set any values you care about) after each change to the letter-spacing style; the spacing appears to be baked into the 2d context that you get.

Example: https://jsfiddle.net/hg4pbsne/1/

var inp = document.querySelectorAll('input'),
    can = document.querySelector('canvas'),
    ctx = can.getContext('2d');
    can.width = can.offsetWidth;

[].forEach.call(inp,function(inp){ inp.addEventListener('input', redraw, false) });
redraw();

function redraw(){
  ctx.clearRect(0,0,can.width,can.height);
  can.style.letterSpacing = inp[0].value + 'px';

  ctx = can.getContext('2d');
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  ctx.font = '4em sans-serif';
  ctx.fillText('Hello', can.width/2, can.height*1/4);
  
  can.style.letterSpacing = inp[1].value + 'px';
  ctx = can.getContext('2d');
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  ctx.font = '4em sans-serif';
  ctx.fillText('World', can.width/2, can.height*3/4);
};
canvas { background:white }
canvas, label { display:block; width:400px; margin:0.5em auto }
<canvas></canvas>
<label>hello spacing: <input type="range" min="-20" max="40" value="1" step="0.1"></label>
<label>world spacing: <input type="range" min="-20" max="40" value="1" step="0.1"></label>


Original, cross-browser answer:

This is not possible; the HTML5 Canvas does not have all the text-transformation power of CSS in HTML. I would suggest that you should combine the appropriate technologies for each usage. Use HTML layered with Canvas and perhaps even SVG, each doing what it does best.

Note also that 'rolling your own'—drawing each character with a custom offset—is going to produce bad results for most fonts, given that there are letter kerning pairs and pixel-aligned font hinting.

查看更多
Fickle 薄情
6楼-- · 2019-02-02 21:17

Actually letter spacing concept canvas is not supporting.

So i used javascript to do this.

var value = $('#sourceText1').val().split("").join(" ");

OR

var sample_text = "Praveen Chelumalla";
var text = sample_text.split("").join(" ");
查看更多
聊天终结者
7楼-- · 2019-02-02 21:26

You can't set letter-spacing as a property of the Canvas context. You can only achieve the effect by doing manual spacing, sorry. (As in, drawing each letter manually increasing the x by some pixel amount on each)

For the record, you can set a few text properties by using ctx.font but letter-spacing is not one of them. The ones you can set are: "font-style font-variant font-weight font-size/line-height font-family"

For instance you can technically write ctx.font = "bold normal normal 12px/normal Verdana" (or any omission of any of those) and it will parse correctly.

查看更多
登录 后发表回答