Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Letter spacing in canvas element

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?

like image 986
Pinpickle Avatar asked Jan 21 '12 12:01

Pinpickle


People also ask

How do I add spacing between letters in Canva?

Text spacing: Tap Spacing and drag the Letter spacing and Line spacing sliders to your preferred value.

What is adjusting space between letters?

Kerning adjusts the spacing between any two letters while tracking affects spacing for more than two letters.

Should I use REM for letter-spacing?

Using font-relative values (em or rem) is recommended, so that the letter-spacing will increase or decrease by an appropriate amount if the font-size is changed. The length value is added to (or subtracted from) the browsers default spacing (which is based on the font metrics.)


2 Answers

You can't set the letter spacing property, but you you can accomplish wider letter spacing in canvas by inserting one of the various white spaces in between every letter in the string. For instance

ctx.font = "3em sheepsans"; ctx.textBaseline = "middle"; ctx.textAlign = "center"; ctx.fillStyle = "rgb(255, 255, 255)"; var ctext = "Blah blah text".split("").join(String.fromCharCode(8202)) ctx.fillText(ctext, 1024 / 2, 768 / 2); 

This will insert a hair space between every letter.

Using 8201 (instead of 8202) will insert the slightly wider thin space

For more white space options, see this list of Unicode Spaces

This method will help you to preserve the font's kerning much more easily than manually positioning each letter, however you wont be able to tighten your letter spacing this way.

like image 63
Yanofsky Avatar answered Sep 24 '22 13:09

Yanofsky


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.

like image 28
Phrogz Avatar answered Sep 24 '22 13:09

Phrogz