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 t
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; }
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.
I don't know about other people but I have adjusted line spacing by increasing the y value on the text that I am writing. I'm actually splitting a string by spaces and kicking each word down a line inside a loop. The numbers i use are based on the default font. If you use a different font that these numbers may need to be adjusted.
// object holding x and y coordinates
var vectors = {'x':{1:100, 2:200}, 'y':{1:0, 2:100}
// replace the first letter of a word
var newtext = YOURSTRING.replace(/^\b[a-z]/g, function(oldtext) {
// return it uppercase
return oldtext.toUpperCase();
});
// split string by spaces
newtext = newtext.split(/\s+/);
// line height
var spacing = 10 ;
// initial adjustment to position
var spaceToAdd = 5;
// for each word in the string draw it based on the coordinates + spacing
for (var c = 0; c < newtext.length; c++) {
ctx.fillText(newtext[c], vectors.x[i], vectors.y[i] - spaceToAdd);
// increment the spacing
spaceToAdd += spacing;
}
here's some coffeescript that allows you to set kerning to your context like so
tctx = tcanv.getContext('2d')
tctx.kerning = 10
tctx.fillStyle = 'black'
tctx.fillText 'Hello World!', 10, 10
the supporting code is:
_fillText = CanvasRenderingContext2D::fillText
CanvasRenderingContext2D::fillText = (str, x, y, args...) ->
# no kerning? default behavior
return _fillText.apply this, arguments unless @kerning?
# we need to remember some stuff as we loop
offset = 0
_.each str, (letter) =>
_fillText.apply this, [
letter
x + offset + @kerning
y
].concat args # in case any additional args get sent to fillText at any time
offset += @measureText(letter).width + @kerning
The javascript would be
var _fillText,
__slice = [].slice;
_fillText = CanvasRenderingContext2D.prototype.fillText;
CanvasRenderingContext2D.prototype.fillText = function() {
var args, offset, str, x, y,
_this = this;
str = arguments[0], x = arguments[1], y = arguments[2], args = 4 <= arguments.length ? __slice.call(arguments, 3) : [];
if (this.kerning == null) {
return _fillText.apply(this, arguments);
}
offset = 0;
return _.each(str, function(letter) {
_fillText.apply(_this, [letter, x + offset + _this.kerning, y].concat(args));
offset += _this.measureText(letter).width + _this.kerning;
});
};
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.
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.
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.