viernes, 14 de diciembre de 2012

Trasteando con SVG



La verdad es que la evolución de la web y sus estándares es apasionante. Si, muchas veces nos hemos tirado de los pelos por incompatibilidades, y aún queda para que todos esos problemas queden atrás... pero también podemos frotarnos las manos con las posibilidades que ya podemos ver y tocar.

Uno de los elementos que me lleva tiempo interesando es SVG. Siempre me ha gustado la idea de expresar gráficos como XML me parece elegante, legible y portable. Y gracias a que son documentos XML se pueden adaptar al estílo de la página utilizando CSS.

A los que siempre hemos huido de flash, por una razón y otra, ahora podemos sentir el potencial de este (bueno, al menos parte) utilizando SVG. Ya que no solo podemos describir elementos con XML, también podemos modificarlos utilizando JavaScript.

Para ir aprendiendo he dedicado un par de tardes a hacer este indicador que podría encajar en cualquier panel de control. Puedes descargarlo desde aquí.


Crear imágenes SVG es sencillo y toda la documentación está en la web del W3C como cualquier otro estándar. Veamos los puntos más importantes de esta imagen:


1. ViewBox



El ViewBox nos indica la zona visible del dibujo. Si no declaramos un ViewBox este se adaptará a la zona en la que se esté dibujando el documento. Si por el contrario lo declaramos, el dibujo se reescalará para adaptar el ViewBox a la zona donde se esté dibujando.

<svg
xmlns = "http://www.w3.org/2000/svg"
xmlns:xlink = "http://www.w3.org/1999/xlink"
version = "1.1"
viewBox = "0 0 300 150"
with = "300"
height = "150">
<!-- your code here -->
</svg>
view raw svg_1.xml hosted with ❤ by GitHub

2. Definiciones (defs)


Es posible reutilizar parte del código dentro del mismo documento utilizando definiciones. Podemos pensar en las definiciones como componentes que se podrán incrustar posteriormente tantas veces como queramos.

En este dibujo se han definido dos componentes: indicator_step y needle. El primero es utilizado para dibujar las marcas que nos ayudan a leer los valores indicados por la aguja, el segundo es la aguja del indicador.

<defs>
<g id="indicator_step" stroke-linecap="round" stroke="black">
<line x1="-145" y1="0" x2="-120" y2="0" stroke-width="1" />
<line x1="-145" y1="0" x2="-135" y2="0" stroke-width="4" />
</g>
<g id="needle" stroke-linecap="round" stroke="black" >
<line x1="-110" y1="0" x2="0" y2="0" stroke-width="1" />
<line x1="-80" y1="0" x2="0" y2="0" stroke-width="4" />
<line x1="-30" y1="0" x2="0" y2="0" stroke-width="6" />
<line x1="-20" y1="0" x2="0" y2="0" stroke-width="9" />
<line x1="-15" y1="0" x2="0" y2="0" stroke-width="12" />
<line x1="-10" y1="0" x2="0" y2="0" stroke-width="18" />
</g>
</defs>
view raw svg_2.xml hosted with ❤ by GitHub

3. Dibujado


Finalmente dibujamos todos los componentes de dentro a fuera. Primero dibujamos el fondo dibidido en tres areas de diferentes colores para distinguir rápidamente la importancia del nivel indicado:

<g id="background" stroke-width="4">
<path d="M150,148 H4 A148,148 0 0,1 150,2 Z" fill="lime" stroke="green" />
<path d="M150,148 V2 A148,148 0 0,1 278,78 Z" fill="yellow" stroke="orange" />
<path d="M150,148 L278,78 A148,148 0 0,1 296,148 Z" fill="pink" stroke="red" />
</g>
view raw svg_3.xml hosted with ❤ by GitHub

Sobre el fondo vamos a dibujar un texto para indicar de forma numérica el nivel mostrado po el indicador:

<rect x = "105" y = "55" width = "90" height = "30"
style = "opacity : .60;
fill : white;
stroke : black;
stroke-width : 2"
rx = "10"
ry = "10" />
<text id = "text" x = "150" y = "80"
style = "fill : black;
stroke-width : 2;
font-family : sans;
font-size : 25;
font-weight : bold;
text-anchor : middle">
0
</text>
view raw svg_4.xml hosted with ❤ by GitHub

A continuación reutilizamos la definición indicator_step para las marcas para dibujar los pasos del indicador una marca cada 15 grados:

<g id="steps" transform="translate(150, 148)">
<g transform="rotate(0)">
<use id="step_000" xlink:href="#indicator_step" />
</g>
<g transform="rotate(15)">
<use id="step_015" xlink:href="#indicator_step" />
</g>
<!-- more repetitions here -->
<g transform="rotate(180)">
<use id="step_180" xlink:href="#indicator_step" />
</g>
</g>
view raw svg_5.xml hosted with ❤ by GitHub

Finalmente dibujamos la aguja reutilizando la definición needle. Vemos que dentro se define una animación. Esta animcación ahora no tiene utilidaz, pero la declarración nos ayudará a mover la aguja mediante scripting de forma más sencilla:

<g id="needle" transform="translate(150, 148)">
<g id="needle_rotation" transform="rotate(0)">
<use xlink:href="#needle" />
<animateTransform id="animation"
attributeType = "XML"
attributeName = "transform"
type = "rotate"
begin = "indefinite"
end = "indefinite"
dur = "2s"
calcMode = "spline"
keySplines = "0 .5 0 1"
fill = "freeze" />
</g>
</g>
view raw svg_6.xml hosted with ❤ by GitHub

4. Scripting


Ya tenemos el dibujo, ahora podemos manipularlo gracias a que podemos acceder a él a través del DOM y una serie de APIs extra definidas en el estándar SVG. Este script es realmente sencillo, pero nos da una idea de como realizar unas transformaciones básicas.

function setAngle(finalAngle) {
var initialAngle = document
.getElementById('needle_rotation')
.transform
.animVal
.getItem(0)
.angle;
var aElement = document.getElementById('animation');
aElement.setAttribute('values', initialAngle + ';' + finalAngle);
aElement.beginElement();
}
function start(source) {
function iteration() {
setAngle(source.next() * 180);
setTimeout(iteration, 2000);
}
function updateText() {
document.getElementById('text').textContent = Math.floor(
document.getElementById('needle_rotation')
.transform
.animVal
.getItem(0)
.angle);
setTimeout(updateText, 100);
}
iteration();
updateText();
}
start({
next : function() {
return Math.random();
}
});
view raw svg.js hosted with ❤ by GitHub

No hay comentarios:

Publicar un comentario