2012-12-12 24 views
0

幾年前,我爲APNGedit製作了一個Javascript腳本來繪製笑男人的標誌。它使用現在已不存在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

+0

看看在這裏提到的raphael.js:http://stackoverflow.com/questions/2612764/attach-text-on-path-in-raphael – DhruvPathak

回答

1

很顯然,這是沒有什麼困難的地方在電弧本身的字母(只是居中對齊底部的圓圈)。但是,正如你所指出的那樣,問題在於核心問題。

幸運的是,我們有measureText(),它可以告訴我們字母的寬度,因此可以使用什麼字體。

圓的圓周只是2πr,文字的總寬度是ctx.measureText("Your text here");。獲得這兩個值的比率,你會發現你需要多少空間或壓縮你的話。

您可能希望將間距修飾詞應用於整個單詞,而不是單個字母。爲此,請在句子上使用measureText(),並去掉空格以獲取字母的寬度(以及擴展空格的總寬度)。

現在你需要繪製每個單詞的去向。再次使用measureText()來查找每個單詞的寬度並在圓上繪製其中心點,並在每個單詞之間添加一部分總空間值。現在在每個單獨的字母上使用measureText(),並在正確的位置繪製完成字距。

一切都很好,你應該有一個完美的文字圈。

+0

這是一個更好的主意,做char它通過字符,我嘗試兩種方式。從我的測試中,我使用的任何字符實際上都沒有字距,只是可變的寬度。我添加了「AVA」,並設法通過測量當前字符和下一個字符,然後計算出重疊來實現字距調整。我會發布我的完整解決方案,並且現在接受你的答案,因爲'measureText()'和比率就是我所需要的:) – Annan

0

所以測量文本是很好的,我落得這樣做,是Math.pow(measureText + measureTextOfLastChar, 3/4)

出於某種原因,目前和前一個字符的寬度之和的平方根取得了一定的間距太瘦了,沒有一個正方形根本,它也不好,但由於某種原因Math.pow(總和,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, [email protected](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(sum,3/4)之外,一切都可以改變,因爲這是我比在網上找到的其他東西更好的部分。