Diversión con lienzo crea un complemento de gráficos de barras, parte 1

En esta serie de dos partes, combinaremos el versátil elemento de lienzo con la robusta biblioteca jQuery para crear un complemento de gráficos de barras. En esta primera parte, vamos a codificar la lógica central del complemento como una versión independiente.

Hoy vamos a crear un plugin de gráficos de barras. No es un plugin ordinario, fíjate. Mostraremos un poco de amor por jQuery al elemento del lienzo para crear un complemento muy robusto..

En este artículo de dos partes, comenzaremos desde el principio implementando la lógica del complemento como una secuencia de comandos independiente, refactorizándolo en un complemento y, finalmente, agregando todos los complementos adicionales al código del complemento. En esta primera parte, vamos a tratar únicamente con la implementación de la lógica central.

¿Necesitas un ejemplo antes de empezar? Aqui tienes!


Diferentes gráficos creados con diferentes configuraciones para nuestro complemento.

¿Satisfecho? ¿Interesado ya? Empecemos.


Funcionalidad

Nuestro complemento necesita lograr algunas cosas básicas mientras que no hace otras cosas. Déjame aclarar:

  • Como de costumbre, vamos a utilizar solo el elemento canvas y JavaScript. No hay imágenes de ningún tipo, no hay técnicas CSS dañadas, no hay representaciones previas. Un elemento de lienzo antiguo (¿o es nuevo?) Junto con algunos jQuery para aligerar nuestra carga de trabajo.
  • Con respecto a la fuente de datos, vamos a extraer todos los datos directamente de una tabla estándar. No hay matrices para pasar el complemento en el inicio. De esta manera, el usuario puede poner todos los datos en una tabla y luego invocar nuestro complemento. Además, es mucho más accesible..
  • No hay marcas especiales para la tabla que actúa como fuente de datos y definitivamente no hay nombres de clases especiales para las celdas de datos. Vamos a utilizar solo el ID de la tabla y extraeremos todos nuestros datos desde allí.
  • No hay superposición de texto endeble para representar las etiquetas y demás en el gráfico. No solo es altamente tedioso, sino que el texto representado no forma parte del gráfico cuando se guarda. Vamos a utilizar el fillText y strokeText según lo definido por las especificaciones WHATWG.

Dependencias

A medida que nos adentramos en el mundo de la tecnología de vanguardia, aún no totalmente especificada, tenemos algunas dependencias. Para que el elemento lienzo funcione, la mayoría de los navegadores modernos son suficientes. Pero como hacemos uso de la nueva API de representación de texto, necesitamos compilaciones más nuevas. Los navegadores que utilizan el motor Webkit r433xx y superior o el motor Gecko 1.9.1 y superior deben ser plataformas excelentes para el complemento. Recomiendo agarrar una compilación nocturna de Chromium o Firefox.


Antes que empecemos

Me gustaría mencionar que nuestro plugin es solo para propósitos de aprendizaje. Este complemento no pretende de ninguna manera reemplazar otros complementos gráficos completos como Flot, Plotr y similares. Además, el código será lo más detallado posible. Usted podría escribir lejos, lejos Código más eficiente, pero por el bien del aprendizaje, todo va a ser lo más sencillo posible. Siéntase libre de refactorizarlo a favor de la eficiencia en su código de producción.


El marcado HTML

   OMG WTF HAX   
Año Ventas
2009 130
2008 200
2007 145
2006 140
2005 210
2004 250
2003 170
2002 215
2001 115
2000 135
1999 110
1998 180
1997 105

Nada especial en el marcado. Voy a hacer una visión general rápida de todos modos.

  • Comenzamos por incluir el doctype requerido. Ya que estamos usando el elemento canvas, usamos el apropiado para HTML 5.
  • A continuación, se define la tabla de origen de datos. Observe que no se está describiendo un marcado especial o que se están definiendo y asignando nuevas clases dentro de sus miembros.
  • Se define un elemento de lienzo y luego se le asigna una ID para luego hacer referencia a él. Este elemento de lienzo específico solo estará aquí para la versión independiente. En la versión del complemento, el elemento de lienzo y sus atributos se inyectarán dinámicamente en el DOM y luego se manipularán según sea necesario. Para la mejora progresiva de esta manera funciona mucho mejor.
  • Finalmente, incluimos la biblioteca jQuery y nuestro script personalizado. Como Jeffrey ha mencionado una y otra vez, incluir guiones al final del documento siempre es una buena idea.

La rejilla de lona

Antes de iniciar el Javascript, permítanme explicar el sistema de coordenadas del lienzo. La esquina superior izquierda actúa como el origen, es decir (0, 0). Los puntos se miden con respecto al origen, con x aumentando a lo largo de la derecha e y aumentando a lo largo de la izquierda. Para los inclinados matemáticamente, estamos trabajando efectivamente en el cuarto cuadrante, excepto que tomamos el valor absoluto de y en lugar de su valor negativo. Si ha trabajado con gráficos en otros idiomas, debería estar en casa aquí..


El sistema de coordenadas lienzo.

La rutina de renderizado de rectángulo

La rutina de renderización del rectángulo de Canvas se utilizará ampliamente en todo el artículo para representar las barras, la cuadrícula y algunos otros elementos. Con eso en mente, echemos un vistazo a esas rutinas.

De las tres rutinas disponibles, usaremos el fillRect y strokeRect metodos los fillRect El método en realidad llena el rectángulo renderizado mientras que el strokeRect El método solo traza los rectángulos. Aparte de eso, ambos métodos toman los mismos parámetros.

  • X - La coordenada x del punto desde donde empezar a dibujar..
  • y - La coordenada y con respecto al origen..
  • anchura - Define el ancho del rectángulo a dibujar..
  • altura - Define la altura del rectángulo..

La magia de Javascript

Como siempre, te recomiendo que descargues el código fuente y lo tengas en el lado como referencia. Es más fácil mirar la imagen general y analizar cada función una por una que mirar cada función individualmente y luego crear la imagen grande en su mente.


Declaración Variable

 var barSpacing = 20, barWidth = 20, cvHeight = 220, numYlabels = 8, xOffset = 20, gWidth = 550, gHeight = 200; var maxVal, gValues ​​= [], xLabels = [], yLabels = []; var cv, ctx;

Variables graficas

  • xLabels - Una matriz que contiene el valor de las etiquetas del eje X.
  • yLabels - Igual que el anterior, excepto que contiene los valores de las etiquetas del eje Y.
  • gValores - Array que contiene todos los datos del gráfico que extraemos de la fuente de datos.
  • CV - Variable para apuntar hacia el elemento canvas..
  • ctx - Variable para referirse al contexto del elemento canvas..

Variables de opción de gráfico

Estas variables contienen valores codificados para ayudarnos a posicionar y diseñar el gráfico y las barras individuales..

  • barra espaciadora - Define el espaciado entre barras individuales..
  • barwidth - Define el ancho de cada barra individual..
  • cvHeight - Define la altura del elemento canvas. Codificado duro ya que creamos el elemento canvas de antemano. La versión del complemento varía en esta funcionalidad..
  • NumYlabels - Define el número de etiquetas a dibujar en el eje Y.
  • xOffset - Define el espacio entre el comienzo del elemento del lienzo y el gráfico real. Este espacio se utiliza para dibujar las etiquetas del eje Y.
  • gWidth, gHeight - Valores codificados de forma rígida que contienen la dimensión del espacio de representación real del gráfico mismo.

Cómo cada variable controla la apariencia de la gráfica.

Agarrando los valores

Con el potente motor de selección de jQuery, nos resulta muy fácil obtener los datos que necesitamos. Aquí tenemos varias formas de acceder a los elementos necesarios. Permítanme explicarles a continuación:

 $ ("tr"). children ("td: odd"). each (function () // code here);

La forma más sencilla de acceder a las filas necesarias. Busca un tr elemento y luego accede a todos los demás td elemento. Falla miserablemente cuando tienes más de una tabla en tu página.

 $ ("# data"). find ("td: odd"). each (function () // code here);

Un camino mucho más directo. Pasamos el ID de la tabla y luego accedemos a cada otra fila.

 $ ("# data tr td: odd"). each (function () // code here);

Igual que el anterior, excepto que solo usamos la sintaxis del selector de estilo CSS.

 $ ("# data tr td: nth-child (2)"). each (function () // code here);

La versión que vamos a utilizar hoy. De esta manera es mucho mejor si necesitamos capturar datos de una fila diferente o, si es necesario, varias filas.

La versión final se ve así:

 function grabValues ​​() // Acceda a la celda de la tabla requerida, extraiga y agregue su valor a la matriz de valores. $ ("# data tr td: nth-child (2)"). each (function () gValues.push ($ (this) .text ());); // Acceda a la celda de la tabla requerida, extraiga y agregue su valor a la matriz xLabels. $ ("# data tr td: nth-child (1)"). each (function () xLabels.push ($ (this) .text ());); 

Nada complicado aquí. Usamos el fragmento de código mencionado anteriormente para agregar el valor de la celda de la tabla a la gValores formación. A continuación, hacemos lo mismo, excepto que accedemos a la primera celda de la tabla para extraer la etiqueta necesaria para el eje x. Hemos encapsulado la lógica de extracción de datos en su propia función para la reutilización y legibilidad del código..


Inicialización de Canvas

 function initCanvas () // Intente acceder al elemento del lienzo y lanzar un error si no está disponible cv = $ ("# graph"). get (0); if (! cv) return;  // Trate de obtener un contexto 2D para el lienzo y arroje un error si no puede ctx = cv.getContext ('2d'); if (! ctx) return; 

Inicialización de lienzo de rutina. Primero intentamos acceder al propio elemento del lienzo. Lanzamos un error si no podemos. A continuación, tratamos de obtener una referencia al contexto de representación 2d a través de getContext Método y lanzar un error si no podemos hacerlo.


Funciones de utilidad

Antes de pasar a la representación real del gráfico en sí, necesitamos ver una serie de funciones de utilidad que nos ayudan mucho en el proceso. Cada uno de ellos es pequeño por sí mismo, pero se utilizará ampliamente en todo nuestro código..

Determinando el valor máximo

 función maxValues ​​(arr) maxVal = 0; para (i = 0; i 

Una pequeña función que recorre la matriz pasada y actualiza la maxVal variable. Tenga en cuenta que inflamos el valor máximo en un 10% para fines especiales. Si el valor máximo se deja como está, entonces la barra que representa el valor más alto tocará el borde del elemento de lienzo que no deseamos. Con eso en mente, se emite un incremento del 10%..

Normalizando el valor

 escala de funciones (param) return Math.round ((param / maxVal) * gHeight); 

Una pequeña función para normalizar el valor extraído con respecto a la altura del elemento del lienzo. Esta función se usa ampliamente en otras funciones y directamente en nuestro código para expresar el valor como una función de la altura del lienzo. Toma un solo parámetro.

Devolviendo la coordenada X

 función x (param) return (param * barWidth) + ((param + 1) * barSpacing) + xOffset; 

Devuelve la ordenada x al fillRect Para ayudarnos en el posicionamiento de cada barra individual. Explicaré esto un poco más en detalle cuando se use..

Devolviendo la coordenada Y

 función y (param) return gHeight - scale (param); 

Devuelve la ordenada y al fillRect Método para ayudarnos en el posicionamiento de cada barra individual. Más explicaciones un poco más tarde..

Devolviendo el Ancho

 ancho de la función () return barWidth; 

Devuelve el ancho de cada barra individual..

Volviendo la altura

 función altura (param) retorno escala (param); 

Devuelve la altura de la barra a dibujar. Utiliza el escala función para normalizar el valor y luego lo devuelve a la persona que llama.


Dibujando las etiquetas del eje X

 función drawXlabels () ctx.save (); ctx.font = "10px 'arial'"; ctx.fillStyle = "# 000"; para (índice = 0; índice 

Una función simple para renderizar las etiquetas del eje x. Primero guardamos el estado actual del lienzo, incluyendo todas las configuraciones de renderización, de modo que cualquier cosa que hagamos dentro de las funciones nunca se filtre. Luego establecemos el tamaño y la fuente de las etiquetas. A continuación, iteramos a través de la xLabels array y llama al fillText Método cada vez para hacer la etiqueta. Usamos el X Función que nos ayuda en el posicionamiento de las etiquetas..


Dibujando las etiquetas del eje Y

 función drawYlabels () ctx.save (); para (índice = 0; índice 

Una función algo más verbosa. Primero guardamos el estado actual del lienzo y luego procedemos. A continuación dividimos maxVal's Valor en n elementos donde la variable NumYlabels dicta n. Estos valores se agregan a la yLabels formación. Ahora, como se muestra arriba, la fillText Se llama método para dibujar las etiquetas individuales con el y Función que nos ayuda en el posicionamiento de cada etiqueta individual..

Hacemos un cero en la parte inferior del lienzo para terminar de dibujar las etiquetas Y.


Dibujando la gráfica

 función drawGraph () for (index = 0; index  

La función que dibuja las barras reales en el gráfico de barras. Iteriza a través de la gValores Arreglo y renderiza cada barra individual. Usamos el fillRect Método para dibujar las barras. Como se explicó anteriormente, el método toma cuatro parámetros, cada uno de los cuales está a cargo de nuestras funciones de utilidad. El índice actual del bucle se pasa a nuestras funciones como parámetros junto con el valor real mantenido en la matriz, según sea necesario.

los X La función devuelve la coordenada x de la barra. Cada vez, se incrementa por el valor de la suma de barwidth y barra espaciadora variables.

los y La función calcula la diferencia entre la altura del elemento del lienzo y los datos normalizados y lo devuelve. Sé que esto suena un poco al revés, pero esto se debe al hecho de que los valores de y en la cuadrícula del lienzo aumentan al moverse hacia abajo, mientras que en nuestro gráfico los valores de y aumentan al ascender. Por lo tanto, tenemos que hacer un poco de trabajo para que funcione como deseamos..

los anchura La función devuelve el ancho de las barras individuales..

los altura La función simplemente devuelve el valor normalizado que se usará como la altura de la barra que se dibujará..


Resumen

En esta primera parte, hemos implementado la lógica base de nuestro complemento como una versión independiente con aspectos y características simples. Revisamos el sistema de coordenadas del lienzo, los métodos de renderización de rectángulos, algunos extraños datos extraídos utilizando el asombro innato de jQuery [¿He mencionado cuánto me gusta jQuery?], Observamos cómo se dibujan las etiquetas y finalmente observamos la lógica detrás de la renderización de la gráfica misma.

Al final de este artículo, la salida debería verse así.


Siguiente!

Nuestra implementación actual es bastante deficiente. Parece insípido, no puede crear múltiples gráficos en la misma página, y admitámoslo, es bastante espartano en el frente de características. Vamos a abordar todo eso la próxima semana. En el siguiente artículo vamos a:

  • Refactoriza nuestro código para convertirlo en un complemento de jQuery completo.
  • Añadir un poco de dulce de ojos.
  • Incluye algunas pequeñas características ingeniosas.

Preguntas? Criticas? Alabanzas Siéntase libre de golpear los comentarios. Gracias por leer y, cuando esté listo, pase a la segunda parte!

  • Síganos en Twitter o suscríbase a la Fuente RSS de NETTUTS para obtener más artículos y artículos de desarrollo web diarios.