45

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

テキストを描画するこのcanvas要素があります。letter-spacingCSS属性と同様に文字間隔を設定したい。つまり、文字列を描画するときに文字間のピクセル数を増やすことを意味します。

テキストを描画するための私のコードはそのようなものです。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";図面の前に追加しようとしましたが、役に立ちませんでした。簡単な設定でこれを行う方法はありますか、それとも間隔を考慮して各文字を個別に描画する関数を作成する必要がありますか?

4

12 に答える 12

50

文字間隔プロパティを設定することはできませんが、文字列内のすべての文字の間にさまざまな空白の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スペースのリストを参照してください。

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

于 2013-02-20T22:46:57.140 に答える
25

(仕様に従って)機能するかどうかはわかりませんが、一部のブラウザ(Chrome)では、要素自体にletter-spacingCSSプロパティを設定でき、コンテキストに描画されるすべてのテキストに適用されます。<canvas>(Chrome v56で動作し、Firefoxv51またはIEv11では動作しません。)

letter-spacingChrome v56では、スタイルを変更するたびに、キャンバスの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とおそらくSVGで階層化されたHTMLを使用し、それぞれが最善を尽くします。

また、「独自のローリング」(カスタムオフセットを使用して各文字を描画する)は、文字のカーニングペアとピクセルに揃えられたフォントのヒントがあるため、ほとんどのフォントで悪い結果をもたらすことにも注意してください。

于 2012-01-21T19:07:48.243 に答える
9

Canvasコンテキストのプロパティとして文字間隔を設定することはできません。申し訳ありませんが、手動で間隔を空けることによってのみ効果を達成できます。(のように、各文字を手動で描画すると、xがそれぞれピクセル量ずつ増加します)

記録のために、を使用していくつかのテキストプロパティを設定できますがctx.font、文字間隔はそれらの1つではありません。設定できるのは、「font-style font-variant font-weight font-size /line-heightfont-family」です。

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

于 2012-01-21T19:05:59.997 に答える
7

「文字カーニングペア」などを考慮して、以下のように書いています。それはそれを考慮に入れるべきであり、大まかなテストはそれがそうすることを示唆しています。それについて何かコメントがあれば、私はあなたにその主題に関する私の質問を指摘します(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よりも優れていることは理解しています。他のフォントに関する提案はありがたいことに受け入れられています。

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

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

于 2015-12-03T04:51:21.533 に答える
3

これがあなたがそのようにあなたの文脈にカーニングを設定することを可能にするいくつかのコーヒースクリプトです

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;
  });
};
于 2013-03-19T19:44:44.553 に答える
3

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

于 2016-11-07T16:31:31.420 に答える
1

これは古い質問かもしれませんが、それでも関連性があります。PatrickMatteによるJamesCarlyle-Clarkeの応答の拡張を採用し、今でも昔ながらのJavascriptと同様に非常にうまく機能するものを手に入れました。アイデアは、2つの連続する文字間のスペースを測定し、それに「追加」することです。はい、負の数は機能します。

これが私が持っているものです(コメントの多いバージョン):

function fillTextWithSpacing (context, text, x, y, spacing) {
    // Total width is needed to adjust the starting X based on text alignment.
    const total_width = context.measureText (text).width + spacing * (text.length - 1);

    // We need to save the current text alignment because we have to set it to
    // left for our calls to fillText() draw in the places we expect. Don't
    // worry, we're going to set it back at the end.
    const align = context.textAlign;
    context.textAlign = "left";

    // Nothing to do for left alignment, but adjustments are needed for right
    // and left. Justify defeats the purpose of manually adjusting character
    // spacing, and it requires a width to be known.
    switch (align) {
        case "right":
            x -= total_width;
            break;
        case "center":
            x -= total_width / 2;
            break;
    }

    // We have some things to keep track of and the C programmer in me likes
    // declarations on their own and in groups.
    let offset, pair_width, char_width, char_next_width, pair_spacing, char, char_next;

    // We're going to step through the text one character at a time, but we
    // can't use for(... of ...) because we need to be able to look ahead.
    for (offset = 0; offset < text.length; offset = offset + 1) {
        // Easy on the eyes later
        char = text.charAt (offset);
        // Default the spacing between the "pair" of characters to 0. We need
        // for the last character.
        pair_spacing = 0;
        // Check to see if there's at least one more character after this one.
        if (offset + 1 < text.length) {
            // This is always easier on the eyes
            char_next = text.charAt (offset + 1);
            // Measure to the total width of both characters, including the
            // spacing between them... even if it's negative.
            pair_width = context.measureText (char + char_next).width;
            // Measure the width of just the current character.
            char_width = context.measureText (char).width;
            // Measure the width of just the next character.
            char_next_width = context.measureText (char_next).width;
            // We can determine the kerning by subtracting the width of each
            // character from the width of both characters together.
            pair_spacing = pair_width - char_width - char_next_width;
        }

        // Draw the current character
        context.fillText (char, x, y);
        // Advanced the X position by adding the current character width, the
        // spacing between the current and next characters, and the manual
        // spacing adjustment (negatives work).
        x = x + char_width + pair_spacing + spacing;
    }

    // Set the text alignment back to the original value.
    context.textAlign = align;

    // Profit
}

そして、ここにデモがあります:

let canvas = document.getElementById ("canvas");
canvas.width = 600;
canvas.height = 150;
let context = canvas.getContext ("2d");

function fillTextWithSpacing (context, text, x, y, spacing) {
    const total_width = context.measureText (text).width + spacing * (text.length - 1);

    const align = context.textAlign;
    context.textAlign = "left";

    switch (align) {
        case "right":
            x -= total_width;
            break;
        case "center":
            x -= total_width / 2;
            break;
    }

    let offset, pair_width, char_width, char_next_width, pair_spacing, char, char_next;

    for (offset = 0; offset < text.length; offset = offset + 1) {
        char = text.charAt (offset);
        pair_spacing = 0;
        if (offset + 1 < text.length) {
            char_next = text.charAt (offset + 1);
            pair_width = context.measureText (char + char_next).width;
            char_width = context.measureText (char).width;
            char_next_width = context.measureText (char_next).width;
            pair_spacing = pair_width - char_width - char_next_width;
        }

        context.fillText (char, x, y);
        x = x + char_width + pair_spacing + spacing;
    }

    context.textAlign = align;
}

function update () {
    let
        font = document.getElementById ("font").value,
        size = parseInt (document.getElementById ("size").value, 10),
        weight = parseInt (document.getElementById ("weight").value, 10),
        italic = document.getElementById ("italic").checked,
        spacing = parseInt (document.getElementById ("spacing").value, 10),
        text = document.getElementById ("text").value;

    context.textAlign = "center";
    context.textBaseline = "alphabetic";
    context.fillStyle = "#404040";
    context.font = (italic ? "italic " : "") + weight + " " + size + "px " + font;

    context.clearRect (0, 0, canvas.width, canvas.height);
    fillTextWithSpacing (context, text, canvas.width / 2, (canvas.height + size) / 2, spacing);
}

document.getElementById ("font").addEventListener (
    "change",
    (event) => {
        update ();
    }
);
document.getElementById ("size").addEventListener (
    "change",
    (event) => {
        update ();
    }
);
document.getElementById ("weight").addEventListener (
    "change",
    (event) => {
        update ();
    }
);
document.getElementById ("italic").addEventListener (
    "change",
    (event) => {
        update ();
    }
);
document.getElementById ("spacing").addEventListener (
    "change",
    (event) => {
        update ();
    }
);
document.getElementById ("text").addEventListener (
    "input",
    (event) => {
        update ();
    }
);

update ();
select, input {
  display: inline-block;
}
input[type=text] {
  display: block;
  margin: 0.5rem 0;
}
canvas {
  border: 1px solid #b0b0b0;
  width: 600px;
  height: 150px;
}
<!DOCTYPE html>
<html lang="en-US">
    <head>
        <meta charset="utf-8" />
    </head>
    <body>
        <select id="font">
            <option value="serif">Serif</option>
            <option value="sans-serif">Sans Serif</option>
            <option value="fixed-width">Fixed Width</option>
        </select>
        <label>Size: <input type="number" id="size" value="60" min="1" max="200" size="3" /></label>
        <label>Weight: <input type="number" id="weight" value="100" min="100" max="1000" step="100" size="4" /></label>
        <label>Italic: <input type="checkbox" id="italic" checked /></label>
        <label>Spacing: <input type="number" id="spacing" value="0" min="-200" max="200" size="4" /></label>
        <input type="text" id="text" placeholder="Text" value="hello" size="40"/>
        <canvas id="canvas"></canvas>
    </body>
</html>

于 2021-12-30T04:21:14.267 に答える
0

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

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

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

また

var sample_text = "Praveen Chelumalla";
var text = sample_text.split( "")。join( "");
于 2017-09-07T06:51:36.707 に答える
0

他の人のことは知りませんが、書いているテキストのy値を大きくして行間隔を調整しました。私は実際に文字列をスペースで分割し、ループ内の各単語を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;
}               
于 2019-05-02T02:02:18.490 に答える
0

これは、JamesCarlyle-Clarkeの以前の回答に基づく別の方法です。また、テキストを左、中央、右に揃えることもできます。

export function fillTextWithSpacing(context, text, x, y, spacing, textAlign) {
    const totalWidth = context.measureText(text).width + spacing * (text.length - 1);
    switch (textAlign) {
        case "right":
            x -= totalWidth;
            break;
        case "center":
            x -= totalWidth / 2;
            break;
    }
    for (let i = 0; i < text.length; i++) {
        let char = text.charAt(i);
        context.fillText(char, x, y);
        x += context.measureText(char).width + spacing;
    }
}
于 2021-03-23T20:21:08.550 に答える
-1

キャンバスの文字間隔がサポートされています。これを使用しました

canvas = document.getElementById('canvas');
canvas.style.letterSpacing = '2px';
于 2017-11-01T09:12:53.563 に答える
-4

私が使う:

ctx.font = "32px Tahoma";//set font
ctx.scale(0.75,1);//important! the scale
ctx.fillText("LaFeteParFete test text", 2, 274);//draw
ctx.setTransform(1,0,0,1,0,0);//reset transform
于 2016-05-25T00:01:51.660 に答える