web-dev-qa-db-fra.com

Espacement des lettres dans l'élément de toile

La question dit tout à peu près. J'ai cherché et commencé à craindre que ce soit impossible.

J'ai cet élément de toile sur lequel je dessine le texte. Je souhaite définir un espacement des lettres similaire à l'attribut CSS letter-spacing. J'entends par là l'augmentation du nombre de pixels entre les lettres lorsqu'une chaîne est dessinée.

Mon code pour dessiner le texte est comme ça, ctx est la variable de contexte canvas. 

ctx.font = "3em sheepsans";
ctx.textBaseline = "middle";
ctx.textAlign = "center";
ctx.fillStyle = "rgb(255, 255, 255)";
ctx.fillText("Blah blah text", 1024 / 2, 768 / 2);

J'ai essayé d'ajouter ctx.letterSpacing = "2px"; avant le dessin, mais sans succès. Y a-t-il un moyen de faire cela simplement avec un paramètre simple ou dois-je créer une fonction permettant de dessiner chaque personnage individuellement avec l'espacement en tête?

30
Pinpickle

Je ne sais pas si cela devrait fonctionner (selon les spécifications), mais dans certains navigateurs (Chrome), vous pouvez définir la propriété CSS letter-spacing sur l'élément <canvas> lui-même, qui sera appliqué à tout le texte dessiné dans le contexte. . (Fonctionne dans Chrome v56, ne fonctionne pas dans Firefox v51 ou IE v11.)

Notez que dans Chrome v56, vous devez récupérer le contexte canvas 2d (et redéfinir les valeurs qui vous intéressent) après chaque modification du style letter-spacing; l'espacement semble être cuit dans le contexte 2d que vous obtenez.

Exemple: https://jsfiddle.net/hg4pbsne/1/

var inp = document.querySelectorAll('input'),
    can = document.querySelector('canvas'),
    ctx = can.getContext('2d');
    can.width = can.offsetWidth;

[].forEach.call(inp,function(inp){ inp.addEventListener('input', redraw, false) });
redraw();

function redraw(){
  ctx.clearRect(0,0,can.width,can.height);
  can.style.letterSpacing = inp[0].value + 'px';

  ctx = can.getContext('2d');
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  ctx.font = '4em sans-serif';
  ctx.fillText('Hello', can.width/2, can.height*1/4);
  
  can.style.letterSpacing = inp[1].value + 'px';
  ctx = can.getContext('2d');
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  ctx.font = '4em sans-serif';
  ctx.fillText('World', can.width/2, can.height*3/4);
};
canvas { background:white }
canvas, label { display:block; width:400px; margin:0.5em auto }
<canvas></canvas>
<label>hello spacing: <input type="range" min="-20" max="40" value="1" step="0.1"></label>
<label>world spacing: <input type="range" min="-20" max="40" value="1" step="0.1"></label>


Réponse originale sur plusieurs navigateurs:

Ce n'est pas possible; Le canevas HTML5 ne dispose pas de tout le pouvoir de transformation de texte du CSS en HTML. Je suggérerais que vous deviez combiner les technologies appropriées pour chaque utilisation. Utilisez HTML en couches avec Canvas et peut-être même en SVG, chacun faisant ce qu’il fait de mieux.

Notez également que le fait de «rouler vous-même» (dessiner chaque caractère avec un décalage personnalisé) produira des résultats erronés pour la plupart des polices, car il existe des paires de crénage des lettres et des repères de polices alignés en pixels.

17
Phrogz

Vous ne pouvez pas définir la propriété d'espacement des lettres, mais vous pouvez obtenir un espacement plus large des lettres dans la zone de dessin en insérant l'un des différents espaces entre chaque lettre de la chaîne. Par exemple

ctx.font = "3em sheepsans";
ctx.textBaseline = "middle";
ctx.textAlign = "center";
ctx.fillStyle = "rgb(255, 255, 255)";
var ctext = "Blah blah text".split("").join(String.fromCharCode(8202))
ctx.fillText(ctext, 1024 / 2, 768 / 2);

Cela insérera un espace de cheveux entre chaque lettre. 

En utilisant 8201 (au lieu de 8202), vous insérerez un espace légèrement plus large espace mince

Pour plus d'options sur les espaces, voir cette liste d'espaces Unicode

Cette méthode vous aidera à préserver le crénage de la police beaucoup plus facilement que le positionnement manuel de chaque lettre. Toutefois, vous ne pourrez pas resserrer l'espacement des lettres de cette façon. 

32
Yanofsky

Vous ne pouvez pas définir l'espacement des lettres en tant que propriété du contexte Canvas. Désolé, vous ne pouvez obtenir cet effet qu'en espaçant manuellement. (Comme dans, dessiner chaque lettre en augmentant le x de quelques pixels manuellement)

Pour l'enregistrement, vous pouvez définir quelques propriétés de texte à l'aide de ctx.font, mais l'espacement des lettres n'en fait pas partie. Ceux que vous pouvez définissez sont les suivants: "type de police variante de police taille de police taille de police/hauteur de ligne famille de polices"

Par exemple, vous pouvez techniquement écrire ctx.font = "bold normal normal 12px/normal Verdana" (ou toute omission de ceux-ci) et l'analyse sera correcte.

6
Simon Sarris

Pas vrai. Vous pouvez ajouter une propriété d'espacement des lettres à l'élément canvas dans css et cela fonctionne parfaitement. Pas besoin de solutions de contournement compliquées. Je viens de le comprendre maintenant dans mon projet de toile. c'est à dire.: Toile { largeur: 480px; hauteur: 350px; marge: 30px auto 0; remplissage: 15px 0 0 0; fond: rose; bloc de visualisation; bord: 2px en pointillé brun; espacement des lettres: 0.1em; }

5
Maria Campbell

voici quelques coffeescript qui vous permet de définir le crénage à votre contexte comme si 

tctx = tcanv.getContext('2d')
tctx.kerning = 10
tctx.fillStyle = 'black'
tctx.fillText 'Hello World!', 10, 10

le code de support est:

_fillText = CanvasRenderingContext2D::fillText
CanvasRenderingContext2D::fillText = (str, x, y, args...) ->

  # no kerning? default behavior
  return _fillText.apply this, arguments unless @kerning?

  # we need to remember some stuff as we loop
  offset = 0

  _.each str, (letter) =>

    _fillText.apply this, [
      letter
      x + offset + @kerning
      y
    ].concat args # in case any additional args get sent to fillText at any time

    offset += @measureText(letter).width + @kerning

Le javascript serait

var _fillText,
  __slice = [].slice;

_fillText = CanvasRenderingContext2D.prototype.fillText;

CanvasRenderingContext2D.prototype.fillText = function() {
  var args, offset, str, x, y,
    _this = this;

  str = arguments[0], x = arguments[1], y = arguments[2], args = 4 <= arguments.length ? __slice.call(arguments, 3) : [];
  if (this.kerning == null) {
    return _fillText.apply(this, arguments);
  }
  offset = 0;

  return _.each(str, function(letter) {
    _fillText.apply(_this, [letter, x + offset + _this.kerning, y].concat(args));
    offset += _this.measureText(letter).width + _this.kerning;
  });
};
3
Funkodebat

Pour permettre les "paires de crénage" et similaires, j'ai écrit ce qui suit. Il devrait en tenir compte, et les tests approximatifs suggèrent que oui. Si vous avez des commentaires à ce sujet, je vous renvoie à ma question sur le sujet ( Ajout d'espacement des lettres dans HTML Canvas )

Fondamentalement, il utilise measureText () pour obtenir la largeur de la chaîne entière, puis supprime le premier caractère de la chaîne et mesure la largeur de la chaîne restante, et utilise la différence pour calculer le positionnement correct - prenant ainsi en compte les paires de crénage et le même. Voir le lien donné pour plus de pseudocodes.

Voici le code HTML:

<canvas id="Test1" width="800px" height="200px"><p>Your browser does not support canvas.</p></canvas>

Voici le code:

this.fillTextWithSpacing = function(context, text, x, y, spacing)
{
    //Start at position (X, Y).
    //Measure wAll, the width of the entire string using measureText()
    wAll = context.measureText(text).width;

    do
    {
    //Remove the first character from the string
    char = text.substr(0, 1);
    text = text.substr(1);

    //Print the first character at position (X, Y) using fillText()
    context.fillText(char, x, y);

    //Measure wShorter, the width of the resulting shorter string using measureText().
    if (text == "")
        wShorter = 0;
    else
        wShorter = context.measureText(text).width;

    //Subtract the width of the shorter string from the width of the entire string, giving the kerned width of the character, wChar = wAll - wShorter
    wChar = wAll - wShorter;

    //Increment X by wChar + spacing
    x += wChar + spacing;

    //wAll = wShorter
    wAll = wShorter;

    //Repeat from step 3
    } while (text != "");
}

Code pour test de démonstration/globe oculaire:

element1 = document.getElementById("Test1");
textContext1 = element1.getContext('2d');

textContext1.font = "72px Verdana, sans-serif";
textContext1.textAlign = "left";
textContext1.textBaseline = "top";
textContext1.fillStyle = "#000000";

text = "Welcome to go WAVE";
this.fillTextWithSpacing(textContext1, text, 0, 0, 0);
textContext1.fillText(text, 0, 100);

Idéalement, je lancerais plusieurs chaînes aléatoires et comparerais pixel par pixel. Je ne sais pas non plus à quel point le crénage par défaut de Verdana est bon, même si je comprends que c'est meilleur que Arial - des suggestions sur d'autres polices à essayer, acceptées avec gratitude.

Alors ... jusqu'à présent, ça a l'air bien. En fait, cela semble parfait ... Tout en espérant que quelqu'un signalera les défauts du processus.

En attendant, je mettrai ceci ici pour que les autres voient s'ils cherchent une solution à ce problème.

3

Je ne connais pas d'autres personnes, mais j'ai ajusté l'espacement des lignes en augmentant la valeur y du texte que je suis en train d'écrire. En fait, je coupe une chaîne par des espaces et jette chaque mot sur une ligne dans une boucle. Les chiffres que j'utilise sont basés sur la police par défaut. Si vous utilisez une police différente, il peut être nécessaire d’ajuster ces chiffres.

// object holding x and y coordinates
var vectors = {'x':{1:100, 2:200}, 'y':{1:0, 2:100}
// replace the first letter of a Word
var newtext = YOURSTRING.replace(/^\b[a-z]/g, function(oldtext) {
    // return it uppercase
    return oldtext.toUpperCase(); 
});
// split string by spaces
newtext = newtext.split(/\s+/);

// line height
var spacing = 10 ;
// initial adjustment to position
var spaceToAdd = 5;
// for each Word in the string draw it based on the coordinates + spacing
for (var c = 0; c < newtext.length; c++) {
    ctx.fillText(newtext[c], vectors.x[i], vectors.y[i] - spaceToAdd);
    // increment the spacing 
    spaceToAdd += spacing;
}               
0
modnarrandom

En réalité, le canevas d'espacement des lettres ne prend pas en charge.

J'ai donc utilisé javascript pour faire cela.

var value = $ ('# sourceText1'). val (). split (""). join ("");

OR

var sample_text = "Praveen Chelumalla"; 
 var text = sample_text.split (""). join ("");
0
Praveen Chelumalla

Espacement des lettres dans le canevas IS SUPPORTED, je l’utilise

canvas = document.getElementById('canvas');
canvas.style.letterSpacing = '2px';
0
Artyom Hachikov