HTML5 canvas ctx.fillText ne fera pas de sauts de ligne?

108

Je n'arrive pas à ajouter du texte à un canevas si le texte comprend "\ n". Je veux dire, les sauts de ligne ne montrent pas / ne fonctionnent pas.

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

Le code ci-dessus dessinera "s ome \n <br/> thing"sur une seule ligne.

Est-ce une limitation de fillText ou est-ce que je le fais mal? les "\ n" sont là, et ne sont pas imprimés, mais ils ne fonctionnent pas non plus.

Saut spectral
la source
1
voulez-vous envelopper automatiquement lorsque vous atteignez la fin? ou juste pour prendre en considération les caractères de nouvelle ligne présents dans le texte?
Gabriele Petrioli
Enveloppez le texte sur plusieurs lignes.
Tour du
Salut twodordan, cette limitation existe-t-elle à la fois sur Chrome et Mozilla? Les gens utilisent souvent du texte html simple qu'ils mettent sur la toile avec une position: absolue par exemple. Vous pouvez également faire deux fillText et déplacer l'origine Y de votre texte pour vos secondes lignes.
Tim
TL; DR: Appelez fillText()plusieurs fois et utilisez la hauteur de votre police pour séparer, ou utilisez developer.mozilla.org/en-US/docs/Web/API/TextMetrics developer.mozilla.org/en-US/docs/Web/API /… - ou, utilisez l'une des "solutions" très compliquées ci-dessous qui n'utilisent pas TextMetrics ...
Andrew

Réponses:

62

J'ai peur que ce soit une limitation de Canvas » fillText. Il n'y a pas de support multiligne. Pire encore, il n'y a pas de moyen intégré de mesurer la hauteur de la ligne, seulement la largeur, ce qui rend le faire vous-même encore plus difficile!

Beaucoup de gens ont écrit leur propre support multi-lignes, peut-être le projet le plus remarquable qui a été Mozilla Skywriter .

L'essentiel de ce que vous devrez faire est plusieurs fillTextappels tout en ajoutant la hauteur du texte à la valeur y à chaque fois. (mesurer la largeur de M est ce que les gens de skywriter font pour approximer le texte, je crois.)

Simon Sarris
la source
Je vous remercie! J'avais le sentiment que ce serait gênant ... C'est bien de savoir sur le SKYWRITER, mais je vais juste "attendre" jusqu'à ce que fillText () soit amélioré. Ce n'était pas une affaire très importante dans mon cas. Hah, pas de hauteur de ligne, c'est comme si quelqu'un faisait ça exprès. : D
Spectraljump
19
Honnêtement, je ne retiendrais pas votre souffle sur le fait que fillText () soit "amélioré" pour supporter cela, car j'ai le sentiment que c'est ainsi qu'il est destiné à être utilisé (appels multiples et calcul du yOffset vous-même). Je pense qu'une grande partie de la puissance de l'API canvas est qu'elle sépare la fonctionnalité de dessin de niveau inférieur de ce que vous pouvez déjà faire (effectuer les mesures nécessaires). En outre, vous pouvez connaître la hauteur du texte simplement en fournissant la taille du texte en pixels; en d'autres termes: context.font = "16px Arial"; - vous avez la hauteur là-bas; la largeur est la seule qui soit dynamique.
Lev
1
Certaines propriétés supplémentaires pour measureText()ont été ajoutées que je pense pourrait résoudre le problème. Chrome a un indicateur pour les activer, mais les autres navigateurs ne le font pas ... encore!
SWdV
@SWdV juste pour être clair, ceux-ci sont dans la spécification depuis des années maintenant, cela peut prendre des années jusqu'à ce que nous ayons une adoption suffisamment large pour les utiliser :(
Simon Sarris
67

Si vous voulez simplement vous occuper des caractères de nouvelle ligne dans le texte, vous pouvez le simuler en divisant le texte aux nouvelles lignes et en appelant plusieurs fois le fillText()

Quelque chose comme 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>


Je viens de créer un exemple de preuve de concept d'emballage ( enveloppement absolu à la largeur spécifiée. Pas de rupture de mots de manipulation, pour le moment )
à 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>


Et une preuve de concept enveloppant les mots ( cassant les espaces ).
exemple sur 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>


Dans les deuxième et troisième exemples, j'utilise la measureText()méthode qui montre la longueur ( en pixels ) d'une chaîne lors de l'impression.

Gabriele Petrioli
la source
comment justifier tout le long texte?
Amirhossein Tarmast le
Si vous avez besoin d'un texte long et justifié, pourquoi utiliseriez-vous une toile?
Mike 'Pomax' Kamermans
39

Peut-être que je viens à cette fête un peu tard, mais j'ai trouvé le tutoriel suivant pour envelopper du texte sur une toile parfait.

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

À partir de là, j'ai pu penser à faire fonctionner plusieurs lignes (désolé Ramirez, le vôtre n'a pas fonctionné pour moi!). Mon code complet pour envelopper du texte dans un canevas est le suivant:

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

cest l'ID de ma toile et textl'ID de ma zone de texte.

Comme vous pouvez probablement le voir, j'utilise une police non standard. Vous pouvez utiliser @ font-face tant que vous avez utilisé la police sur du texte AVANT de manipuler le canevas - sinon le canevas ne prendra pas la police.

J'espère que cela aide quelqu'un.

Colin Wiseman
la source
26

Divisez le texte en lignes et dessinez chacune séparément:

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
la source
17

Voici ma solution, en modifiant la fonction populaire wrapText () qui est déjà présentée ici. J'utilise la fonctionnalité de prototypage de JavaScript afin que vous puissiez appeler la fonction à partir du contexte de canevas.

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

Utilisation de base:

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

Voici une démonstration que j'ai préparée : http://jsfiddle.net/7RdbL/

Jake
la source
A fonctionné comme un charme. Je vous remercie.
couzzi
13

Je viens d'étendre le CanvasRenderingContext2D en ajoutant deux fonctions: mlFillText et mlStrokeText.

Vous pouvez trouver la dernière version dans GitHub :

Avec ces fonctions, vous pouvez remplir / tracer du texte miltiline dans une boîte. Vous pouvez aligner le texte verticalement et horizontalement. (Il prend en compte les \ n et peut également justifier le texte).

Les prototypes sont:

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

Où vAlign peut être: "top", "center" ou "button" Et hAlign peut être: "left", "center", "right" ou "justify"

Vous pouvez tester la lib ici: http://jsfiddle.net/4WRZj/1/

entrez la description de l'image ici

Voici le code de la bibliothèque:

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

Et voici l'exemple d'utilisation:

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
la source
Uncaught ReferenceError: Words is not definedSi j'essaye de changer de police. Par exemple: ctx.font = '40px Arial';- essayez de mettre ça dans votre violon
psycho brm
Btw, d'où vient la Wordsvariable (sensible à la casse) ?? Ce n'est défini nulle part. Cette partie du code n'est exécutée que lorsque vous changez de police.
psycho brm
1
@psychobrm Vous avez absolument raison. C'est un bug (je le corrige déjà). Cette partie de code n'est exécutée que si vous devez diviser un mot en deux lignes. Je vous remercie!
jbaylina
J'ai fait quelques mises à niveau dont j'avais besoin: rendre les espaces, rendre les nouvelles lignes de début / de fin, rendre le trait et remplir avec un appel (ne pas mesurer le texte deux fois), j'ai également dû changer l'itération, car for inne fonctionne pas bien avec extended Array.prototype. Pourriez-vous le mettre sur github afin que nous puissions l'itérer?
psycho brm
@psychobrm J'ai fusionné vos modifications. Je vous remercie!
jbaylina
8

En utilisant javascript, j'ai développé une solution. Ce n'est pas beau mais cela a fonctionné pour moi:


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

J'espère que cela pourra aider!

Ramirez
la source
1
bonjour, supposons que mon texte est comme ce texte var = « aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa »; alors que se passe-t-il dans la toile ???
Amol Navsupe
Il sortira du canevas, car @Ramirez n'a pas mis le paramètre maxWidth à fillText :)
KaHa6uc
6

Le code de retour à la ligne (coupure aux espaces) fourni par @Gaby Petrioli est très utile. J'ai étendu son code pour prendre en charge les caractères de nouvelle ligne \n. De plus, il est souvent utile d'avoir les dimensions du cadre de sélection, donc multiMeasureText()renvoie à la fois la largeur et la hauteur.

Vous pouvez voir le code ici: http://jsfiddle.net/jeffchan/WHgaY/76/

Jeffchan
la source
les liens expirent, veuillez mettre le code dans cette réponse même si vous avez un lien fonctionnel. Si jsfiddle s'arrête, cette réponse devient totalement inutile telle quelle.
Mike 'Pomax' Kamermans
5

Voici une version de Colin wrapText()qui prend également en charge le texte centré verticalement avec 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
la source
5

Si vous n'avez besoin que de deux lignes de texte, vous pouvez les diviser en deux appels fillText différents et attribuer à chacun une ligne de base différente.

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

Cette question ne concerne pas le fonctionnement de la toile. Si vous voulez un saut de ligne, ajustez simplement les coordonnées de votre prochain ctx.fillText.

ctx.fillText("line1", w,x,y,z)
ctx.fillText("line2", w,x,y,z+20)
Jayjey
la source
3

Je pense que vous pouvez toujours compter sur CSS

ctx.measureText().height doesnt exist.

Heureusement, grâce à CSS hack-ardry (voir Métriques typographiques pour plus de moyens de corriger les anciennes implémentations de l'utilisation des mesures CSS), nous pouvons trouver la hauteur du texte en mesurant l'offsetHeight de a avec les mêmes propriétés de police:

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

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

MarioF
la source
C'est une bonne solution si vous avez la mémoire nécessaire pour créer un élément comme celui-ci à chaque fois que vous devez mesurer. Vous pouvez également ctx.save()alors, ctx.font = '12pt Arial' alors parseInt( ctx.font, 10 ),. Notez que j'utilise «pt» lors du réglage. Il se traduira alors en PX et pourra se transformer en chiffre à consommer comme la hauteur de la police.
Eric Hodonsky
3

J'ai créé une petite bibliothèque pour ce scénario ici: Canvas-Txt

Il rend le texte sur plusieurs lignes et offre des modes d'alignement décents.

Pour l'utiliser, vous devrez soit l'installer, soit utiliser un CDN.

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)

Cela rendra le texte dans une boîte invisible avec la position / les dimensions de:

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

Exemple de violon

/* 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
la source
Je suis allé de l'avant et j'ai défini quelques variables pour aider à "auto-documenter" l'exemple. Il gère également le centrage du cadre de sélection dans la zone de dessin. J'ai également ajouté un rectangle derrière, pour que vous puissiez le voir centré par rapport. Bon travail! +1 Une petite chose que j'ai remarquée est que les lignes qui s'enroulent n'auront pas leurs espaces de début supprimés. Vous voudrez peut-être couper chaque ligne, par exemple, ctx.fillText(txtline.trim(), textanchor, txtY)je n'ai remarqué cela que dans votre démo interactive sur votre site Web.
M. Polywhirl le
@ Mr.Polywhirl Merci d'avoir clarifié la réponse. J'ai résolu le problème de coupe et publié la 2.0.9version. Le site de démonstration est corrigé en mettant à jour la version du package. Il y a un problème avec plusieurs espaces. Je ne sais pas s'il vaut mieux aller avec un paquet opiniâtre ou ignorer le problème. J'ai reçu des demandes pour cela de plusieurs endroits. Je suis allé de l'avant et j'ai quand même ajouté des garnitures. Lorem ipsum dolor, sit <many spaces> amet c'était la raison pour laquelle je ne l'ai pas fait en premier lieu. Que pensez-vous que je devrais considérer plusieurs espaces et ne supprimer que s'il n'y en a qu'un?
Geon George le
Edit: il semble que le bloc de code StackOverflow ignore également les espaces multiples
Geon George
2

Je ne pense pas que ce soit possible non plus, mais une solution de contournement pour cela est de créer un <p>élément et de le positionner avec Javascript.

Harmen
la source
Oui, c'est ce que je pense faire. C'est juste qu'avec fillText()et strokeText(), vous pouvez faire des choses au-delà de ce que CSS peut faire.
Tour du
Je n'ai pas testé cela, mais je pense que cela peut être une meilleure solution - les autres solutions ici utilisant fillText () font en sorte que le texte ne puisse pas être sélectionné (ou vraisemblablement collé).
Jerry Asher
2

Je suis tombé sur cela en raison du même problème. Je travaille avec une taille de police variable, donc cela prend cela en compte:

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

où .noteContent est le div contenteditable que l'utilisateur a modifié (il est imbriqué dans un jQuery chaque fonction), et ctx.font est "14px Arial" (notez que la taille du pixel vient en premier)

MaKR
la source
0

L'élément Canvas ne prend pas en charge les caractères tels que le retour à la ligne '\ n', la tabulation '\ t' ou la balise <br />.

Essayez-le:

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 

ou peut-être plusieurs lignes:

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
la source
0

Ma solution ES5 pour le problème:

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

Plus d'informations sur le problème sont sur mon blog .

Oleg Berman
la source