Il semble que je ne puisse pas ajouter de texte à un canevas si le texte inclut "\ n". Je veux dire, les sauts de ligne ne montrent pas/travail.
ctxPaint.fillText("s ome \n \\n <br/> thing", x, y);
Le code ci-dessus affichera "s ome \n <br/> thing"
, sur une ligne.
Est-ce une limitation de fillText ou est-ce que je me trompe? les "\ n" sont là et ne sont pas imprimés, mais ils ne fonctionnent pas non plus.
J'ai bien peur que ce soit une limitation de la variable fillText
de Canvas. Il n'y a pas de support multiligne. Quoi de pire, il n'y a pas de moyen intégré de mesurer la hauteur de ligne, mais seulement la largeur, ce qui rend la tâche encore plus difficile!
Beaucoup de gens ont écrit leur propre support multiligne, le projet le plus remarquable qui soit est Mozilla Skywriter .
Vous devez effectuer plusieurs appels fillText
tout en ajoutant la hauteur du texte à la valeur y à chaque fois. (Mesurer la largeur de M est ce que les gens du skywriter font pour se rapprocher du texte, je crois.)
Si vous voulez juste vous occuper des caractères de nouvelle ligne dans le texte, vous pouvez le simuler en scindant le texte à la nouvelle ligne 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 faire une preuve de concept d’emballage (emballage absolu à la largeur spécifiée. Aucun mot de manipulation ne se casse, mais)
exemple à 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 de Word-wrapping (break at spaces).
exemple à 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 méthode measureText()
qui indique la durée (en pixels) d'une chaîne lors de son impression.
Peut-être arriver un peu tard à cette soirée, mais j’ai trouvé le tutoriel suivant pour envelopper du texte sur une toile.
http://www.html5canvastutorials.com/tutorials/html5-canvas-wrap-text-tutorial/
A partir de là, j'ai pu penser à utiliser plusieurs lignes (désolé Ramirez, la tienne ne m'a pas fonctionné!). Mon code complet pour insérer 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>
Où c
est l'ID de ma toile et text
est l'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 ça aide quelqu'un.
Divisez le texte en lignes et dessinez-les 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;
}
}
Voici ma solution, modifiant la fonction populaire wrapText () 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 la zone de dessin.
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 composée: http://jsfiddle.net/7RdbL/
En utilisant javascript, j'ai développé une solution. Ce n'est pas beau mais ça 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!
Le code pour le retour à la ligne fourni par @Gaby Petrioli est très utile . J'ai étendu son code afin de 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/
Je viens d'étendre 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 zone. Vous pouvez aligner le texte verticalement et horizontalement. (Il prend en compte les\n et peut aussi justifier le texte).
Les prototypes sont:
fonction mlFillText (texte, x, y, w, h, vAlign, hAlign, hauteur de ligne); fonction mlStrokeText (texte, x, y, w, h, vAlign, hAlign, ligne)
Où vAlign peut être: "haut", "centre" ou "bouton" Et hAlign peut être: "gauche", "centre", "droite" ou "justifier"
Vous pouvez tester la lib ici: http://jsfiddle.net/4WRZj/1/
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);
Si vous n'avez besoin que de deux lignes de texte, vous pouvez les scinder en deux appels fillText différents et leur attribuer 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);
Voici une version de wrapText()
de Colin qui supporte également 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;
}
};
Je pense que vous pouvez toujours compter sur CSS
ctx.measureText().height doesn’t exist.
Heureusement, via CSS hack-ardry (voir Métriques typographiques pour plus de façons de corriger les anciennes implémentations utilisant des mesures CSS), nous pouvons trouver la hauteur du texte en mesurant l'offsetHauteur d'un 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;
à partir de: http://www.html5rocks.com/fr/tutorials/canvas/texteffects/
Je ne pense pas que ce soit possible non plus, mais une solution de contournement consiste à créer un élément <p>
et à le positionner avec Javascript.
Je suis tombé dessus parce que j'avais le même problème. Je travaille avec une taille de police variable, donc cela en tient 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 la division contenteditable que l'utilisateur a modifiée (elle est imbriquée dans une fonction jQuery), et ctx.font est "14px Arial" (notez que la taille en pixels vient en premier)
L'élément de canevas ne prend pas en charge des caractères tels que les balises newline '\ n', tab '\ t' ou <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);
}
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 sujet est sur mon blog .
J'ai créé un module npm simple pour cette utilisation exacte . https://www.npmjs.com/package/canvas-txt
Vous pouvez diviser le texte en multiligne automatiquement et également sur \n