画布元素中的字母间距

时间:2012-01-21 12:19:47

标签: javascript css html5 canvas letter-spacing

这个问题几乎都说明了。我一直在寻找并开始担心这是不可能的。

我有这个画布元素,我正在绘制文字。我想设置类似于CSS letter-spacing属性的字母间距。我的意思是在绘制字符串时增加字母之间的像素数量。

我绘制文本的代码是这样的,ctx是画布上下文变量。

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);

我尝试在绘图之前添加ctx.letterSpacing = "2px";,但无济于事。有没有办法只用一个简单的设置来做到这一点,或者我是否必须创建一个函数来单独绘制每个字符的间距?

11 个答案:

答案 0 :(得分:33)

您不能设置字母间距属性,但是您可以通过在字符串中的每个字母之间插入各种空格之一来在画布中实现更宽的字母间距。例如

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);

这将在每个字母之间插入hair space

使用8201(而不是8202)将插入稍宽thin space

有关更多空白区域选项,请参阅this list of Unicode Spaces

此方法可以帮助您比手动定位每个字母更容易保留字体的字距,但是您不能以这种方式收紧字母间距。

答案 1 :(得分:18)

我不确定 是否有效(按规格),但在某些浏览器(Chrome)中,您可以在letter-spacing元素上设置<canvas> CSS属性本身,它将应用于在上下文中绘制的所有文本。 (适用于Chrome v56,在Firefox v51或IE v11中不起作用。)

请注意,在Chrome v56中,您必须在每次更改letter-spacing样式后重新获取画布2d上下文(并重新设置您关注的任何值);间距似乎被烘焙到你得到的第二个上下文中。

示例: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>


原创,跨浏览器回答:

这是不可能的; HTML5 Canvas没有HTML中所有CSS的文本转换功能。我建议你应该为每种用法结合适当的技术。使用与Canvas分层的HTML,甚至是SVG,每个都做它最擅长的事情。

另请注意,使用自定义偏移量'滚动自己'绘制每个字符 - 对于大多数字体都会产生不良结果,因为有字母字距调整对和像素对齐字体提示。

答案 2 :(得分:6)

您不能将字母间距设置为Canvas上下文的属性。您只能通过手动间距来实现效果,抱歉。 (如图所示,手动将每个字母的x增加一些像素数量)

对于记录,您可以使用ctx.font设置一些文本属性,但字母间距不是其中之一。您可以设置的那些是:“font-style font-variant font-weight font-size / line-height font-family”

例如,您可以在技术上写ctx.font = "bold normal normal 12px/normal Verdana"(或任何遗漏任何内容),它将正确解析。

答案 3 :(得分:4)

不正确。你可以在css中的canvas元素中添加letter-spacing属性,它可以很好地工作。无需复杂的解决方法。我刚刚在我的画布项目中想出来了。即:     帆布{         宽度:480px;         身高:350px;         保证金:30px auto 0;         填充:15px 0 0 0;         背景:粉红色;         显示:块;         边框:2px棕色;         字母间距:0.1em;     }

答案 4 :(得分:3)

这里有一些coffeescript,允许你像这样设置字符串

tctx = tcanv.getContext('2d')
tctx.kerning = 10
tctx.fillStyle = 'black'
tctx.fillText 'Hello World!', 10, 10

支持代码是:

_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

javascript将是

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;
  });
};

答案 5 :(得分:3)

允许使用&#39;字母对齐&#39;等等,我写了以下内容。它应该考虑到这一点,粗略测试表明它确实如此。如果您对此有任何意见,那么我会指出您对该主题的问题(Adding Letter Spacing in HTML Canvas

基本上它使用measureText()来获取整个字符串的宽度,然后删除字符串的第一个字符并测量剩余字符串的宽度,并使用差值来计算正确的位置 - 因此考虑到字距对等。有关更多伪代码,请参阅给定链接。

这是HTML:

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

以下是代码:

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 != "");
}

演示/眼球测试代码:

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);

理想情况下,我会在其上投掷多个随机字符串并逐个像素地进行比较。我也不确定Verdana的默认字距是多么好,虽然我明白它比Arial更好 - 其他字体的建议值得感激接受。

所以...到目前为止看起来不错。事实上它看起来很完美。 仍然希望有人能指出这个过程中的任何缺陷。

与此同时,我会把这个放在别人面前,看看他们是否在寻找解决方案。

答案 6 :(得分:0)

实际上字母间距概念画布不支持。

所以我用javascript来做这件事。

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

OR

var sample_text = "Praveen Chelumalla";
var text = sample_text.split("").join(" ");

答案 7 :(得分:0)

我不认识其他人,但是我通过增加所写文本的y值来调整行距。我实际上是用空格分割字符串,然后将每个单词逐个踢到循环内的一行中。我使用的数字基于默认字体。如果您使用其他字体,则可能需要调整这些数字。

// 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;
}               

答案 8 :(得分:0)

这是基于 James Carlyle-Clarke 之前回答的另一种方法。它还可以让您左、中、右对齐文本。

export function fillTextWithSpacing(context, text, x, y, spacing, textAlign) {
    const totalWidth = context.measureText(text).width + spacing * (text.length - 1);
    switch (textAlign) {
        case "right":
            x -= totalWidth;
            break;
        case "center":
            x -= totalWidth / 2;
            break;
    }
    for (let i = 0; i < text.length; i++) {
        let char = text.charAt(i);
        context.fillText(char, x, y);
        x += context.measureText(char).width + spacing;
    }
}

答案 9 :(得分:-1)

画布中的字母间距是支持的,我使用了这个

canvas = document.getElementById('canvas');
canvas.style.letterSpacing = '2px';

答案 10 :(得分:-4)

我用:

ctx.font = "32px Tahoma";//set font
ctx.scale(0.75,1);//important! the scale
ctx.fillText("LaFeteParFete test text", 2, 274);//draw
ctx.setTransform(1,0,0,1,0,0);//reset transform