web-dev-qa-db-ja.com

canvas要素の文字間隔

質問は、ほとんどすべてを言います。私は周りを探していて、それが不可能であることを心配し始めました。

私はテキストを描画しているこのキャンバス要素を持っています。 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";図面の前ですが、何もありません。簡単な設定だけでこれを行う方法はありますか、または間隔を考慮して各文字を個別に描画する関数を作成する必要がありますか?

33
Pinpickle

それが動作するかどうかはわかりません(仕様に従って)が、一部のブラウザー(Chrome)ではletter-spacing CSSプロパティを設定できます<canvas>要素自体に適用され、コンテキストに描画されたすべてのテキストに適用されます。 (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);
};
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を使用して、それぞれが最も効果を発揮します。

また、文字のカーニングペアとピクセル配置のフォントヒントがある場合、「独自のロール」(各文字をカスタムオフセットで描画)を行うと、ほとんどのフォントで悪い結果が生じることにも注意してください。

19
Phrogz

文字間隔プロパティを設定することはできませんが、文字列内のすべての文字の間にさまざまな空白の1つを挿入することにより、キャンバスでより広い文字間隔を実現できます。例えば

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

これにより、すべての文字の間に ヘアスペース が挿入されます。

(8202の代わりに)8201を使用すると、少し広い 薄いスペース が挿入されます

空白オプションの詳細については、 このUnicodeスペースのリスト を参照してください。

この方法は、各文字を手動で配置するよりもはるかに簡単にフォントのカーニングを維持するのに役立ちますが、この方法で文字の間隔を狭めることはできません。

34
Yanofsky

Canvasコンテキストのプロパティとしてletter-spacingを設定することはできません。手動の間隔を設定することによってのみ効果を得ることができます。 (のように、各文字を手動で描画して、xをそれぞれのピクセル量だけ増やします)

レコードについては、ctx.fontを使用していくつかのテキストプロパティを設定できますが、文字間隔はその1つではありません。あなたが設定できるものは次のとおりです: "font-style font-variant font-weight font-size/line-height font-family"

たとえば、技術的にctx.font = "bold normal normal 12px/normal Verdana"(またはそれらの省略)を書くと、正しく解析されます。

6
Simon Sarris

これはあなたのコンテキストにカーニングを設定できるようにするコーヒースクリプトです

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

「カーニングペア」などを考慮して、次のように記述しました。それはそれを考慮に入れるべきであり、大まかなテストはそれがそうであることを示唆しています。それについてコメントがあれば、私は件名についての私の質問を指摘します( HTMLキャンバスでの文字間隔の追加

基本的には、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よりも優れていることは理解しています。

だから...今のところそれはよさそうだ。実際、それは完璧に見えます。誰かがプロセスの欠陥を指摘してくれることを期待しています。

それまでの間、他の人がこれに対する解決策を探しているかどうかを確認するために、これをここに配置します。

違います。 cssのcanvas要素にletter-spacingプロパティを追加すると、完全に機能します。複雑な回避策は必要ありません。私は今、私のキャンバスプロジェクトでそれを理解しました。つまり:canvas {width:480px;高さ:350px;マージン:30px自動0;パディング:15px 0 0 0;背景:ピンク;表示ブロック;ボーダー:2px破線の茶色;文字間隔:0.1em; }

3
Maria Campbell

他の人については知りませんが、書いているテキストのy値を増やすことで行間隔を調整しています。私は実際には文字列をスペースで分割し、各Wordをループ内の1行下に蹴っています。私が使用する数字は、デフォルトのフォントに基づいています。別のフォントを使用する場合は、これらの数値を調整する必要がある場合があります。

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

実際には、文字間隔の概念キャンバスはサポートされていません。

だから私はこれを行うためにJavaScriptを使用しました。

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

OR

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

キャンバスの文字間隔IS SUPPORTED、これを使用しました

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