2010-07-02 16 views
46

Parte de una aplicación web que estoy desarrollando me obliga a crear gráficos de barras para mostrar diversa información. Pensé que, si el navegador del usuario es capaz, los dibujaría usando el elemento Canvas de HTML5. No tengo problemas dibujando líneas y barras para mis gráficos, pero cuando se trata de etiquetar los ejes, las barras o las líneas, me encontré con un problema. ¿Cómo dibujo texto girado en un elemento canvas para que se alinee con el elemento que está etiquetando? Un par de ejemplos incluyen:Dibujar texto girado en un lienzo HTML5

  • girar el texto 90 grados hacia las agujas del reloj para etiquetar el eje y
  • girar el texto 90 grados hacia las agujas del reloj para etiquetar las barras en un gráfico barra vertical
  • girar el texto arbitrario cantidad a líneas de etiqueta en un gráfico de línea

Cualquier puntero sería apreciado.

+0

¿Ha reflexionado sobre mirar en soluciones gráficas existentes en lugar de tratar de construir su propia ? flot (http://code.google.com/p/flot/) es un ejemplo que usa lienzo. – Bartek

Respuesta

29

Al igual que otros han mencionado, es probable que desee ver la reutilización de una solución de gráficos existente, pero la rotación de texto no es demasiado difícil. El bit algo confuso (para mí) es que se gira todo el contexto y luego dibuja en él:

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

la angle is in radians. El código es taken from this example, que creo que fue hecho para el transformations part of the MDC canvas tutorial.

Consulte the answer below para obtener una solución más completa.

98

Publicando esto en un esfuerzo por ayudar a otros con problemas similares. Resolví este problema con un enfoque de cinco pasos: guardar el contexto, traducir el contexto, rotar el contexto, dibujar el texto y luego restaurar el contexto a su estado guardado.

Creo que las traducciones y las transformaciones al contexto manipulan la cuadrícula de coordenadas superpuesta en el lienzo. Por defecto, el origen (0,0) comienza en la esquina superior izquierda del lienzo. X aumenta de izquierda a derecha, Y aumenta de arriba a abajo. Si haces una "L" con el dedo índice y el pulgar en tu mano izquierda y la sostienes frente a ti con el pulgar hacia abajo, tu pulgar apuntará en la dirección de aumentar Y y tu dedo índice apuntará en la dirección de aumentar X. Sé que es elemental, pero me parece útil cuando pienso en las traducciones y las rotaciones. A continuación se detalla el motivo:

Cuando traduce el contexto, mueve el origen de la cuadrícula de coordenadas a una nueva ubicación en el lienzo. Cuando gire el contexto, piense en girar la "L" que hizo con la mano izquierda en el sentido de las agujas del reloj, la cantidad indicada por el ángulo que especifique en radianes sobre el origen. Cuando aplique strokeText o fillText, especifique sus coordenadas en relación con los ejes recién alineados. Para orientar el texto de modo que sea legible de abajo hacia arriba, se traduciría a una posición debajo de donde desea comenzar sus etiquetas, rotará en -90 grados y rellenará o acariciará el texto, desplazando cada etiqueta a lo largo del eje x girado.Algo como esto debería funcionar:

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

.restore() restablece el contexto de nuevo al estado que tenía cuando llamó .save() - práctico para el retorno de las cosas de nuevo a la "normalidad".


+2

excelente explicación para traducir/rotar. +1 –

+6

si quiere establecer el ángulo a algo específico, puede hacer: 'context.rotate (angle * (Math.PI/180));' – PaulBGD

+0

Más que el código, me encanta su explicación. – Sorter

21

Si bien esto es una especie de seguimiento de la respuesta anterior, agrega un poco (con suerte).

Principalmente lo que quiero aclarar es que generalmente pensamos en dibujar cosas como draw a rectangle at 10, 3.

Así que si pensamos en eso de esta manera: move origin to 10, 3, entonces draw rectangle at 0, 0. Entonces todo lo que tenemos que hacer es agregar un giro en el medio.

Otro punto importante es la alineación del texto. Es más fácil dibujar el texto en 0, 0, por lo que usar la alineación correcta puede permitirnos hacer eso sin medir el ancho del texto.

Todavía deberíamos mover el texto por una cantidad para centrarlo verticalmente, y desafortunadamente el lienzo no tiene gran soporte de altura de línea, así que eso es algo de adivinar y verificar (corríjanme si hay algo mejor).

He creado 3 ejemplos que proporcionan un punto y un texto con 3 alineaciones, para mostrar cuál es el punto real en la pantalla donde irá la fuente.

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 línea this.context.fillText('right', 0, lineHeight/2); es básicamente 0, 0, salvo que nos movemos un poco para el texto que se va centrada cerca del punto