画布元素中的字母间距

Pin*_*kle 32 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);
Run Code Online (Sandbox Code Playgroud)

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

Yan*_*sky 35

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

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);
Run Code Online (Sandbox Code Playgroud)

这将在每个字母之间插入一个头发空间.

使用8201(而不是8202)将插入稍宽的薄空间

有关更多空白区域选项,请参阅此Unicode空间列表

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

  • 我喜欢这个解决方法.不幸的是,Chrome似乎平等地渲染所有Unicode空间. (3认同)
  • @flu现在似乎在Chrome 42中工作:http://codepen.io/anon/pen/dPwJjo (2认同)

Phr*_*ogz 19

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

请注意,在Chrome v56中,您必须在每次更改letter-spacing样式后重新获取画布2d上下文(并重新设置您关心的任何值); 间距似乎被烘焙到您获得的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);
};
Run Code Online (Sandbox Code Playgroud)
canvas { background:white }
canvas, label { display:block; width:400px; margin:0.5em auto }
Run Code Online (Sandbox Code Playgroud)
<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>
Run Code Online (Sandbox Code Playgroud)


原创,跨浏览器答案:

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

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

  • 这并不是说你可以自己滚动并完全完成,手动输入字距调整对的图表.我发现做`Math.sqrt(width_of_character + width_of_previous_char,3/4)`产生的间距对于大多数用途来说是相当准确的.当然,通过`context.measureText(myString [26]).width`可以找到字符的宽度.在这种情况下,26是字符串的第26个字母,因此应该循环 (5认同)
  • 遗憾的是它无法在屏幕外工作(当您使用“document.createElement('canvas');”时) (2认同)

Sim*_*ris 6

您不能将letter-spacing设置为Canvas上下文的属性.抱歉,你只能通过手动间距来达到效果.(如图所示,手动绘制每个字母,将x增加一些像素值)

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

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


Jam*_*rke 6

为了允许“字母字距调整对”等,我写了以下内容。它应该考虑到这一点,粗略的测试表明确实如此。如果您对此有任何评论,那么我会向您指出我关于该主题的问题(在 HTML Canvas 中添加字母间距

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

这是 HTML:

<canvas id="Test1" width="800px" height="200px"><p>Your browser does not support canvas.</p></canvas>
Run Code Online (Sandbox Code Playgroud)

这是代码:

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 != "");
}
Run Code Online (Sandbox Code Playgroud)

演示/眼球测试代码:

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);
Run Code Online (Sandbox Code Playgroud)

理想情况下,我会向它抛出多个随机字符串并逐个像素地进行比较。我也不确定 Verdana 的默认字距调整有多好,但我知道它比 Arial 更好 - 关于其他字体的建议尝试被感激地接受。

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

与此同时,我将把它放在这里供其他人看看他们是否正在寻找解决方案。