0

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

我最近重新发现了这个脚本,并使用翻译、旋转和 fillText() 重新编写了它。但是,这不尊重字符宽度,也没有紧缩(看起来很糟糕)。

大约 2009 年的原版(不完美,但还可以):

使用 mozTextAlongPath 的原始笑人

当前版本:

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

固定的

4

2 回答 2

1

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

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

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

您可能希望将间距修饰符应用于整个单词,而不是单个字母。为此,请measureText()在去除空格的句子上使用以获得字母的宽度(并扩展空格的总宽度)。

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

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

于 2012-12-12T06:40:25.700 回答
0

所以测量文本很好,我最终做的是Math.pow(measureText + measureTextOfLastChar, 3 / 4)

出于某种原因,当前和前一个字符的宽度之和的平方根使一些间距太窄,而且根本没有平方根,这也很糟糕,但是 Math.pow(sum, 3/4) 对于某些理性创造了很大的比例。这是代码(在咖啡脚本中)

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)

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

一切都可以改变,除了保持 Math.pow(sum, 3/4) 在循环中,因为这是我比我在网上找到的其他东西做得更好的部分。

于 2013-03-08T21:06:35.357 回答