HTML5 canvas ctx.fillText nie będzie robić znaków końca linii?

108

Nie mogę dodać tekstu do obszaru roboczego, jeśli tekst zawiera „\ n”. To znaczy, podziały linii nie pokazują / nie działają.

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

Powyższy kod będzie rysowany "s ome \n <br/> thing"w jednej linii.

Czy to jest ograniczenie fillText, czy robię to źle? "\ n" są tam i nie są drukowane, ale też nie działają.

Spectraljump
źródło
1
czy chcesz automatycznie zawijać po dotarciu do końca? czy po prostu wziąć pod uwagę znaki nowej linii obecne w tekście?
Gabriele Petrioli,
Zawiń tekst w wiele linii.
Wieża
Cześć twodordan, czy to ograniczenie istnieje zarówno w przeglądarce Chrome, jak i Mozilli? Ludzie często używają prostego tekstu HTML, który umieszczają na płótnie z pozycją: na przykład bezwzględną. Możesz także zrobić dwa fillText i przesunąć początek Y tekstu dla drugich linii.
Tim
TL; DR: Zadzwoń fillText()wiele razy i użyj wysokości czcionki do oddzielenia lub użyj developer.mozilla.org/en-US/docs/Web/API/TextMetrics developer.mozilla.org/en-US/docs/Web/API /… - lub użyj jednego z bardzo skomplikowanych „rozwiązań” poniżej, które nie używają TextMetrics ...
Andrew

Odpowiedzi:

62

Obawiam się, że to ograniczenie Canvas ” fillText. Nie ma obsługi wielu linii. Co gorsza, nie ma wbudowanego sposobu pomiaru wysokości linii, tylko szerokość, co sprawia, że ​​robienie tego samemu jest jeszcze trudniejsze!

Wiele osób napisało własne wsparcie dla wielu linii, być może najbardziej znanym projektem, jaki ma Mozilla Skywriter .

Istotą tego, co musisz zrobić, jest wiele fillTextwywołań, za każdym razem dodając wysokość tekstu do wartości y. (Wydaje mi się, że mierzenie szerokości M jest tym, co robią skywriterowie, aby przybliżać tekst).

Simon Sarris
źródło
Dziękuję Ci! Miałem przeczucie, że byłoby to uciążliwe ... Miło wiedzieć o SKYWRITERZE, ale ja po prostu „poczekam”, aż funkcja fillText () zostanie poprawiona. W moim przypadku nie była to strasznie ważna transakcja. Hah, bez wysokości linii, wygląda na to, że ktoś zrobił to specjalnie. : D
Spectraljump
19
Szczerze mówiąc, nie wstrzymywałbym oddechu, gdyby funkcja fillText () została „ulepszona”, aby to wspierać, ponieważ mam wrażenie, że jest to zamierzone (wielokrotne wywołania i samodzielne obliczanie yOffset). Myślę, że dużą zaletą API Canvas jest to, że oddziela on niższą funkcjonalność rysowania od tego, co już możesz zrobić (wykonać niezbędne pomiary). Możesz także poznać wysokość tekstu, podając rozmiar tekstu w pikselach; innymi słowy: context.font = "16px Arial"; - masz tam wysokość; szerokość jest jedyną dynamiczną.
Lew
1
Niektóre dodatkowe właściwości dla measureText()zostały dodane, które myślę, że może rozwiązać ten problem. Chrome ma flagę, która je włącza, ale inne przeglądarki nie ... jeszcze!
SWdV,
@SWdV, żeby było jasne, te są w specyfikacji od lat, może minąć jeszcze wiele lat, zanim będziemy mieć wystarczająco szeroką akceptację do użycia :(
Simon Sarris,
67

Jeśli chcesz tylko zająć się znakami nowej linii w tekście, możesz to zasymulować, dzieląc tekst na nowe linie i wywołując wielokrotnie fillText()

Coś jak 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>


Właśnie wykonałem zawijany dowód koncepcji ( absolutne zawijanie przy określonej szerokości. Nie ma jeszcze łamania słów obsługujących ),
przykład na http://jsfiddle.net/BaG4J/2/

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>


Oraz zawijanie słów ( przerywanie spacjami ) dowód koncepcji.
przykład pod adresem 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>


W drugim i trzecim przykładzie używam measureText()metody, która pokazuje, jak długo ( w pikselach ) ciąg będzie drukowany.

Gabriele Petrioli
źródło
jak uzasadnić cały długi tekst?
Amirhossein Tarmast
Jeśli potrzebujesz długiego, wyjustowanego tekstu, dlaczego miałbyś używać płótna?
Mike 'Pomax' Kamermans
39

Może przyjdę na tę imprezę trochę późno, ale poniższy samouczek dotyczący zawijania tekstu na płótnie był idealny.

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

Z tego powodu mogłem pomyśleć, aby działało wiele linii (przepraszam Ramirez, twój nie działa dla mnie!). Mój kompletny kod do zawijania tekstu na płótnie jest następujący:

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

Gdzie cjest identyfikator mojego płótna i textjest identyfikatorem mojego pola tekstowego.

Jak pewnie widzisz, używam niestandardowej czcionki. Możesz użyć @ font-face, o ile użyłeś czcionki na jakimś tekście PRZED manipulowaniem płótnem - w przeciwnym razie płótno nie pobierze czcionki.

Mam nadzieję, że to komuś pomoże.

Colin Wiseman
źródło
26

Podziel tekst na linie i narysuj je osobno:

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;
  }
}
Rok Strniša
źródło
17

Oto moje rozwiązanie, modyfikujące popularną funkcję wrapText (), która jest już tutaj przedstawiona. Używam funkcji prototypowania JavaScript, aby można było wywołać funkcję z kontekstu kanwy.

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

Podstawowe użycie:

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

Oto prezentacja, którą przygotowałem: http://jsfiddle.net/7RdbL/

Jake
źródło
Działał jak urok. Dziękuję Ci.
couzzi
13

Właśnie rozszerzyłem CanvasRenderingContext2D o dwie funkcje: mlFillText i mlStrokeText.

Ostatnią wersję można znaleźć w GitHub :

Dzięki tym funkcjom możesz wypełnić / obrysować tekst miltilinowy w ramce. Możesz wyrównać tekst w pionie i poziomie. (Bierze pod uwagę \ n i może również uzasadniać tekst).

Prototypy to:

funkcja mlFillText (text, x, y, w, h, vAlign, hAlign, lineheight); function mlStrokeText (text, x, y, w, h, vAlign, hAlign, lineheight);

Gdzie vAlign może być: „top”, „center” lub „button”, a hAlign może być: „left”, „center”, „right” lub „justify”

Możesz przetestować bibliotekę tutaj: http://jsfiddle.net/4WRZj/1/

wprowadź opis obrazu tutaj

Oto kod biblioteki:

// 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");
    };
})();

A oto przykład użycia:

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);
jbaylina
źródło
Uncaught ReferenceError: Words is not definedJeśli spróbuję zmienić czcionkę. Na przykład: ctx.font = '40px Arial';- spróbuj włożyć to w swoje skrzypce
psycho brm
Btw, skąd do diabła Wordsbierze się zmienna (uwzględniająca wielkość liter)? Nigdzie nie jest zdefiniowane. Ta część kodu jest wykonywana tylko wtedy, gdy zmienisz czcionkę ...
psycho brm
1
@psychobrm Masz absolutną rację. To błąd (już go naprawiłem). Ta część kodu jest wykonywana tylko wtedy, gdy musisz podzielić słowo na dwie linie. Dziękuję Ci!
jbaylina
Dokonałem pewnych ulepszeń, których potrzebowałem: renderowanie spacji, renderowanie wiodących / końcowych znaków nowej linii, renderowanie obrysu i wypełnianie jednym wywołaniem (nie mierz tekstu dwa razy), musiałem również zmienić iterację, ponieważ for innie działa dobrze z rozszerzonym Array.prototype. Czy możesz umieścić go na githubie, abyśmy mogli go powtórzyć?
psycho brm
@psychobrm Połączyłem twoje zmiany. Dziękuję Ci!
jbaylina
8

Korzystając z javascript opracowałem rozwiązanie. To nie jest piękne, ale zadziałało:


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

Mam nadzieję, że to pomoże!

Ramirez
źródło
1
Witam, załóżmy, że mój tekst jest jak tego tekstu var = „aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa”; to co się dzieje na płótnie ???
Amol Navsupe
To wyjdzie z płótna, ponieważ @Ramirez nie wstawił parametru maxWidth do fillText :)
KaHa6uc
6

Bardzo pomocny jest kod zawijania wyrazów (łamanie spacji) dostarczony przez @Gaby Petrioli . Rozszerzyłem jego kod, aby zapewnić obsługę znaków nowej linii \n. Ponadto często warto mieć wymiary obwiedni, więc multiMeasureText()zwraca zarówno szerokość, jak i wysokość.

Możesz zobaczyć kod tutaj: http://jsfiddle.net/jeffchan/WHgaY/76/

jeffchan
źródło
linki wygasają, proszę umieścić kod w tej odpowiedzi, nawet jeśli masz działający link. Jeśli jsfiddle wyłączy się, ta odpowiedź stanie się całkowicie bezużyteczna.
Mike 'Pomax' Kamermans
5

Oto wersja Colina, wrapText()która obsługuje również tekst wyśrodkowany w pionie z 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;
    }
};
Tom Söderlund
źródło
5

Jeśli potrzebujesz tylko dwóch wierszy tekstu, możesz podzielić je na dwa różne wywołania fillText i nadać każdemu inny punkt odniesienia.

ctx.textBaseline="bottom";
ctx.fillText("First line", x-position, y-position);
ctx.textBaseline="top";
ctx.fillText("Second line", x-position, y-position);
rzeczEvery
źródło
4

To pytanie nie dotyczy tego, jak działa płótno. Jeśli chcesz przerwać linię, po prostu dostosuj współrzędne następnego ctx.fillText.

ctx.fillText("line1", w,x,y,z)
ctx.fillText("line2", w,x,y,z+20)
jayjey
źródło
3

Myślę, że nadal możesz polegać na CSS

ctx.measureText().height doesnt exist.

Na szczęście dzięki hack-ardry CSS (zobacz Metryki typograficzne, aby uzyskać więcej sposobów naprawienia starszych implementacji pomiarów CSS), możemy znaleźć wysokość tekstu, mierząc offsetHeight a z tymi samymi właściwościami czcionki:

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

z: http://www.html5rocks.com/en/tutorials/canvas/texteffects/

MarioF
źródło
To dobre rozwiązanie, jeśli masz pamięć, aby zbudować taki element za każdym razem, gdy musisz dokonać pomiaru. Można również ctx.save()wtedy, ctx.font = '12pt Arial' wtedy parseInt( ctx.font, 10 ). Zauważ, że podczas ustawiania używam „pt”. Następnie zostanie przetłumaczony na PX i będzie mógł przekształcić się w cyfrę do spożycia jako wysokość czcionki.
Eric Hodonsky,
3

Stworzyłem małą bibliotekę dla tego scenariusza tutaj: Canvas-Txt

Renderuje tekst w wielu wierszach i oferuje przyzwoite tryby wyrównania.

Aby z tego skorzystać, musisz go zainstalować lub użyć CDN.

Instalacja

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)

Spowoduje to wyświetlenie tekstu w niewidocznym polu z pozycją / wymiarami:

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

Przykład Fiddle

/* 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/[email protected]/build/index.js"></script>

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

Geon George
źródło
Poszedłem do przodu i zdefiniowałem kilka zmiennych, aby pomóc w „samodzielnym udokumentowaniu” przykładu. Obsługuje także wyśrodkowanie obwiedni w obszarze roboczym. Dodałem również prostokąt z tyłu, więc możesz zobaczyć go wyśrodkowany względem. Świetna robota! +1 Jedną małą rzeczą, jaką zauważyłem, jest to, że linie, które zawijają, nie będą miały pomniejszonych spacji wiodących. Możesz chcieć przyciąć każdą linię, np. ctx.fillText(txtline.trim(), textanchor, txtY)Zauważyłem to tylko w twojej interaktywnej demonstracji na twojej stronie.
Pan Polywhirl
@ Mr.Polywhirl Dziękuję za wyjaśnienie odpowiedzi. Naprawiłem problem z wykończeniem i opublikowałem 2.0.9wersję. Witryna demonstracyjna jest naprawiana poprzez aktualizację wersji pakietu. Wystąpił problem z wieloma spacjami. Nie wiem, czy lepiej iść z upartym pakietem, czy zignorować problem. Otrzymuję prośby o to z wielu miejsc. Poszedłem dalej i dodałem trymowanie. Lorem ipsum dolor, sit <many spaces> amet to był powód, dla którego tego nie zrobiłem. Jak myślisz, co powinienem wziąć pod uwagę wiele spacji i usunąć tylko wtedy, gdy jest tylko jedna?
Geon George
Edycja: wydaje się, że blok kodu StackOverflow ignoruje również wiele spacji
Geon George
2

Nie sądzę, żeby to było również możliwe, ale obejściem tego problemu jest utworzenie <p>elementu i umieszczenie go za pomocą JavaScript.

Harmen
źródło
Tak, właśnie o tym myślę. Po prostu za pomocą fillText()i strokeText()możesz robić rzeczy wykraczające poza możliwości CSS.
Wieża
Nie testowałem tego, ale myślę, że może to być lepsze rozwiązanie - inne rozwiązania tutaj wykorzystujące funkcję fillText () sprawiają, że tekst nie może być zaznaczony (lub prawdopodobnie wklejony).
Jerry Asher
2

Zdarzyło mi się to z powodu tego samego problemu. Pracuję ze zmiennym rozmiarem czcionki, więc bierze to pod uwagę:

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

gdzie .noteContent to edytowalny element div edytowany przez użytkownika (jest to zagnieżdżone w jQuery każdej funkcji), a ctx.font to „14px Arial” (zwróć uwagę, że rozmiar piksela jest na pierwszym miejscu)

MaKR
źródło
0

Element Canvas nie obsługuje takich znaków jak nowy wiersz '\ n', tab '\ t' czy znacznik <br />.

Spróbuj:

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 

lub może wiele linii:

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); 
}  
Dariusz J
źródło
0

Moje rozwiązanie problemu z ES5:

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
  }
}

Więcej informacji na ten temat na moim blogu .

Oleg Berman
źródło