web-dev-qa-db-fra.com

Dessin de texte pivoté sur un canevas HTML5

Une partie d'une application Web que je développe nécessite de créer des graphiques à barres pour afficher diverses informations. Je pensais que si le navigateur de l'utilisateur en était capable, je le dessinais à l'aide de l'élément de canevas HTML5. Je n'ai aucun problème à dessiner des lignes et des barres pour mes graphiques, mais en ce qui concerne l'étiquetage des axes, des barres ou des lignes, j'ai rencontré un problème. Comment dessiner un texte pivoté sur un élément de la toile de manière à l'aligner sur l'élément en cours d'étiquetage? Quelques exemples incluent:

  • Faites pivoter le texte du compteur de 90 degrés Dans le sens des aiguilles d'une montre pour identifier l'axe des y
  • Faites pivoter le texte du compteur de 90 degrés Dans le sens des aiguilles d'une montre pour étiqueter des barres sur un graphique vertical.
  • Faire pivoter le texte d'une valeur arbitraire pour créer des lignes d'étiquette .__ sur un graphique linéaire

Tous les pointeurs seraient appréciés.

55
Sparafusile

Comme d'autres l'ont mentionné, vous voudrez probablement réutiliser une solution graphique existante, mais la rotation du texte n'est pas trop difficile. Ce qui est un peu déroutant (pour moi), c’est que vous faites pivoter tout le contexte et que vous vous en inspirez:

ctx.rotate(Math.PI*2/(i*6));

L'angle est en radians . Le code est tiré de cet exemple , qui, je crois, a été créé pour la partie transformations du didacticiel de canevas MDC .

Veuillez consulter la réponse ci-dessous pour une solution plus complète.

33
robertc

Afficher ceci dans un effort pour aider les autres avec des problèmes similaires. J'ai résolu ce problème avec une approche en cinq étapes: enregistrer le contexte, traduire le contexte, faire pivoter le contexte, dessiner le texte, puis restaurer le contexte à son état enregistré. 

Je pense que les traductions et les transformations dans le contexte consistent à manipuler la grille de coordonnées superposée sur la toile. Par défaut, l'origine (0,0) commence dans le coin supérieur gauche du canevas. X augmente de gauche à droite, Y augmente de haut en bas. Si vous faites un "L" avec votre index et votre pouce sur votre main gauche et que vous le maintenez devant vous avec votre pouce vers le bas, votre pouce se dirigera dans la direction correspondant à l'augmentation de Y et votre index, dans la direction Je sais que c’est élémentaire, mais j’estime utile de penser aux traductions et aux rotations. Voici pourquoi:

Lorsque vous traduisez le contexte, vous déplacez l'origine de la grille de coordonnées vers un nouvel emplacement du canevas. Lorsque vous faites pivoter le contexte, pensez à faire pivoter le "L" que vous avez fait avec votre main gauche dans le sens des aiguilles d'une montre, le montant indiqué par l'angle que vous spécifiez en radians autour de l'origine. Lorsque vous strokeText ou fillText, spécifiez vos coordonnées par rapport aux axes nouvellement alignés. Pour orienter votre texte de sorte qu'il soit lisible de bas en haut, vous traduisez une position située en-dessous de l'endroit où vous souhaitez commencer vos étiquettes, faire une rotation de -90 degrés et un remplissage ou un contour, en décalant chaque étiquette le long de l'axe x pivoté. Quelque chose comme ça devrait marcher:

 context.save();
 context.translate(newx, newy);
 context.rotate(-Math.PI/2);
 context.textAlign = "center";
 context.fillText("Your Label Here", labelXposition, 0);
 context.restore();

.restore () réinitialise le contexte à l'état qu'il avait lorsque vous avez appelé .save () - pratique pour ramener les choses à "normal".


110
user631644

Même s’il s’agit d’une sorte de suivi de la réponse précédente, cela ajoute un peu (espérons-le).

Ce que je veux surtout préciser, c’est que nous pensons généralement à dessiner des choses comme draw a rectangle at 10, 3.

Donc, si nous pensons à cela comme ceci: move Origin to 10, 3, alors draw rectangle at 0, 0. Ensuite, tout ce que nous avons à faire est d’ajouter une rotation entre les deux.

Un autre point important est l'alignement du texte. Comme il est plus facile de dessiner le texte à 0, 0, utiliser le bon alignement peut permettre de le faire sans mesurer la largeur du texte.

Nous devrions tout de même déplacer le texte d’un certain montant pour le centrer verticalement, et malheureusement, canevas n’a pas une excellente prise en charge de la hauteur des lignes.

J'ai créé 3 exemples qui fournissent un point et un texte avec 3 alignements afin d'indiquer le point réel à l'écran à l'endroit où la police sera placée.

enter image description here

var font, lineHeight, x, y;

x = 100;
y = 100;
font = 20;
lineHeight = 15; // this is guess and check as far as I know
this.context.font = font + 'px Arial';


// Right Aligned
this.context.save();
this.context.translate(x, y);
this.context.rotate(-Math.PI / 4);

this.context.textAlign = 'right';
this.context.fillText('right', 0, lineHeight / 2);

this.context.restore();

this.context.fillStyle = 'red';
this.context.fillRect(x, y, 2, 2);


// Center
this.context.fillStyle = 'black';
x = 150;
y = 100;

this.context.save();
this.context.translate(x, y);
this.context.rotate(-Math.PI / 4);

this.context.textAlign = 'center';
this.context.fillText('center', 0, lineHeight / 2);

this.context.restore();

this.context.fillStyle = 'red';
this.context.fillRect(x, y, 2, 2);


// Left
this.context.fillStyle = 'black';
x = 200;
y = 100;

this.context.save();
this.context.translate(x, y);
this.context.rotate(-Math.PI / 4);

this.context.textAlign = 'left';
this.context.fillText('left', 0, lineHeight / 2);

this.context.restore();

this.context.fillStyle = 'red';
this.context.fillRect(x, y, 2, 2);

La ligne this.context.fillText('right', 0, lineHeight / 2); est fondamentalement 0, 0, sauf que nous déplaçons légèrement pour que le texte soit centré près du point

22
Funkodebat

Voici une alternative HTML5 à homebrew: http://www.rgraph.net/ Vous pourrez peut-être faire de l'ingénierie inverse de leurs méthodes ....

Vous pouvez également envisager quelque chose comme Flot ( http://code.google.com/p/flot/ ) ou GCharts: ( http://www.maxb.net/scripts/jgcharts/include/demo/# 1 ) Ce n'est pas aussi cool, mais totalement compatible avec les versions antérieures et facile à mettre en œuvre.

0
bpeterson76

Funkodebat a publié une excellente solution à laquelle j'ai déjà fait référence à maintes reprises ... Encore une fois, je me retrouve à écrire mon propre modèle de travail chaque fois que j'en ai besoin ... Voici donc mon modèle de travail ... avec plus de clarté.

Tout d’abord, la hauteur du texte est égale à la taille de la police de pixels . C’est quelque chose que j’avais lu il ya quelque temps et qui a fonctionné dans mes calculs. Je ne sais pas si cela fonctionne avec toutes les polices, mais cela semble fonctionner avec Arial, sans-serif.

De plus, pour vous assurer que tout le texte de votre canevas est bien ajusté (et ne coupez pas les queues de vos "p"), vous devez définir context.textBaseline * .

Vous verrez dans le code que nous faisons pivoter le texte autour de son centre . Pour ce faire, nous devons définir context.textAlign = "center" et le context.textBaseline en bas, sinon, nous coupons certaines parties de notre texte.

Pourquoi redimensionner la toile? J'ai généralement une toile qui n'est pas ajoutée à la page. Je l’utilise pour dessiner l’ensemble de mon texte pivoté, puis je le dessine sur un autre canevas que j’affiche .Par exemple, vous pouvez utiliser ce canevas pour dessiner toutes les étiquettes d’un graphique (une par une) et les toile masquée sur la toile du graphique où vous avez besoin de l’étiquette (context.drawImage(hiddenCanvas, 0, 0);).

REMARQUE IMPORTANTE: Définissez votre police avant de mesurer votre texte et réappliquez tous vos styles au contexte après le redimensionnement de votre toile. Le contexte d'une toile est complètement réinitialisé lorsque la toile est redimensionnée.

J'espère que cela t'aides!

var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var font, text, x, y;

text = "Mississippi";

//Set font size before measuring
font = 20;
ctx.font = font + 'px Arial, sans-serif';
//Get width of text
var metrics = ctx.measureText(text);
//Set canvas dimensions
c.width = font;//The height of the text. The text will be sideways.
c.height = metrics.width;//The measured width of the text
//After a canvas resize, the context is reset. Set the font size again
ctx.font = font + 'px Arial';
//Set the drawing coordinates
x = font/2;
y = metrics.width/2;
//Style
ctx.fillStyle = 'black';
ctx.textAlign = 'center';
ctx.textBaseline = "bottom";
//Rotate the canvas and draw the text
ctx.save();
ctx.translate(x, y);
ctx.rotate(-Math.PI / 2);
ctx.fillText(text, 0, font / 2);
ctx.restore();
<canvas id="myCanvas" width="300" height="150" style="border:1px solid #d3d3d3;">

0
WebWanderer