好看的电弧文字?

时间:2012-12-12 06:31:51

标签: javascript html5-canvas kerning

几年前,我为APNGedit制作了一个Javascript脚本来绘制Laughing Man徽标。它使用了现已解散的mozTextAlongPath

我最近重新发现了这个脚本,并使用翻译,旋转和fillText()重新编写它。但是,这不会影响字符宽度,也不会被调整(看起来很糟糕)。

原作大约2009年(不完美,但没关系):

original laughing man using mozTextAlongPath

当前版本:

new version using fillText

如何在HTML5画布上以弧形绘制文字并使其看起来不错?


解决方案代码基于Kolink的回答:

ctx.fillStyle = primaryColor;
ctx.font = fontSize + 'px ' + fontFamily;

var textWidth = ctx.measureText(text).width,
    charRotation = 0,
    character, charWidth, nextChar, nextWidth, bothWidth, kern, extraRotation, charSegment;

for (var i=0, l=text.length; i<l; i++) {
    character = nextChar || text[i];
    charWidth = nextWidth || ctx.measureText(character).width;

    // Rotate so the letter base makes a circle segment instead of a tangent
    extraRotation = (Math.PI/2) - Math.acos((charWidth/2) / radius);

    ctx.save();
    ctx.translate(radius, h/2);
    ctx.rotate(charRotation);
    ctx.translate(0, -textRadius);
    ctx.rotate(extraRotation);
    ctx.fillText(character,0,0);
    ctx.restore();

    nextChar = text[i+1] || '';
    nextWidth = ctx.measureText(nextChar).width;

    bothWidth = ctx.measureText(character+nextChar).width;
    kern = bothWidth - charWidth - nextWidth;

    charSegment = (charWidth+kern) / textWidth; // percent of total text size this takes up
    charRotation += charSegment * (Math.PI*2);
}

fixed

2 个答案:

答案 0 :(得分:1)

显然,在圆弧上放置字母并不困难(只需将中心底部对准圆圈)。但是,正如您所指出的,问题是字距调整。

幸运的是,我们有measureText(),它可以告诉我们字母的宽度,以及使用什么字距。

圆圈的周长只是2πr,文字的总宽度为ctx.measureText("Your text here");。获得这两个值的比率,你会发现你需要多少空间或压缩你的单词。

您可能希望将间距修改器应用于整个单词,而不是单个字母。要执行此操作,请在句子上使用measureText()并删除空格以获取字母的宽度(以及扩展名为空格的总宽度)。

现在你需要绘制每个单词的位置。再次使用measureText()查找每个单词的宽度并在圆上绘制其中心点,在每个单词之间添加一部分总空间值。现在在每个单独的字母上使用measureText()并在正确的位置绘制它以获得完美的字距调整。

一切顺利,你应该有一个完美间隔的文字圈。

答案 1 :(得分:0)

因此衡量文本是好的,我最终做的是Math.pow(measureText + measureTextOfLastChar, 3 / 4)

由于某种原因,当前和前一个字符的宽度之和的平方根使得一些间距太粗,并且根本没有平方根,也使它变坏,但是Math.pow(sum,3/4) )由于某种原因创造了一个很大的比例。下面是代码(在coffeescript中)

CanvasRenderingContext2D::fillTextCircle = (str, centerX, centerY, radius, angle) ->
  len = str.length
  s = undefined
  @save()
  @translate centerX, centerY
  @rotate - (1 + 1 / len) * angle / 2
  n = 0

  prevWidth = 0
  while n < len
    thisWidth = @measureText(str[n]).width
    @rotate angle / len * Math.pow(thisWidth + prevWidth, 3 / 4) / @measureText(str).width
    s = str[n]
    prevWidth = @measureText(str[n]).width
    @fillText s, -@measureText(str[n]).width / 2, -1 * radius
    n++
  @restore()

然后使用

调用它
context.fillTextCircle('hiya world', halfWidth, halfHeight, 95, 26)

我猜测并检查了一下,虽然我接受了计算4,所以我下意识地知道我在做什么。无论如何,它产生了完美的字符间距,没有Math.pow(sum_character_widths,3/4)

除了在循环中保留Math.pow(总和,3/4)之外,所有内容都可以更改,因为这是我在网上找到的其他内容所做的更好的部分。