web-dev-qa-db-ja.com

<canvas>要素でのテキストの折り返し

<canvas>要素を使用して画像にテキストを追加しようとしています。最初に画像が描画され、画像上にテキストが描画されます。ここまでは順調ですね。

しかし、私が問題に直面しているのは、テキストが長すぎると、キャンバスによって最初と最後で途切れるということです。キャンバスのサイズを変更する予定はありませんが、長いテキストを複数行に折り返して、すべて表示されるようにする方法を考えていました。誰かが私を正しい方向に向けることができますか?

35
Gwood

@mizarの回答の更新バージョン。1つの重大なバグと1つのマイナーなバグが修正されています。

function getLines(ctx, text, maxWidth) {
    var words = text.split(" ");
    var lines = [];
    var currentLine = words[0];

    for (var i = 1; i < words.length; i++) {
        var Word = words[i];
        var width = ctx.measureText(currentLine + " " + Word).width;
        if (width < maxWidth) {
            currentLine += " " + Word;
        } else {
            lines.Push(currentLine);
            currentLine = Word;
        }
    }
    lines.Push(currentLine);
    return lines;
}

私たちはしばらくの間このコードを使用してきましたが、今日はテキストが描画されない理由を解明しようとしていたところ、バグが見つかりました!

GetLines()関数に(スペースなしで)1つのWordを与えると、1行の配列ではなく空の配列が返されることがわかります。

調査中に、元のコードでは行の長さを測定するときにスペースが考慮されていなかったため、行が最終的に必要以上に長くなることがある別の(はるかに微妙な)バグが見つかりました。

私たちがそれに投げかけたすべてに機能する私たちの更新されたバージョンは、上記です。バグを見つけたら教えてください!

29
crazy2be

可能な方法(完全にはテストされていませんが、現時点では完全に機能しました)

    /**
     * Divide an entire phrase in an array of phrases, all with the max pixel length given.
     * The words are initially separated by the space char.
     * @param phrase
     * @param length
     * @return
     */
function getLines(ctx,phrase,maxPxLength,textStyle) {
    var wa=phrase.split(" "),
        phraseArray=[],
        lastPhrase=wa[0],
        measure=0,
        splitChar=" ";
    if (wa.length <= 1) {
        return wa
    }
    ctx.font = textStyle;
    for (var i=1;i<wa.length;i++) {
        var w=wa[i];
        measure=ctx.measureText(lastPhrase+splitChar+w).width;
        if (measure<maxPxLength) {
            lastPhrase+=(splitChar+w);
        } else {
            phraseArray.Push(lastPhrase);
            lastPhrase=w;
        }
        if (i===wa.length-1) {
            phraseArray.Push(lastPhrase);
            break;
        }
    }
    return phraseArray;
}
22
mizar

ここに私のスピンがありました...私は@mizarの答えを読んでそれにいくつかの変更を加えました...そして少しの援助で私はこれを得ることができました.

コードが削除されました。フィドルを参照してください。

以下に使用例を示します。 http://jsfiddle.net/9PvMU/1/ -このスクリプトも表示されます here そして最終的に私が使用したものになります...この関数はctxは親スコープで使用できます...できない場合は、常に渡すことができます。


編集する

投稿は古く、私がまだいじっていた私のバージョンの機能がありました。このバージョンはこれまでのところ私のニーズを満たしているようであり、他の人を助けることができることを願っています。


編集する

このコードに小さなバグがあることに気づきました。修正に取り掛かるのに少し時間がかかりましたが、ここでは更新されています。私はそれを自分でテストしましたが、今は期待どおりに動作するようです。

function fragmentText(text, maxWidth) {
    var words = text.split(' '),
        lines = [],
        line = "";
    if (ctx.measureText(text).width < maxWidth) {
        return [text];
    }
    while (words.length > 0) {
        var split = false;
        while (ctx.measureText(words[0]).width >= maxWidth) {
            var tmp = words[0];
            words[0] = tmp.slice(0, -1);
            if (!split) {
                split = true;
                words.splice(1, 0, tmp.slice(-1));
            } else {
                words[1] = tmp.slice(-1) + words[1];
            }
        }
        if (ctx.measureText(line + words[0]).width < maxWidth) {
            line += words.shift() + " ";
        } else {
            lines.Push(line);
            line = "";
        }
        if (words.length === 0) {
            lines.Push(line);
        }
    }
    return lines;
}
7
rlemon

context.measureText(text).widthはあなたが探しているものです...

5
Warty

ここのスクリプトから: http://www.html5canvastutorials.com/tutorials/html5-canvas-wrap-text-tutorial/

段落サポートを含めるように拡張しました。新しい行には\ nを使用します。

function wrapText(context, text, x, y, line_width, line_height)
{
    var line = '';
    var paragraphs = text.split('\n');
    for (var i = 0; i < paragraphs.length; i++)
    {
        var words = paragraphs[i].split(' ');
        for (var n = 0; n < words.length; n++)
        {
            var testLine = line + words[n] + ' ';
            var metrics = context.measureText(testLine);
            var testWidth = metrics.width;
            if (testWidth > line_width && n > 0)
            {
                context.fillText(line, x, y);
                line = words[n] + ' ';
                y += line_height;
            }
            else
            {
                line = testLine;
            }
        }
        context.fillText(line, x, y);
        y += line_height;
        line = '';
    }
}

テキストは次のようにフォーマットできます。

var text = 
[
    "Paragraph 1.",
    "\n\n",
    "Paragraph 2."
].join("");

使用する:

wrapText(context, text, x, y, line_width, line_height);

代わりに

context.fillText(text, x, y);
2
squarcle

このスクリプトを試して、テキストをキャンバスにラップします。

 <script>
  function wrapText(ctx, text, x, y, maxWidth, lineHeight) {
    var words = text.split(' ');
    var line = '';

    for(var n = 0; n < words.length; n++) {
      var testLine = line + words[n] + ' ';
      var metrics = ctx.measureText(testLine);
      var testWidth = metrics.width;
      if (testWidth > maxWidth && n > 0) {
        ctx.fillText(line, x, y);
        line = words[n] + ' ';
        y += lineHeight;
      }
      else {
        line = testLine;
      }
    }
    ctx.fillText(line, x, y);
  }

  var canvas = document.getElementById('Canvas01');
  var ctx = canvas.getContext('2d');
  var maxWidth = 400;
  var lineHeight = 24;
  var x = (canvas.width - maxWidth) / 2;
  var y = 70;
  var text = 'HTML is the language for describing the structure of Web pages. HTML stands for HyperText Markup Language. Web pages consist of markup tags and plain text. HTML is written in the form of HTML elements consisting of tags enclosed in angle brackets (like <html>). HTML tags most commonly come in pairs like <h1> and </h1>, although some tags represent empty elements and so are unpaired, for example <img>..';

  ctx.font = '15pt Calibri';
  ctx.fillStyle = '#555555';

  wrapText(ctx, text, x, y, maxWidth, lineHeight);
  </script>
</body>

こちらのデモをご覧ください http://codetutorial.com/examples-canvas/canvas-examples-text-wrap

2
MichaelCalvin

見てください https://developer.mozilla.org/en/Drawing_text_using_a_canvas#measureText%28%29

選択したテキストが表示され、キャンバスよりも幅が広い場合は、テキストが十分に短くなるまで単語を削除できます。削除された単語を使用すると、2行目から開始して同じことができます。

もちろん、これはあまり効率的ではないので、1つのWordを削除しないことで改善できますが、テキストがキャンバスの幅よりもはるかに広い場合は、複数の単語を削除できます。

私は調査しませんでしたが、多分それらはあなたのためにこれを行うJavaScriptライブラリです

1
Nathan

私が使用した自分のバージョンを投稿しています ここ ここでの答えは私にとって不十分ではなかったので。私の場合、最初の単語を測定する必要がありました。これは、小さなキャンバス領域から長すぎる単語を否定できるようにするためです。そして、「break + space」、「space + break」、または二重改行/段落区切りコンボのサポートが必要でした。

wrapLines: function(ctx, text, maxWidth) {
    var lines = [],
        words = text.replace(/\n\n/g,' ` ').replace(/(\n\s|\s\n)/g,'\r')
        .replace(/\s\s/g,' ').replace('`',' ').replace(/(\r|\n)/g,' '+' ').split(' '),
        space = ctx.measureText(' ').width,
        width = 0,
        line = '',
        Word = '',
        len = words.length,
        w = 0,
        i;
    for (i = 0; i < len; i++) {
        Word = words[i];
        w = Word ? ctx.measureText(Word).width : 0;
        if (w) {
            width = width + space + w;
        }
        if (w > maxWidth) {
            return [];
        } else if (w && width < maxWidth) {
            line += (i ? ' ' : '') + Word;
        } else {
            !i || lines.Push(line !== '' ? line.trim() : '');
            line = Word;
            width = w;
        }
    }
    if (len !== i || line !== '') {
        lines.Push(line);
    }
    return lines;
}

改行、または段落の区切りのバリアントをサポートし、先頭と末尾の段落の区切りと同様に二重スペースを削除します。テキストが収まらない場合は、空の配列を返します。または、すぐに描画できる線の配列。

0
hexalys

これにより、テキストボックスから行が正しく表示されます:-

 function fragmentText(text, maxWidth) {
    var lines = text.split("\n");
    var fittingLines = [];
    for (var i = 0; i < lines.length; i++) {
        if (canvasContext.measureText(lines[i]).width <= maxWidth) {
            fittingLines.Push(lines[i]);
        }
        else {
            var tmp = lines[i];
            while (canvasContext.measureText(tmp).width > maxWidth) {
                tmp = tmp.slice(0, tmp.length - 1);
            }
            if (tmp.length >= 1) {
                var regex = new RegExp(".{1," + tmp.length + "}", "g");
                var thisLineSplitted = lines[i].match(regex);
                for (var j = 0; j < thisLineSplitted.length; j++) {
                    fittingLines.Push(thisLineSplitted[j]);
                }
            }
        }
    }
    return fittingLines;

そして、取得した線をキャンバスに描画します:-

 var lines = fragmentText(textBoxText, (rect.w - 10)); //rect.w = canvas width, rect.h = canvas height
                    for (var showLines = 0; showLines < lines.length; showLines++) { // do not show lines that go beyond the height
                        if ((showLines * resultFont.height) >= (rect.h - 10)) {      // of the canvas
                            break;
                        }
                    }
                    for (var i = 1; i <= showLines; i++) {
                        canvasContext.fillText(lines[i-1], rect.clientX +5 , rect.clientY + 10 + (i * (resultFont.height))); // resultfont = get the font height using some sort of calculation
                    }
0
JBelfort
0
wizztjh