HTML5 canvas ctx.fillText macht keine Zeilenumbrüche?


108

Ich kann anscheinend keinen Text zu einer Zeichenfläche hinzufügen, wenn der Text "\ n" enthält. Ich meine, die Zeilenumbrüche werden nicht angezeigt / funktionieren nicht.

ctxPaint.fillText("s  ome \n \\n <br/> thing", x, y);

Der obige Code wird "s ome \n <br/> thing"in einer Zeile gezeichnet.

Ist dies eine Einschränkung von fillText oder mache ich es falsch? Die "\ n" sind da und werden nicht gedruckt, aber sie funktionieren auch nicht.


1
Möchten Sie am Ende automatisch einwickeln? oder nur um die im Text enthaltenen Zeilenumbrüche zu berücksichtigen?
Gabriele Petrioli

Wickeln Sie den Text in mehrere Zeilen.
Turm

Hallo Twodordan, gibt es diese Einschränkung sowohl für Chrom als auch für Mozilla? Menschen verwenden oft einfachen HTML-Text, den sie mit einer Position über die Leinwand legen: zum Beispiel absolut. Sie können auch zwei fillText ausführen und den Y-Ursprung Ihres Textes für Ihre zweiten Zeilen verschieben.
Tim


TL; DR: Rufen Sie entweder fillText()mehrmals auf und verwenden Sie Ihre Schrifthöhe zum Trennen oder verwenden Sie developer.mozilla.org/en-US/docs/Web/API/TextMetrics developer.mozilla.org/en-US/docs/Web/API /… - oder verwenden Sie eine der sehr komplizierten "Lösungen", die TextMetrics nicht verwenden ...
Andrew

Antworten:


62

Ich fürchte, es ist eine Einschränkung von Canvas fillText. Es gibt keine Unterstützung für mehrere Leitungen. Was noch schlimmer ist, es gibt keine eingebaute Möglichkeit, die Linienhöhe zu messen, nur die Breite, was es noch schwieriger macht, es selbst zu tun!

Viele Leute haben ihre eigene Unterstützung für mehrere Zeilen geschrieben, das vielleicht bemerkenswerteste Projekt, das es gibt, ist Mozilla Skywriter .

Das Wesentliche, was Sie tun müssen, sind mehrere fillTextAufrufe, während Sie jedes Mal die Höhe des Textes zum y-Wert hinzufügen. (Ich glaube, die Skywriter messen die Breite von M, um den Text zu approximieren.)


Danke dir! Ich hatte das Gefühl, es wäre lästig ... Schön, etwas über den SKYWRITER zu wissen, aber ich werde einfach "warten", bis fillText () verbessert ist. In meinem Fall war das kein besonders wichtiger Deal. Hah, keine Zeilenhöhe, es ist, als hätte jemand das absichtlich getan. : D
Spectraljump

18
Ehrlich gesagt, ich würde Ihren Atem nicht anhalten, wenn fillText () "verbessert" wird, um dies zu unterstützen, da ich das Gefühl habe, dass es so verwendet werden soll (mehrere Aufrufe und Berechnung des yOffset selbst). Ich denke, ein großer Teil der Leistung der Canvas-API besteht darin, dass sie die Zeichnungsfunktionalität auf niedrigerer Ebene von dem trennt, was Sie bereits tun können (die erforderlichen Messungen durchführen). Sie können die Texthöhe auch einfach durch Angabe der Textgröße in Pixel ermitteln. mit anderen Worten: context.font = "16px Arial"; - Sie haben die Höhe dort; Die Breite ist die einzige, die dynamisch ist.
Lev

1
Es wurden einige zusätzliche Eigenschaften für measureText()hinzugefügt, die meines Erachtens das Problem lösen könnten. Chrome hat eine Flagge, um sie zu aktivieren, andere Browser jedoch noch nicht ...!
SWdV

@ SWdV nur um klar zu sein, diese sind seit Jahren in der Spezifikation, es kann noch Jahre dauern, bis wir weit genug Akzeptanz haben, um zu verwenden :(
Simon Sarris

67

Wenn Sie sich nur um die Zeilenumbrüche im Text kümmern möchten, können Sie dies simulieren, indem Sie den Text an den Zeilenumbrüchen aufteilen und mehrmals aufrufen fillText()

So etwas wie http://jsfiddle.net/BaG4J/1/

var c = document.getElementById('c').getContext('2d');
c.font = '11px Courier';
    console.log(c);
var txt = 'line 1\nline 2\nthird line..';
var x = 30;
var y = 30;
var lineheight = 15;
var lines = txt.split('\n');

for (var i = 0; i<lines.length; i++)
    c.fillText(lines[i], x, y + (i*lineheight) );
canvas{background-color:#ccc;}
<canvas id="c" width="150" height="150"></canvas>


Ich habe gerade einen Wrapping Proof of Concept ( absoluter Wrap bei angegebener Breite. Noch kein Umgang mit Wörtern )
unter http://jsfiddle.net/BaG4J/2/ erstellt.

var c = document.getElementById('c').getContext('2d');
c.font = '11px Courier';

var txt = 'this is a very long text to print';

printAt(c, txt, 10, 20, 15, 90 );


function printAt( context , text, x, y, lineHeight, fitWidth)
{
    fitWidth = fitWidth || 0;
    
    if (fitWidth <= 0)
    {
         context.fillText( text, x, y );
        return;
    }
    
    for (var idx = 1; idx <= text.length; idx++)
    {
        var str = text.substr(0, idx);
        console.log(str, context.measureText(str).width, fitWidth);
        if (context.measureText(str).width > fitWidth)
        {
            context.fillText( text.substr(0, idx-1), x, y );
            printAt(context, text.substr(idx-1), x, y + lineHeight, lineHeight,  fitWidth);
            return;
        }
    }
    context.fillText( text, x, y );
}
canvas{background-color:#ccc;}
<canvas id="c" width="150" height="150"></canvas>


Und ein Zeilenumbruch ( bei Räumen zu brechen Proof of Concept).
Beispiel unter http://jsfiddle.net/BaG4J/5/

var c = document.getElementById('c').getContext('2d');
c.font = '11px Courier';

var txt = 'this is a very long text. Some more to print!';

printAtWordWrap(c, txt, 10, 20, 15, 90 );


function printAtWordWrap( context , text, x, y, lineHeight, fitWidth)
{
    fitWidth = fitWidth || 0;
    
    if (fitWidth <= 0)
    {
        context.fillText( text, x, y );
        return;
    }
    var words = text.split(' ');
    var currentLine = 0;
    var idx = 1;
    while (words.length > 0 && idx <= words.length)
    {
        var str = words.slice(0,idx).join(' ');
        var w = context.measureText(str).width;
        if ( w > fitWidth )
        {
            if (idx==1)
            {
                idx=2;
            }
            context.fillText( words.slice(0,idx-1).join(' '), x, y + (lineHeight*currentLine) );
            currentLine++;
            words = words.splice(idx-1);
            idx = 1;
        }
        else
        {idx++;}
    }
    if  (idx > 0)
        context.fillText( words.join(' '), x, y + (lineHeight*currentLine) );
}
canvas{background-color:#ccc;}
<canvas id="c" width="150" height="150"></canvas>


Im zweiten und dritten Beispiel verwende ich die measureText()Methode, die zeigt, wie lang ( in Pixel ) eine Zeichenfolge beim Drucken sein wird.


Wie kann man den ganzen Langtext rechtfertigen?
Amirhossein Tarmast

39

Vielleicht komme ich etwas spät zu dieser Party, aber ich fand das folgende Tutorial zum Einwickeln von Text auf eine Leinwand perfekt.

http://www.html5canvastutorials.com/tutorials/html5-canvas-wrap-text-tutorial/

Von da an konnte ich denken, dass mehrere Leitungen funktionieren (sorry Ramirez, deine hat bei mir nicht funktioniert!). Mein vollständiger Code zum Umschließen von Text in eine Zeichenfläche lautet wie folgt:

<script type="text/javascript">

     // http: //www.html5canvastutorials.com/tutorials/html5-canvas-wrap-text-tutorial/
     function wrapText(context, text, x, y, maxWidth, lineHeight) {
        var cars = text.split("\n");

        for (var ii = 0; ii < cars.length; ii++) {

            var line = "";
            var words = cars[ii].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 > maxWidth) {
                    context.fillText(line, x, y);
                    line = words[n] + " ";
                    y += lineHeight;
                }
                else {
                    line = testLine;
                }
            }

            context.fillText(line, x, y);
            y += lineHeight;
        }
     }

     function DrawText() {

         var canvas = document.getElementById("c");
         var context = canvas.getContext("2d");

         context.clearRect(0, 0, 500, 600);

         var maxWidth = 400;
         var lineHeight = 60;
         var x = 20; // (canvas.width - maxWidth) / 2;
         var y = 58;


         var text = document.getElementById("text").value.toUpperCase();                

         context.fillStyle = "rgba(255, 0, 0, 1)";
         context.fillRect(0, 0, 600, 500);

         context.font = "51px 'LeagueGothicRegular'";
         context.fillStyle = "#333";

         wrapText(context, text, x, y, maxWidth, lineHeight);
     }

     $(document).ready(function () {

         $("#text").keyup(function () {
             DrawText();
         });

     });

    </script>

Wo cist die ID meiner Leinwand und textist die ID meines Textfelds.

Wie Sie wahrscheinlich sehen können, verwende ich eine nicht standardmäßige Schriftart. Sie können @ font-face verwenden, solange Sie die Schriftart für einen Text VOR der Bearbeitung der Zeichenfläche verwendet haben. Andernfalls nimmt die Zeichenfläche die Zeichenfläche nicht auf.

Hoffe das hilft jemandem.


26

Teilen Sie den Text in Zeilen und zeichnen Sie jede einzeln:

function fillTextMultiLine(ctx, text, x, y) {
  var lineHeight = ctx.measureText("M").width * 1.2;
  var lines = text.split("\n");
  for (var i = 0; i < lines.length; ++i) {
    ctx.fillText(lines[i], x, y);
    y += lineHeight;
  }
}

17

Hier ist meine Lösung: Ändern Sie die beliebte Funktion wrapText (), die hier bereits vorgestellt wird. Ich verwende die Prototyping-Funktion von JavaScript, damit Sie die Funktion aus dem Canvas-Kontext aufrufen können.

CanvasRenderingContext2D.prototype.wrapText = function (text, x, y, maxWidth, lineHeight) {

    var lines = text.split("\n");

    for (var i = 0; i < lines.length; i++) {

        var words = lines[i].split(' ');
        var line = '';

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

        this.fillText(line, x, y);
        y += lineHeight;
    }
}

Grundlegende Verwendung:

var myCanvas = document.getElementById("myCanvas");
var ctx = myCanvas.getContext("2d");
ctx.fillStyle = "black";
ctx.font = "12px sans-serif";
ctx.textBaseline = "top";
ctx.wrapText("Hello\nWorld!",20,20,160,16);

Hier ist eine Demonstration, die ich zusammengestellt habe: http://jsfiddle.net/7RdbL/


Lief wie am Schnürchen. Danke dir.
Couzzi

13

Ich habe gerade den CanvasRenderingContext2D um zwei Funktionen erweitert: mlFillText und mlStrokeText.

Sie finden die letzte Version in GitHub :

Mit diesen Funktionen können Sie Miltiline-Text in ein Feld einfügen / streichen. Sie können den Text vertikal und horizontal ausrichten. (Es berücksichtigt \ n und kann auch den Text rechtfertigen).

Die Prototypen sind:

Funktion mlFillText (Text, x, y, w, h, vAlign, hAlign, Zeilenhöhe); Funktion mlStrokeText (Text, x, y, w, h, vAlign, hAlign, Zeilenhöhe);

Wo vAlign sein kann: "oben", "Mitte" oder "Schaltfläche" Und hAlign kann sein: "links", "Mitte", "rechts" oder "rechtfertigen"

Sie können die Bibliothek hier testen: http://jsfiddle.net/4WRZj/1/

Geben Sie hier die Bildbeschreibung ein

Hier ist der Code der Bibliothek:

// Library: mltext.js
// Desciption: Extends the CanvasRenderingContext2D that adds two functions: mlFillText and mlStrokeText.
//
// The prototypes are: 
//
// function mlFillText(text,x,y,w,h,vAlign,hAlign,lineheight);
// function mlStrokeText(text,x,y,w,h,vAlign,hAlign,lineheight);
// 
// Where vAlign can be: "top", "center" or "button"
// And hAlign can be: "left", "center", "right" or "justify"
// Author: Jordi Baylina. (baylina at uniclau.com)
// License: GPL
// Date: 2013-02-21

function mlFunction(text, x, y, w, h, hAlign, vAlign, lineheight, fn) {
    text = text.replace(/[\n]/g, " \n ");
    text = text.replace(/\r/g, "");
    var words = text.split(/[ ]+/);
    var sp = this.measureText(' ').width;
    var lines = [];
    var actualline = 0;
    var actualsize = 0;
    var wo;
    lines[actualline] = {};
    lines[actualline].Words = [];
    i = 0;
    while (i < words.length) {
        var word = words[i];
        if (word == "\n") {
            lines[actualline].EndParagraph = true;
            actualline++;
            actualsize = 0;
            lines[actualline] = {};
            lines[actualline].Words = [];
            i++;
        } else {
            wo = {};
            wo.l = this.measureText(word).width;
            if (actualsize === 0) {
                while (wo.l > w) {
                    word = word.slice(0, word.length - 1);
                    wo.l = this.measureText(word).width;
                }
                if (word === "") return; // I can't fill a single character
                wo.word = word;
                lines[actualline].Words.push(wo);
                actualsize = wo.l;
                if (word != words[i]) {
                    words[i] = words[i].slice(word.length, words[i].length);
                } else {
                    i++;
                }
            } else {
                if (actualsize + sp + wo.l > w) {
                    lines[actualline].EndParagraph = false;
                    actualline++;
                    actualsize = 0;
                    lines[actualline] = {};
                    lines[actualline].Words = [];
                } else {
                    wo.word = word;
                    lines[actualline].Words.push(wo);
                    actualsize += sp + wo.l;
                    i++;
                }
            }
        }
    }
    if (actualsize === 0) lines[actualline].pop();
    lines[actualline].EndParagraph = true;

    var totalH = lineheight * lines.length;
    while (totalH > h) {
        lines.pop();
        totalH = lineheight * lines.length;
    }

    var yy;
    if (vAlign == "bottom") {
        yy = y + h - totalH + lineheight;
    } else if (vAlign == "center") {
        yy = y + h / 2 - totalH / 2 + lineheight;
    } else {
        yy = y + lineheight;
    }

    var oldTextAlign = this.textAlign;
    this.textAlign = "left";

    for (var li in lines) {
        var totallen = 0;
        var xx, usp;
        for (wo in lines[li].Words) totallen += lines[li].Words[wo].l;
        if (hAlign == "center") {
            usp = sp;
            xx = x + w / 2 - (totallen + sp * (lines[li].Words.length - 1)) / 2;
        } else if ((hAlign == "justify") && (!lines[li].EndParagraph)) {
            xx = x;
            usp = (w - totallen) / (lines[li].Words.length - 1);
        } else if (hAlign == "right") {
            xx = x + w - (totallen + sp * (lines[li].Words.length - 1));
            usp = sp;
        } else { // left
            xx = x;
            usp = sp;
        }
        for (wo in lines[li].Words) {
            if (fn == "fillText") {
                this.fillText(lines[li].Words[wo].word, xx, yy);
            } else if (fn == "strokeText") {
                this.strokeText(lines[li].Words[wo].word, xx, yy);
            }
            xx += lines[li].Words[wo].l + usp;
        }
        yy += lineheight;
    }
    this.textAlign = oldTextAlign;
}

(function mlInit() {
    CanvasRenderingContext2D.prototype.mlFunction = mlFunction;

    CanvasRenderingContext2D.prototype.mlFillText = function (text, x, y, w, h, vAlign, hAlign, lineheight) {
        this.mlFunction(text, x, y, w, h, hAlign, vAlign, lineheight, "fillText");
    };

    CanvasRenderingContext2D.prototype.mlStrokeText = function (text, x, y, w, h, vAlign, hAlign, lineheight) {
        this.mlFunction(text, x, y, w, h, hAlign, vAlign, lineheight, "strokeText");
    };
})();

Und hier ist das Anwendungsbeispiel:

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");

var T = "This is a very long line line with a CR at the end.\n This is the second line.\nAnd this is the last line.";
var lh = 12;

ctx.lineWidth = 1;

ctx.mlFillText(T, 10, 10, 100, 100, 'top', 'left', lh);
ctx.strokeRect(10, 10, 100, 100);

ctx.mlFillText(T, 110, 10, 100, 100, 'top', 'center', lh);
ctx.strokeRect(110, 10, 100, 100);

ctx.mlFillText(T, 210, 10, 100, 100, 'top', 'right', lh);
ctx.strokeRect(210, 10, 100, 100);

ctx.mlFillText(T, 310, 10, 100, 100, 'top', 'justify', lh);
ctx.strokeRect(310, 10, 100, 100);

ctx.mlFillText(T, 10, 110, 100, 100, 'center', 'left', lh);
ctx.strokeRect(10, 110, 100, 100);

ctx.mlFillText(T, 110, 110, 100, 100, 'center', 'center', lh);
ctx.strokeRect(110, 110, 100, 100);

ctx.mlFillText(T, 210, 110, 100, 100, 'center', 'right', lh);
ctx.strokeRect(210, 110, 100, 100);

ctx.mlFillText(T, 310, 110, 100, 100, 'center', 'justify', lh);
ctx.strokeRect(310, 110, 100, 100);

ctx.mlFillText(T, 10, 210, 100, 100, 'bottom', 'left', lh);
ctx.strokeRect(10, 210, 100, 100);

ctx.mlFillText(T, 110, 210, 100, 100, 'bottom', 'center', lh);
ctx.strokeRect(110, 210, 100, 100);

ctx.mlFillText(T, 210, 210, 100, 100, 'bottom', 'right', lh);
ctx.strokeRect(210, 210, 100, 100);

ctx.mlFillText(T, 310, 210, 100, 100, 'bottom', 'justify', lh);
ctx.strokeRect(310, 210, 100, 100);

ctx.mlStrokeText("Yo can also use mlStrokeText!", 0 , 310 , 420, 30, 'center', 'center', lh);

Uncaught ReferenceError: Words is not definedWenn ich versuche, die Schriftart zu ändern. Zum Beispiel: ctx.font = '40px Arial';- versuchen Sie, das in Ihre Geige zu
stecken

Übrigens, woher zum Teufel kommt die Words(Groß- und Kleinschreibung) Variable? Es ist nirgendwo definiert. Dieser Teil des Codes wird nur ausgeführt, wenn Sie die Schriftart ändern.
Psycho Brm

1
@psychobrm Du hast absolut recht. Es ist ein Fehler (ich behebe ihn bereits). Dieser Teil des Codes wird nur ausgeführt, wenn Sie ein Wort in zwei Zeilen teilen müssen. Danke dir!
Jbaylina

Ich habe einige Upgrades vorgenommen, die ich brauchte: Leerzeichen rendern, führende / nachfolgende Zeilenumbrüche rendern, Strich rendern und mit einem Aufruf füllen (Text nicht zweimal messen). Ich musste auch die Iteration ändern, da for indies mit Extended nicht gut funktioniert Array.prototype. Könnten Sie es auf Github setzen, damit wir es wiederholen können?
Psycho Brm

@psychobrm Ich habe Ihre Änderungen zusammengeführt. Danke dir!
Jbaylina

8

Mit Javascript habe ich eine Lösung entwickelt. Es ist nicht schön, aber es hat bei mir funktioniert:


function drawMultilineText(){

    // set context and formatting   
    var context = document.getElementById("canvas").getContext('2d');
    context.font = fontStyleStr;
    context.textAlign = "center";
    context.textBaseline = "top";
    context.fillStyle = "#000000";

    // prepare textarea value to be drawn as multiline text.
    var textval = document.getElementByID("textarea").value;
    var textvalArr = toMultiLine(textval);
    var linespacing = 25;
    var startX = 0;
    var startY = 0;

    // draw each line on canvas. 
    for(var i = 0; i < textvalArr.length; i++){
        context.fillText(textvalArr[i], x, y);
        y += linespacing;
    }
}

// Creates an array where the <br/> tag splits the values.
function toMultiLine(text){
   var textArr = new Array();
   text = text.replace(/\n\r?/g, '<br/>');
   textArr = text.split("<br/>");
   return textArr;
}

Hoffentlich hilft das!


1
hallo, nehme meinen Text wie dieser var Text = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; was passiert dann auf leinwand ???
Amol Navsupe

Es wird aus der Leinwand verschwinden, da @Ramirez den Parameter maxWidth nicht auf fillText gesetzt hat :)
KaHa6uc

7

Der von @Gaby Petrioli bereitgestellte Code zum Umbrechen von Wörtern (Brechen an Leerzeichen) ist sehr hilfreich. Ich habe seinen Code erweitert, um Zeilenumbrüche zu unterstützen \n. Oft ist es auch nützlich, die Abmessungen des Begrenzungsrahmens zu haben, sodass multiMeasureText()sowohl die Breite als auch die Höhe zurückgegeben werden.

Sie können den Code hier sehen: http://jsfiddle.net/jeffchan/WHgaY/76/


5

Hier ist eine Version von Colin, wrapText()die auch vertikal zentrierten Text unterstützt mit context.textBaseline = 'middle':

var wrapText = function (context, text, x, y, maxWidth, lineHeight) {
    var paragraphs = text.split("\n");
    var textLines = [];

    // Loop through paragraphs
    for (var p = 0; p < paragraphs.length; p++) {
        var line = "";
        var words = paragraphs[p].split(" ");
        // Loop through words
        for (var w = 0; w < words.length; w++) {
            var testLine = line + words[w] + " ";
            var metrics = context.measureText(testLine);
            var testWidth = metrics.width;
            // Make a line break if line is too long
            if (testWidth > maxWidth) {
                textLines.push(line.trim());
                line = words[w] + " ";
            }
            else {
                line = testLine;
            }
        }
        textLines.push(line.trim());
    }

    // Move text up if centered vertically
    if (context.textBaseline === 'middle')
        y = y - ((textLines.length-1) * lineHeight) / 2;

    // Render text on canvas
    for (var tl = 0; tl < textLines.length; tl++) {
        context.fillText(textLines[tl], x, y);
        y += lineHeight;
    }
};

5

Wenn Sie nur zwei Textzeilen benötigen, können Sie diese in zwei verschiedene fillText-Aufrufe aufteilen und jedem eine andere Grundlinie geben.

ctx.textBaseline="bottom";
ctx.fillText("First line", x-position, y-position);
ctx.textBaseline="top";
ctx.fillText("Second line", x-position, y-position);

4

Diese Frage bezieht sich nicht darauf, wie Leinwand funktioniert. Wenn Sie einen Zeilenumbruch wünschen, passen Sie einfach die Koordinaten Ihres nächsten an ctx.fillText.

ctx.fillText("line1", w,x,y,z)
ctx.fillText("line2", w,x,y,z+20)

3

Ich denke, Sie können sich immer noch auf CSS verlassen

ctx.measureText().height doesnt exist.

Glücklicherweise können wir durch CSS-Hack-Ardry (siehe Typografische Metriken für weitere Möglichkeiten, ältere Implementierungen der Verwendung von CSS-Messungen zu korrigieren) die Höhe des Textes ermitteln, indem wir die offsetHeight von a mit denselben Schrifteigenschaften messen:

var d = document.createElement(”span”);
d.font = 20px arial
d.textContent = Hello world!”
var emHeight = d.offsetHeight;

von: http://www.html5rocks.com/de/tutorials/canvas/texteffects/


Das ist eine gute Lösung, wenn Sie den Speicher haben, um jedes Mal, wenn Sie messen müssen, ein solches Element zu erstellen. Sie können auch ctx.save()dann, ctx.font = '12pt Arial' dann , parseInt( ctx.font, 10 ). Beachten Sie, dass ich beim Einstellen 'pt' verwende. Es wird dann in PX übersetzt und kann als Verbrauchshöhe in eine Ziffer für den Verbrauch umgewandelt werden.
Eric Hodonsky

3

Ich habe hier eine winzige Bibliothek für dieses Szenario erstellt: Canvas-Txt

Es rendert Text mehrzeilig und bietet anständige Ausrichtungsmodi.

Um dies zu verwenden, müssen Sie es entweder installieren oder ein CDN verwenden.

Installation

npm install canvas-txt --save

JavaScript

import canvasTxt from 'canvas-txt'

var c = document.getElementById('myCanvas')
var ctx = c.getContext('2d')

var txt = 'Lorem ipsum dolor sit amet'

canvasTxt.fontSize = 24

canvasTxt.drawText(ctx, txt, 100, 200, 200, 200)

Dadurch wird Text in einem unsichtbaren Feld mit folgenden Positionen / Abmessungen gerendert:

{ x: 100, y: 200, height: 200, width: 200 }

Beispiel Geige

/* https://github.com/geongeorge/Canvas-Txt  */

const canvasTxt = window.canvasTxt.default;
const ctx = document.getElementById('myCanvas').getContext('2d');

const txt = "Lorem ipsum dolor sit amet";
const bounds = { width: 240, height: 80 };

let origin = { x: ctx.canvas.width / 2, y: ctx.canvas.height / 2, };
let offset = { x: origin.x - (bounds.width / 2), y: origin.y - (bounds.height / 2) };

canvasTxt.fontSize = 20;

ctx.fillStyle = '#C1A700';
ctx.fillRect(offset.x, offset.y, bounds.width, bounds.height);

ctx.fillStyle = '#FFFFFF';
canvasTxt.drawText(ctx, txt, offset.x, offset.y, bounds.width, bounds.height);
body {
  background: #111;
}

canvas {
  border: 1px solid #333;
  background: #222; /* Could alternatively be painted on the canvas */
}
<script src="https://unpkg.com/canvas-txt@2.0.6/build/index.js"></script>

<canvas id="myCanvas" width="300" height="160"></canvas>


Ich habe einige Variablen definiert, um das Beispiel selbst zu dokumentieren. Es wird auch die Zentrierung des Begrenzungsrahmens innerhalb der Leinwand behandelt. Ich habe auch ein Rechteck dahinter hinzugefügt, damit Sie es tatsächlich zentriert in Beziehung sehen können. Gute Arbeit! +1 Eine kleine Sache, die mir aufgefallen ist, ist, dass die führenden Leerzeichen der Zeilen, die umbrochen werden, nicht unterdrückt werden. Möglicherweise möchten Sie jede Zeile kürzen, z. B. habe ctx.fillText(txtline.trim(), textanchor, txtY)ich dies nur in Ihrer interaktiven Demo auf Ihrer Website bemerkt.
Mr. Polywhirl

@ Mr.Polywhirl Vielen Dank, dass Sie die Antwort geklärt haben. Ich habe das Trimmproblem behoben und die 2.0.9Version veröffentlicht. Die Demo-Site wird durch Aktualisieren der Paketversion behoben. Es gibt ein Problem mit mehreren Leerzeichen. Ich weiß nicht, ob es besser ist, ein Paket mit einer Meinung zu verwenden oder das Problem zu ignorieren. Ich habe Anfragen dafür von mehreren Orten erhalten. Ich ging voran und fügte trotzdem Trimm hinzu. Lorem ipsum dolor, sit <many spaces> amet Das war der Grund, warum ich es überhaupt nicht getan habe. Was denkst du, sollte ich mehrere Leerzeichen berücksichtigen und nur entfernen, wenn es nur eines gibt?
Geon George

Bearbeiten: Es scheint, dass der StackOverflow-Codeblock auch mehrere Leerzeichen ignoriert
Geon George

2

Ich denke auch nicht, dass dies möglich ist, aber eine Problemumgehung besteht darin, ein <p>Element zu erstellen und es mit Javascript zu positionieren.


Ja, das ist es, woran ich denke. Es ist nur so, dass Sie mit fillText()und strokeText()Dinge tun können, die über das hinausgehen, was CSS kann.
Turm

Ich habe dies nicht getestet, aber ich denke, dies ist möglicherweise eine bessere Lösung. Die anderen Lösungen hier mit fillText () sorgen dafür, dass der Text nicht ausgewählt (oder vermutlich eingefügt) werden kann.
Jerry Asher

2

Ich bin darauf gestoßen, weil ich das gleiche Problem hatte. Ich arbeite mit variabler Schriftgröße, daher wird dies berücksichtigt:

var texts=($(this).find('.noteContent').html()).split("<br>");
for (var k in texts) {
    ctx.fillText(texts[k], left, (top+((parseInt(ctx.font)+2)*k)));
}

Dabei ist .noteContent das inhaltsbearbeitbare Div, das der Benutzer bearbeitet hat (dies ist in jeder jQuery-Funktion verschachtelt), und ctx.font ist "14px Arial" (beachten Sie, dass die Pixelgröße an erster Stelle steht).


0

Das Canvas-Element unterstützt keine Zeichen wie Zeilenumbruch '\ n', Tab '\ t' oder <br /> -Tag.

Versuch es:

var newrow = mheight + 30;
ctx.fillStyle = "rgb(0, 0, 0)";
ctx.font = "bold 24px 'Verdana'";
ctx.textAlign = "center";
ctx.fillText("Game Over", mwidth, mheight); //first line
ctx.fillText("play again", mwidth, newrow); //second line 

oder vielleicht mehrere Zeilen:

var textArray = new Array('line2', 'line3', 'line4', 'line5');
var rows = 98;
ctx.fillStyle = "rgb(0, 0, 0)";
ctx.font = "bold 24px 'Verdana'";
ctx.textAlign = "center";
ctx.fillText("Game Over", mwidth, mheight); //first line

for(var i=0; i < textArray.length; ++i) {
rows += 30;
ctx.fillText(textArray[i], mwidth, rows); 
}  

0

Meine ES5-Lösung für das Problem:

var wrap_text = (ctx, text, x, y, lineHeight, maxWidth, textAlign) => {
  if(!textAlign) textAlign = 'center'
  ctx.textAlign = textAlign
  var words = text.split(' ')
  var lines = []
  var sliceFrom = 0
  for(var i = 0; i < words.length; i++) {
    var chunk = words.slice(sliceFrom, i).join(' ')
    var last = i === words.length - 1
    var bigger = ctx.measureText(chunk).width > maxWidth
    if(bigger) {
      lines.push(words.slice(sliceFrom, i).join(' '))
      sliceFrom = i
    }
    if(last) {
      lines.push(words.slice(sliceFrom, words.length).join(' '))
      sliceFrom = i
    }
  }
  var offsetY = 0
  var offsetX = 0
  if(textAlign === 'center') offsetX = maxWidth / 2
  for(var i = 0; i < lines.length; i++) {
    ctx.fillText(lines[i], x + offsetX, y + offsetY)
    offsetY = offsetY + lineHeight
  }
}

Weitere Informationen zu diesem Thema finden Sie in meinem Blog .

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.