1. Code
  2. JavaScript

Una introducción a la biblioteca Raphael JS

Raphael JS es un framework de JavaScript ligero y súper atractivo que te permite dibujar gráficos vectoriales en tu navegador. En este tutorial, te presentaré algunas funciones básicas de dibujo, le echaré un vistazo a la animación, proporcionaré acceso DOM y finalmente terminaré creando un widget genial para tu sitio...
Scroll to top

Spanish (Español) translation by Andrea Jiménez (you can also view the original English article)

Raphael JS es un framework de JavaScript ligero y súper atractivo que te permite dibujar gráficos vectoriales en tu navegador. En este tutorial, te presentaré algunas funciones básicas de dibujo, le echaré un vistazo a la animación, proporcionaré acceso DOM y finalmente terminaré creando un widget genial para tu sitio...

Detalles del tutorial

  • Framework: Raphael JS
  • Versión: 1.0
  • Dificultad: Principiante a intermedio
  • Tiempo estimado de finalización: 30 minutos

1. Configuración

Comencemos descargando el framework Raphael JS desde aquí. En la parte superior derecha de la página, verás copias comprimidas y sin comprimir de Raphael versión 1.0. Te recomiendo que obtengas una copia de la fuente sin comprimir por el momento: de esta manera, puedes revisar la fuente y ver qué ventaja adicional puedes obtener en la documentación.

Con eso descargado, configuremos un documento HTML simple llamado index.htm e incluyamos a Raphael en él. También incluimos our_script.js, que es donde escribiremos nuestro propio JavaScript, y en el cuerpo del documento creamos un div de estilo mínimo con ID canvas_container, que actuará como un contenedor para nuestros dibujos.

1
<html>
2
    <head>
3
        <title>Raphael Play</title>
4
        <script type="text/javascript" src="path/to/raphael.js"></script>
5
        <script type="text/javascript" src="path/to/our_script.js"></script>
6
        <style type="text/css">
7
            #canvas_container {
8
                width: 500px;
9
                border: 1px solid #aaa;
10
            }
11
        </style>
12
    </head>
13
    <body>
14
        <div id="canvas_container"></div>
15
    </body>
16
</html>

NOTA. La primera versión estable de la versión 1.0 solo estuvo disponible el 7 de octubre de 2009, por lo que es bastante nueva. Realiza un cambio muy importante en la forma en que dibujas las rutas, por lo que si estás utilizando una versión anterior de Raphael, asegúrate de actualizar y consultar la documentación sobre cómo efectuar la compatibilidad con versiones anteriores.

2. Creación de nuestro lienzo de dibujo

Cuando dibujamos con Raphael, lo hacemos sobre un lienzo. Este lienzo, al que haremos referencia en una variable llamada 'papel', se crea utilizando el objeto Raphael(). Siempre especificamos el ancho y el alto del lienzo, pero tenemos la opción de especificar también a) la posición absoluta del lienzo en relación con la ventana gráfica, o b) un elemento 'contenedor' en el que se dibuja el lienzo.

1
var paper = new Raphael(x, y, width, height); //option (a)
2
var paper = new Raphael(element, width, height); //option (b)

Generalmente prefiero el último método (b), ya que normalmente sabemos dónde están nuestros divs. En our_script.js, esperamos a que el DOM se cargue y luego creamos un lienzo de 500px por 500px dentro de nuestro div canvas_container:

1
window.onload = function() {
2
    var paper = new Raphael(document.getElementById('canvas_container'), 500, 500);
3
}

Todos nuestros métodos de dibujo ahora estarán vinculados a la variable papel.

3. Formas integradas

Ya que tenemos nuestro lienzo, dibujemos algunas formas en él. El origen, es decir, el punto x = 0, y = 0, está en la esquina superior izquierda de  nuestro lienzo. Esto significa que las coordenadas x, y que especifiquemos en nuestros métodos son relativas a este punto.

Primero, un círculo. Modifica our_script.js para que se vea así:

1
window.onload = function() {
2
    var paper = new Raphael(document.getElementById('canvas_container'), 500, 500);
3
    var circle = paper.circle(100, 100, 80);
4
}

Esto dibujará un círculo con un radio de 80px con su centro colocado en x = 100, y = 100. Podemos dibujar tantos círculos como queramos y no tenemos que hacer referencia a ellos en una variable:

1
for(var i = 0; i < 5; i+=1) {
2
    var multiplier = i*5;
3
    paper.circle(250 + (2*multiplier), 100 + multiplier, 50 - multiplier);
4
}

Después, dibujemos un rectángulo. Hacemos esto usando el método rect(), que toma como parámetros: las coordenadas x e y de la esquina superior izquierda del rectángulo y el ancho y alto deseados del rectángulo.

1
var rectangle = paper.rect(200, 200, 250, 100);

Finalmente, dibujaremos una elipse. Sus parámetros son los mismos que los del círculo, es decir, x, y, radio, excepto que podemos especificar los radios x e y específicamente.

1
var ellipse = paper.ellipse(200, 400, 100, 50);

Esto dibujará una elipse con radio x = 100, radio y = 50 en x = 200, y = 400. Nuestro archivo our_script.js ahora debería verse así:

1
window.onload = function() {
2
    var paper = new Raphael(document.getElementById('canvas_container'), 500, 500);
3
    var circle = paper.circle(100, 100, 80);
4
    for(var i = 0; i < 5; i+=1) {
5
        var multiplier = i*5;
6
        paper.circle(250 + (2*multiplier), 100 + multiplier, 50 - multiplier)
7
    }
8
    var rectangle = paper.rect(200, 200, 250, 100);
9
    var ellipse = paper.ellipse(200, 400, 100, 50);
10
11
}

Ahora, si abrimos index.htm en nuestro navegador, deberíamos obtener un montón de dibujos de formas:

Ejemplo aquí

4. Trazados de dibujo

Si bien las formas integradas son prácticas, son los trazados los que nos ofrecen una verdadera flexibilidad en el dibujo. Al dibujar trazados, es útil pensar en un cursor imaginario o en un bolígrafo presionado contra la pantalla. Cuando creamos nuestro lienzo, el cursor está enraizado en la esquina superior izquierda. Entonces, lo primero que debemos hacer es levantar el cursor o la punta del lápiz y moverlo a una región espaciosa en la que podamos dibujar.

Como ejemplo, movamos nuestro cursor al centro de nuestro lienzo. Es decir, movámoslo 250px en la dirección x y lo movemos 250px en la dirección y.

Hacemos esto usando una llamada cadena de ruta.

Una cadena de ruta es una cadena compuesta por comandos de "acción" y valores numéricos correspondientes al comando. Movemos nuestro cursor a x = 250, y = 250 usando la siguiente cadena:

1
"M 250 250"

'M' significa que queremos movernos sin dibujar y va seguido de las coordenadas del lienzo x e y.

Ya que nuestro cursor está donde lo queremos, dibujemos una línea relativa a este punto usando el comando 'L' en minúscula, 'l'.

1
"M 250 250 l 0 -50"

Esto dibujará una línea ascendente 50px en la dirección y. Escribamos una cadena de ruta que dibujará un tetris tetronimo:

1
"M 250 250 l 0 -50 l -50 0 l 0 -50 l -50 0 l 0 50 l -50 0 l 0 50 z"

El comando 'z' significa el cierre de la ruta: se unirá a una línea desde donde estemos hasta el punto especificado por nuestro comando inicial 'M'.

Le decimos a Raphael que realmente dibuje esta ruta usando el método path(). Modifica our_script.js para que se vea así:

1
window.onload = function() {
2
    var paper = new Raphael(document.getElementById('canvas_container'), 500, 500);
3
    var tetronimo = paper.path("M 250 250 l 0 -50 l -50 0 l 0 -50 l -50 0 l 0 50 l -50 0 l 0 50 z");
4
}

Si cargas index.htm, ahora deberías ver un tetronimo como este:

Las cadenas de ruta pueden volverse increíblemente (brillantemente) complejas usando comandos de curva y arco. La cobertura completa de las rutas se puede encontrar en lapágina de especificaciones de la ruta SVG.

5. Estilo de atributos

Nuestro tetris tetromino, aunque maravilloso, no es muy agradable estéticamente. Lo arreglaremos usando el método attr().

El método attr() toma un objeto que consta de varios pares propiedad-valor como parámetro. Como almacenamos una referencia a nuestro tetromino en la variable tetromino, podemos tomar esta variable y agregarle el método attr(). Podríamos igualmente encadenar el método attr() al método path(), pero dejemos las cosas coherentes por el momento. Demostraré el uso de attr() con el ejemplo:

1
window.onload = function() {
2
    var paper = new Raphael(document.getElementById('canvas_container'), 500, 500);
3
    var tetronimo = paper.path("M 250 250 l 0 -50 l -50 0 l 0 -50 l -50 0 l 0 50 l -50 0 l 0 50 z");
4
5
    tetronimo.attr({fill: '#9cf', stroke: '#ddd', 'stroke-width': 5});
6
}

produce esto:

1
window.onload = function() {
2
    var paper = new Raphael(document.getElementById('canvas_container'), 500, 500);
3
    var tetronimo = paper.path("M 250 250 l 0 -50 l -50 0 l 0 -50 l -50 0 l 0 50 l -50 0 l 0 50 z");
4
5
    tetronimo.attr(
6
        {
7
            gradient: '90-#526c7a-#64a0c1',
8
            stroke: '#3b4449',
9
            'stroke-width': 10,
10
            'stroke-linejoin': 'round',
11
            rotation: -90
12
        }
13
    );
14
}

produce esto:

La documentación de Raphael es bastante extensa cuando se trata del método attr(). Juega con las diversas combinaciones del objeto propiedad-valor.

6. Animación

El método animate() en Raphael es muy, muy bueno. Nos permite animar nuestros dibujos de una manera jQuery-esque, animando los atributos que le proporcionamos durante un período de tiempo con un suavizado opcional.

Giremos nuestro tetronimo más reciente 360 ​​grados. El atributo de rotación es absoluto, por lo que debería llevarlo a una rotación completa y devolverlo a su estado no girado.

1
window.onload = function() {
2
    var paper = new Raphael(document.getElementById('canvas_container'), 500, 500);
3
    var tetronimo = paper.path("M 250 250 l 0 -50 l -50 0 l 0 -50 l -50 0 l 0 50 l -50 0 l 0 50 z");
4
    tetronimo.attr(
5
        {
6
            gradient: '90-#526c7a-#64a0c1',
7
            stroke: '#3b4449',
8
            'stroke-width': 10,
9
            'stroke-linejoin': 'round',
10
            rotation: -90
11
        }
12
    );
13
14
    tetronimo.animate({rotation: 360}, 2000, 'bounce');
15
}

La animación se lleva a cabo durante 2 segundos (2000 milisegundos) y se le indica que llegue a su estado final con un "rebote".

Ejemplo aquí.

También podemos proporcionar una función de devolución de llamada como argumento. Esta función de devolución de llamada se invoca después de que finaliza la animación. El siguiente ejemplo animará la rotación y el ancho del trazo del tetronimo y luego se reiniciará con otra animación en la función de devolución de llamada.

1
tetronimo.animate({rotation: 360, 'stroke-width': 1}, 2000, 'bounce', function() {
2
    /* callback after original animation finishes */
3
    this.animate({
4
        rotation: -90,
5
        stroke: '#3b4449',
6
        'stroke-width': 10
7
    }, 1000);
8
});

La palabra clave this hace referencia al tetronimo original desde dentro de la función de devolución de llamada.

Ejemplo aquí.

Animación de rutas

Como soy un fanático del código, rara vez dejé de dibujar formas simples en Flash. Pero una cosa con la que me gustó jugar fue la interpolación de formas. Bien, Raphael logra emular la interpolación de formas especificando una cadena de ruta en el método animate().

Otro tetronimo, el tetronimo Z en Tetris, tiene la siguiente cadena de ruta,

1
"M 250 250 l 0 -50 l -50 0 l 0 -50 l -100 0 l 0 50 l 50 0 l 0 50 z"

y se ve así:

Ahora, usando nuestro tetronimo original con un estilo de atributo mínimo, voy a especificar la nueva cadena de ruta en nuestro método animate().

1
tetronimo.attr(
2
    {
3
        stroke: 'none',
4
        fill: 'blue'
5
    }
6
);
7
8
tetronimo.animate({
9
    path: "M 250 250 l 0 -50 l -50 0 l 0 -50 l -100 0 l 0 50 l 50 0 l 0 50 z"
10
}, 5000, 'elastic');

Deberías ver nuestro tetronimo original transformarse en el nuevo. El efecto se hace aún más pronunciado especificando 'elástico' como el tipo de suavizado.

Ejemplo aquí.

7. Accesibilidad Dom

Si queremos tener acceso a nuestros elementos como elementos DOM, podemos hacerlo con cierta facilidad. Esto es gracias a la propiedad node. Con esto, podemos agregar controladores de eventos a nuestros dibujos, que procederé a mostrarles.

Comencemos dibujando un círculo en nuestro archivo our_script.js.

1
window.onload = function() {
2
        var paper = new Raphael(document.getElementById('canvas_container'), 500, 500);
3
4
        var circ = paper.circle(250, 250, 40);
5
        circ.attr({fill: '#000', stroke: 'none'});
6
}

Ahora, agreguemos el texto, '¡Adiós Círculo!' de modo que su punto central esté en el mismo punto que el centro de nuestro círculo.

1
var text = paper.text(250, 250, 'Bye Bye Circle!')
2
text.attr({opacity: 0, 'font-size': 12}).toBack();

Establecí la opacidad en 0 para que inicialmente esté oculta. Observa el encadenamiento del método toBack(). Esto coloca el texto detrás de todos los demás elementos de dibujo del lienzo (similar a Front () trae elementos al frente de nuestro lienzo).

Ahora, agregaremos un controlador de eventos mouseover a nuestro círculo mediante la propiedad node. Configuraremos el estilo del cursor en 'puntero'.

1
circ.node.onmouseover = function() {
2
    this.style.cursor = 'pointer';
3
}

Lo que esto realmente hace es configurar la propiedad de estilo del objeto <circle> en nuestro documento. Nuestro documento tiene este aspecto:

1
<circle cx="250.5" cy="250.5" r="40" fill="#000000" stroke="none" style="fill: #000000; stroke: none; cursor: pointer">
2
</circle>

Ahora, finalmente agregamos un controlador de eventos onclick a nuestro círculo:

1
circ.node.onclick = function() {
2
    text.animate({opacity: 1}, 2000);
3
    circ.animate({opacity: 0}, 2000, function() {
4
        this.remove();
5
    });
6
}

Cuando se hace clic en el círculo, el texto al que hacemos referencia en el texto variable se anima a una opacidad total durante 2 segundos. El círculo en sí está animado a 0 opacidad durante el mismo periodo de tiempo. También incluimos una función de devolución de llamada en el método animado del círculo. Esto elimina el elemento del círculo de nuestro documento una vez que la animación haya terminado, ya que aunque el círculo tiene 0 opacidad, todavía se puede hacer clic en él hasta que se elimine.

Ejemplo aquí.

8. Construyamos un widget

Finalmente, reunamos lo que aprendimos y construyamos un pequeño medidor de estado de ánimo. Básicamente, seleccionarás un valor de estado de ánimo entre 1 y 5, siendo 1 'basura' y 5 'positivamente maníaco', y Raphael creará una buena representación de esto.

Ver el widget aquí

Comienza modificando our_script.js para que se vea así:

1
window.onload = function() {
2
    var paper = new Raphael(document.getElementById('canvas_container'), 500, 500);
3
    var circ = paper.circle(250, 250, 20).attr({fill: '#000'});
4
    var mood_text = paper.text(250, 250, 'My\nMood').attr({fill: '#fff'});
5
}

Esto crea un círculo de 20 px de radio en el centro de nuestro lienzo y un texto en la parte superior del círculo que dice "Mi estado de ánimo". 'Estado de ánimo' se coloca en una nueva línea usando '\n'.

Después, creamos información personalizada que corresponda a nuestros estados de ánimo y elegimos en qué estado de ánimo estamos.

1
moods = ['Rubbish', 'Not Good', 'OK', 'Smily', 'Positively Manic'];
2
colors = ['#cc0000', '#a97e22', '#9f9136', '#7c9a2d', '#3a9a2d'];
3
4
//pick a mood between 1 and 5, 1 being rubbish and 5 being positively manic
5
var my_mood = 1;

La descripción de texto de nuestro estado de ánimo se almacena en una matriz llamada 'estados de ánimo' y el color correspondiente a este estado de ánimo se almacena en una matriz llamada 'colores'. El estado de ánimo elegido, un valor entre 1 y 5, se almacena en la variable my_mood.

Ahora crearemos una función llamada show_mood. Cuando se invoca, esta función mostrará los círculos de nuestro estado de ánimo (los círculos de colores) y el texto correspondiente a este estado de ánimo.

1
function show_mood() {
2
3
    for(var i = 0; i < my_mood; i+=1) {
4
        (function(i) {
5
            setTimeout(function() {
6
                paper.circle(250, 250, 20).attr({
7
                    stroke: 'none',
8
                    fill: colors[my_mood - 1]
9
                }).animate({translation: '0 ' + (-42 * (i+1))}, 2000, 'bounce').toBack();
10
            }, 50*i);
11
        })(i);
12
    }
13
    paper.text(250, 300, moods[my_mood - 1]).attr({fill: colors[my_mood - 1]});
14
15
    mood_text.node.onclick = function() {
16
        return false;
17
    }
18
    circ.node.onclick = function() {
19
        return false;
20
    }
21
22
}

En show_mood(), tenemos un ciclo que itera tantas veces como el valor de my_mood. Dentro de este bucle hay una función anónima autoejecutable. Esto es necesario para que tengamos acceso a la variable i en cada etapa de la iteración. Dentro de la función autoejecutable, creamos un tiempo de espera: cada 50*i segundos, se crea un círculo en el punto de nuestro círculo original. Luego, cada círculo se traduce durante 2 segundos a 0px en x y algunos múltiplos de -42px en y. Nos aseguramos de colocar cada círculo sucesivo en la parte posterior del lienzo. Ten en cuenta que los círculos se rellenan de acuerdo con el color de la matriz de colores, determinado por my_mood.

show_mood() también es responsable de la visualización de nuestro texto de estado de ánimo que usa my_mood para elegir el estado de ánimo correspondiente de la matriz de estados de ánimo.

show_mood() y finalmente deshazte de los controladores de eventos onclick asignados al texto y círculo original que colocamos en el centro del lienzo. Esto evita que se vuelvan a dibujar círculos de estados de ánimo.

Finalmente, asignamos controladores de eventos onclick al círculo central y al texto 'Mi estado de ánimo'. Asigno controladores de eventos a ambos elementos para que al hacer clic en el texto o en el círculo tenga el efecto de llamar a show_mood().

1
circ.node.onclick = show_mood;
2
mood_text.node.onclick = show_mood;

Conclusión

¡Bueno, eso es todo! Ahora deberías tener una plataforma sólida en la que basar tus exploraciones en el framework Raphael JS. Lo más importante es que espero que ahora estés ansioso por profundizar en Raphael JS y elaborar algunos hermosos dibujos y widgets para el navegador. No olvides seguirme en Twitter y compartir tus creaciones.