Imágenes svg (svg): Se encontraron 28 apuntes:
Hemos trabajado con la imagen que tenemos junto a este párrafo a modo de "caso de estudio" en algunas ocasiones, como en los apuntes javascript: rollover en mapas y Áreas poligonales para efecto rollover. Ahora nos servirá también para iniciarnos en el formato SVG, motivados por el comentario que nos ha dejado en el primero de los apuntes citados el webmaster de uno de nuestros sitios amigos; Centell: Diseño Gráfico.
En principio vamos a poner el código para obtener la misma imagen, pero editable desde un simple editor de textos como el block de notas de cualquier sistema:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="300px" height="100px" > <rect fill="#0000ff" x="8" y="8" width="21" height="22" /> <polygon fill="#ff0000" points="82,7,85,7,86,6,86,7,88,9,88,10,90,12,90,13,91,14,91,15,93,17,93,18,95,20,95,21,97,23,97,24,93,28,93,29,88,34,88,35,84,39,83,39,82,38,80,38,79,37,77,37,76,36,75,36,74,35,72,35,71,34,70,34,69,33,67,33,66,32,65,32,65,25,66,24,66,12,67,11,70,11,71,10,74,10,75,9,77,9,78,8,81,8" /> <polygon fill="#ffff00" points="143,59,144,59,145,60,146,60,147,61,148,61,149,60,151,60,152,59,153,59,154,58,155,58,166,47,166,46,168,44,168,43,169,42,169,40,171,38,171,35,172,34,172,33,173,32,173,31,174,30,174,26,175,25,175,24,177,22,177,19,178,18,178,17,179,16,179,15,183,11,183,10,184,10,185,9,191,9,194,12,194,18,193,19,193,20,191,22,191,23,189,25,189,28,188,29,188,30,186,32,186,34,185,35,185,37,184,38,184,40,183,41,183,43,182,44,182,45,181,46,181,47,180,48,180,49,179,50,179,51,178,52,178,53,177,54,177,55,174,58,174,59,166,67,165,67,164,68,162,68,161,69,160,69,159,70,158,70,155,73,143,73,141,71,139,71,138,70,136,70,135,69,134,69,133,68,132,68,130,66,130,64,129,63,129,62,128,61,128,60,127,59,127,41,128,40,128,39,129,38,129,37,131,35,131,34,133,32,134,32,135,31,137,31,138,30,140,30,141,29,150,29,151,30,152,30,152,31,153,32,153,38,151,40,146,40,145,41,144,41,140,45,140,46,139,47,139,53,140,54,140,56" /> <polygon fill="#00ff00" points="206,49,207,50,209,50,213,54,213,56,214,57,214,62,213,63,213,65,209,69,207,69,206,70,201,70,200,69,198,69,194,65,194,63,193,62,193,57,194,56,194,54,198,50,200,50,201,49" /> </svg>
Viendo la imagen que acompaña este párrafo, parece que hemos copiado la misma imágen del principio, pero la primera es una imagen del tipo "png", y la segunda no es otra cosa que la inserción en este apunte del código que vemos en el recuadro anterior.
Para obtener las coordenadas hemos usado la misma herramienta que usamos en los dos apuntes mencionados en el primer párrafo con unas leves modificaciones, que hemos incluído también en este dominio, y puede accederse desde la sección "Otras páginas del dominio": Capturar áreas.
Por ahora nos hemos interesado en las figuras que pueden usarse también en áreas de imágenes como en los apuntes que hemos mencionado, y como puede apreciarse en el código, existen rectángulos y polígonos; pero también líneas, círculos y elipses. Esta es la referencia: Basic Shapes.
A partir de las coordenadas que ya hemos obtenido de las provincias de España, trataremos de generar el mapa de España en formato svg.
Para obtener el mapa que mostramos a continuación...
Hemos copiado del apunte :"Mapa de España", el array de las áreas de provincias que puede verse en su sección script: script.mapa_hispano, y luego hemos creado los polígonos con un sencillo bucle:
for (i in areas) { area = "<a xlink:href='http://es.wikipedia.org/wiki/" + i + "' xlink:title='" + i + "'>"; area += "\\n\\t<polygon fill='#00FF00' points='" + areas[i].coors + "'>"; area += "\\n\\t\\t<set attributeName='fill' from='#00FF00' to='#FF0000' begin='mouseover' end='mouseout'/>"; area += "\\n\\t\\t<set attributeName='stroke' from='#00FF00' to='#000000' begin='mouseover' end='mouseout'/>"; area += "\\n\\t</polygon>\\n</a>\\n"; tag("textarea_mapa").value += area; }
Nótese el efecto rollover cuyo funcionamiento lo hemos visto en este artículo: "Add interactivity to your SVG", aunque no funcione en todos los navegadores.
Esta pequeña isla balear nos sirve de referencia para mostrar su código, simplemente por tratarse de la figura con menos coordenadas por ser de menor dimensión.
<a xlink:href='http://es.wikipedia.org/wiki/Formentera' xlink:title='Formentera'> <polygon fill='#00FF00' points='532,315,531,316,530,315,530,312,531,312,532,313'> <set attributeName='fill' from='#00FF00' to='#FF0000' begin='mouseover' end='mouseout'/> <set attributeName='stroke' from='#00FF00' to='#000000' begin='mouseover' end='mouseout'/> </polygon> </a>
A diferencia del apunte anterior: SVG: Preliminares, dentro de la etiqueta polygon, que representa la figura poligonal de la isla, destacamos la forma de obtener el efecto rollover. También es de destacar la forma de enlazar de cada área.
Para crear figuras geométricas sencillas podemos hacerlo "a ojo", y obtener los resultados que mostramos a continuación, pero si nos animamos con sencillos cálculos, seguro que mejoraremos los resultados.
Hemos implementado el efecto rollover en las figuras, y aún quedando bien (bonito), no es lo deseado en los casos de las estrellas de cinco y ocho puntas. Intentaremos pues obtener estrellas de otra manera.
No profundizaremos mucho en cuestiones o ecuaciones matemáticas, pero para estos casos podemos usar la ecuación de la circunferencia. Y como precedente veremos el código de una vieja página donde implementamos con una librería gráfica propia, un reloj analógico; en concreto mostraremos como hemos pintado las marcas de las horas:
// creación de las 12 marcas... radian = Math.PI / 180; for (var i = 11; i < 24; i ++) { alfa = 30 * i; colorMarca = (alfa % 90 == 0) ? "black" : "gray"; anchoMarca = (alfa % 90 == 0) ? 3 : 2; alfaX = parseInt(Math.sin(alfa * radian) * 45) + 50; alfaY = parseInt(Math.cos(alfa * radian) * 45) + 50; marca = new Esfera("", alfaX, alfaY, anchoMarca, colorMarca, "visible"); laCaja.innerHTML += marca.generar(); }
Por lo pronto usaremos ese código para ver las coordenadas que recoge y fabricar un dodecágono:
A partir de las coordenadas obtenidas (28,88,49,95,72,88,88,72,95,51,88,28,72,12,51,5,28,12,12,28,5,49,12,72,28,88), hemos creado la figura que mostramos a la derecha; y echándole algo de imaginación, alternamos el radio en unas puntas por su mitad y obtenemos la estrella de seis puntas que se distingue a la izquierda de este párrafo. A diferencia del dodecágono, nuestra "estrella" de seis puntas está generada con valores reales, así que vamos a omitir mostrar esos puntos, aunque puede verse desde el código fuente del apunte; de todos modos mostraremos el código que genera los puntos.
Los retoques que hemos realizado al código lo reflejamos a continuación:
function seis_puntas() { coordenadas = []; radian = Math.PI / 180; for (var i = 11; i < 23; i ++) { alfa = 30 * i; radio = (i % 2 == 1) ? 22.5:45; coordenadas.push((Math.sin(alfa * radian) * radio) + 50); coordenadas.push((Math.cos(alfa * radian) * radio) + 50); } return coordenadas; }
Del código podemos deducir que para un determinado número de puntas necesitamos el doble de coordenadas alternando el radio de cada cálculo.
A partir de la última reflexión modificamos nuestro código y hemos conseguido la estrella que vemos a nuestra izquierda. Hemos cambiado algunos valores para ajustar el número de puntas y sus coordenadas.
También es posible crear estrellas como el asterisco que precede este párrafo agrupando lineas. Para insertar esta figura hemos añadido otros elementos básicos de las imágenes svg como la agrupación de elementos "g" y el atributo "viewbox".
A continuación mostramos variantes de la estrella de cinco puntas escaladas:
Nótese que al agrandar las imágenes, éstas no se pixelan como en los mapas de bits. En la última de las figuras hemos utilizado el atributo preserveAspectRatio para conseguir el efecto de estiramiento.
En un tema de los foros del web: Mapa de imagenes con coordenadas relativas, la única respuesta hasta ahora ha sido la mía recomendando el uso de imágenes svg. Para justificar mi recomendación me propuse hacer un ejemplo, pero para no saturar de "Mapas de España" este diario he decidido usar otro viejo ejemplo: "Rollover en mapa: Creando recortes circulares". A la derecha de este párrafo podemos ver una versión reducida del mismo mapa del citado apunte.
A continuación insertamos el código del mapa en miniatura que tiene la mitad del tamaño del original (300x197), en donde mostramos el mismo área original con su enlace; y hemos añadido otra área con otro enlace de otro de los caricaturistas que aparecen en la foto de familia:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="300px" height="197px" viewbox="0 0 600 394"> <image x="0" y="0" width="600px" height="394px" xlink:href="diario.imagen.php?id=48&max=600" /> <a xlink:href='http://www.caricaturasmarbella.com' xlink:title='caricaturas San Jorge'> <circle cx="231" cy="112" r="27" fill="transparent" stroke="gray" stroke-width="2" > <set attributeName='stroke' from='gray' to='red' begin='mouseover' end='mouseout'/> </circle> </a> <a xlink:href='http://www.sucaricatura.com' xlink:title='sucaricatura.com, su caricatura en Internet'> <circle cx="432" cy="225" r="27" fill="transparent" stroke="gray" stroke-width="2"> <set attributeName='stroke' from='gray' to='red' begin='mouseover' end='mouseout'/> </circle> </a> </svg>
Lo único que estamos usando por primera vez en nuestros apuntes relacionados con las imágenes "svg" es la etiqueta "image".
Esta etiqueta sirve para insertar una imagen en la posición especificada en los atributos x,y con las dimensiones width (ancho) y height (alto). Puede notarse la similitud con la etiqueta "img" en documentos "html"; en este caso no deben definirse etiquetas "map" ni sus "areas".
La última curiosidad sobre los enlaces en las figuras (en este caso los círculos), es que si definimos la figura sin relleno (fill="none"), la zona caliente de esa figura tan solo es la línea del círculo, pero usando el valor "transparent", la zona caliente también es el interior de la figura.
Para poder comparar los resultados, a continuación mostramos el mismo "mapa" sin reducir.
Y si revisamos los códigos, la única deferencia entre ambos mapas se encuentra en los atributos width y height de las etiquetas svg.
A veces queremos un efecto sencillo en alguna de las imágenes que ponemos en nuestras páginas y tenemos que depender de javascript, y en ocasiones con códigos realmente complejos. He de admitir que no todos los navegadores reproducen las imágenes svg de la misma manera, así que iremos indicando cómo incrustarlas para notar las diferencias.
La primera de las imágenes se ha incrustado directamente en la página. El código se muestra a continuación:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="40px" height="40px" viewbox="0 0 40 40" style="float: left; margin: 0 1em; border: 1px solid black; margin: 0 1em .5em 0"> <image x="0" y="0" width="40px" height="40px" xlink:href="http://www.pepemolina.com/logo.png"> <animateTransform attributeType="xml" attributeName="transform" type="rotate" from="0 20 20" to="360 20 20" dur="6s" repeatCount="indefinite"/> </image> </svg>
Para esta segunda presentación se ha creado un fichero externo, y se ha utilizado una etiqueta object; donde se ha asociado el fichero externo a su atributo "data", y además hemos especificado el mime-type "image/svg+xml" en el atributo "type".
La imagen que está en este párrafo se ha insertado con la etiqueta "iframe"; al igual que en el anterior caso, usamos el mismo atributo "type" con el mismo valor mime-type: "image/svg+xml", y la dirección URL la asociamos al atributo "src".
Y esta última imagen se ha mostrado con la etiqueta "img", donde también utilizamos el atributo type, y como toda imagen tiene su atributo alt y como nos gusta hacer en este diario, también ponemos la etiqueta longdesc con la dirección de su descripción.
Si nos planteamos hacer el dibujo de un reloj analógico, sencillamente tenemos que hacer un círculo con doce (12) marcas indicando las horas y tres (3) líneas para las agujas. Pues con el formato svg es tan sencillo como eso.
Con respecto a la animación de las agujas, la idea es que el segundero da una vuelta completa (360º) en un minuto; el minutero lo hace en una hora y la aguja horaria la da en 12 horas. Pues con el formato svg, también es tan sencillo como eso.
Lo único que hemos hecho con un lenguaje distinto al svg+xml es la ubicación inicial de las agujas:
header("Content-type: image/svg+xml"); $ya = getdate(); $segundero = $ya["seconds"]; $minutero = $ya["minutes"]; $hora = $ya["hours"] % 12; $rs = $segundero * 6; $rm = ($minutero + ($segundero / 60)) * 6; $rh = ($hora + ($minutero / 60) + $segundero / 3600) * 30; echo <<< svg <?xml version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="100" height="100" preserveAspectRatio="no"> <g id="esfera"> <circle cx="50" cy="50" r="47" fill="white" stroke="red" stroke-width="3"/> <line x1="50" y1="10" x2="50" y2="20" stroke="black" stroke-width="4" /> <line x1="50" y1="10" x2="50" y2="15" stroke="black" stroke-width="1" transform="rotate(30 50 50)"/> <line x1="50" y1="10" x2="50" y2="15" stroke="black" stroke-width="1" transform="rotate(60 50 50)"/> <line x1="50" y1="10" x2="50" y2="20" stroke="black" stroke-width="4" transform="rotate(90 50 50)"/> <line x1="50" y1="10" x2="50" y2="15" stroke="black" stroke-width="1" transform="rotate(120 50 50)"/> <line x1="50" y1="10" x2="50" y2="15" stroke="black" stroke-width="1" transform="rotate(150 50 50)"/> <line x1="50" y1="10" x2="50" y2="20" stroke="black" stroke-width="4" transform="rotate(180 50 50)"/> <line x1="50" y1="10" x2="50" y2="15" stroke="black" stroke-width="1" transform="rotate(210 50 50)"/> <line x1="50" y1="10" x2="50" y2="15" stroke="black" stroke-width="1" transform="rotate(240 50 50)"/> <line x1="50" y1="10" x2="50" y2="20" stroke="black" stroke-width="4" transform="rotate(270 50 50)"/> <line x1="50" y1="10" x2="50" y2="15" stroke="black" stroke-width="1" transform="rotate(300 50 50)"/> <line x1="50" y1="10" x2="50" y2="15" stroke="black" stroke-width="1" transform="rotate(330 50 50)"/> </g> <g id="hora"> <line x1="50" y1="25" x2="50" y2="55" stroke="black" stroke-width="3" transform="rotate($rh, 50 50)"/> <animateTransform attributeType="xml" attributeName="transform" type="rotate" from="0 50 50" to="360 50 50" dur="12h" repeatCount="indefinite"/> </g> <g id="minutero"> <line x1="50" y1="15" x2="50" y2="55" stroke="blue" stroke-width="2" transform="rotate($rm, 50 50)"/> <animateTransform attributeType="xml" attributeName="transform" type="rotate" from="0 50 50" to="360 50 50" dur="1h" repeatCount="indefinite"/> </g> <g id="segundero"> <line x1="50" y1="10" x2="50" y2="60" stroke="red" stroke-width="1" transform="rotate($rs, 50 50)"/> <animateTransform attributeType="xml" attributeName="transform" type="rotate" from="0 50 50" to="360 50 50" dur="1min" repeatCount="indefinite"/> </g> </svg> svg;
En este caso la hora que mostramos es la del servidor donde está alojada la página, pero para otro apunte lo ajustaremos con la hora del navegador.
Al ser svg un lenguaje "escalable", podemos crear el efecto de relojes ovalados simplemente aplicando estilos distintos en altura y anchura (width-height) de la imagen.
Volvemos a la temática sobre imágenes svg para mostrar otro mapa pero con nuevas características, como la redimensión (la "S" en Svg corresponde a "eScalable"). Otra razón para retomar la temática proviene de una consulta en los "Foros del web" sobre Mapa de España dinámico, donde se pide dinamismo, y aunque en anteriores temas sobre este formato de imágenes existía algún dinamismo, funcionaba de forma desigual en los distintos navegadores... y no debemos olvidar el deficiente soporte en las viejas versiones de éstos. También es desigual el comportamiento según la forma que decidamos incrustar nuestro mapa. A nuestra derecha se ha usado una etiqueta "img", y en el siguiente párrafo la etiqueta es "object".
A la izquierda de este párrafo se evidencia la escalabilidad del formato. También vemos que podemos variar los colores, resaltando si fuera necesario zonas concretas, y sin usar el lenguaje de programación habitual: javascript. El código fuente de esta imagen mayor es el mismo de la menor del primer párrafo, pero se ven distintas debido a los parámetros de cada una. También son distintos los parámetros de ésta más pequeña a la derecha, donde se resaltan todas las provincias de Andalucía. El código de nuestro comodín necesita de varios parámetros para indicar los colores que se muestran, ya sea de los elementos seleccionados, como del efecto "rollover", o la provincia o comunidad autónoma que deba resaltarse.
Para facilitar la creación de mapas para otros objetivos, hemos generado unos ficheros que esperamos sean de utilidad: la imagen del mapa de fondo, y los listados de comunidades con sus provincias, y de las provincias con sus coordenadas
Después de comprobar con qué facilidad se puede dibujar un sencillo reloj analógico decidimos poderlo configurar; y a la vez compartirlo. El código se puede obtener desde el fichero reloj.txt y es el mismo que del reloj que acompaña este párrafo.
Tal vez algún curioso a comparado las direcciones de ambas referencias y habrá notado que una termina en ".txt" y la otra en ".php", pero debemos comprender que los ficheros del tipo "php" no se ven tal cual desde un navegador, sino que se procesa. De hecho, el fichero "reloj.php" tiene solamente este contenido:
<?php include("reloj.txt"); ?>
Ya que estamos tratando la configuración de nuestro reloj, nos pondremos "manos a la obra"; pero aclarando que se trata de cosas básicas como los colores de sus componentes o la hora de inicio de su funcionamiento.
Como siempre en este diario, procuraremos usar un sistema "accesible", aunque desde luego, el formato svg no es soportado por igual por los distintos navegadores. Los cambios podrán verse en el pequeño reloj del párrafo anterior, o si se tiene javascript habilitado, podría mostrarse en una ventana nueva del navegador (marcando el primer control del formulario asociado).
Tampoco será accesible la opción "hora del navegador", aunque siempre se podrá poner la hora de inicio de forma manual:
Si se prueba la opción donde se abre una nueva ventana, el reloj se adaptará a las dimensiones de ésta, debido a que la declaración de las dimensiones en la etiqueta svg es de 100%.
Después de crear estrellas de cinco puntas, nos faltaría hacerlas con distintas características, variando el número de puntas u otras variantes.
Aunque nuestra imagen también es de cinco puntas basta con añadir el parámetro "puntas" con el valor que nos interese para que obtengamos otra estrella, por ejemplo de nueve (9) puntas.
Si han pulsado sobre el enlace del final del anterior párrafo, habrán notado que no solo ha cambiado el número de puntas de la estrella, sino que las puntas son de diferentes tamaños.
Ya hemos visto que se pueden obtener estrellas con distintas formas, y claro que podemos modificar sus colores, y aunque se generan con 100 pixeles de radio, son estrellas "escalables".
Para mostrar las distintas posibilidades crearemos un formulario cuyos resultados se notarán en la estrella superior:
Podemos obtener efectos muy curiosos con un número elevado de puntas, aunque tal vez no sea de utilidad.
Simplemente haremos un recuadro con nueve (9) filas y nueve (9) columnas, resaltando recuadros agrupados en tres (3) filas y tres (3) columnas. Los parámetros adicionales son la anchura de cada casilla y los valores iniciales donde cualquier valor distinto del rango de números aceptados [1..9] corresponderá a una casilla vacía. En la imagen asociada a este párrafo tenemos un ejemplo; y si miramos su código podemos afirmar que sería muy fácil implementar otras opciones como los colores del mismo.
Para conseguir entonces distintos sudokus, vamos a facilitar un formulario con la inicialización del mismo como parámetro:
Como podemos ver, con este apunte solo generamos el recuadro con sus casillas y los valores que nosotros pongamos. Investigaremos en su resolución.
Existen muchos posibles usos para dibujos con forma de estrella; tal vez uno de los más comunes son los sistema de evaluación muy usados en blogs o páginas parecidas (como este diario), pero nosotros lo aplicaremos en una alerta para indicar que tenemos nuevas fotos en la página de un amigo caricaturista: Caricaturas "San Jorge".
Usaremos la misma estrella del apunte "Generando estrellas svg", pero con un número mayor de puntas, y con el borde rojo (tal como la estrella adjunta). Tal como vemos en la imagen, estará estirada y le añadiremos un texto en dos (2) líneas. En nuestro caso concreto "Nuevas Fotos".
Para darle algo de dinamismo le hemos añadido un efecto de rotación a las puntas de la estrella con una duración de cinco (5) segundos por cada giro.
Para los visitantes que no dispongan de un navegador que soporte este tipo de imágenes (podríamos considerarlos como navegadores obsoletos o anticuados), también se generan estrellas estáticas añadiendo el parámetro "img" con un valor distinto de "svg". La imagen del párrafo anterior de este apunte es un ejemplo; en nuestro caso hemos programado el evento de error de carga de la imagen (onerror).
<img src="ficheros/estrella.php?texto=Nuevas|Fotos" id="estrella_ejemplo" alt="estrella" onerror="this.src += '&img=gif'"/>
El código completo se puede obtener desde su fuente original: estrella.txt; tan solo se debe cambiar la extensión del archivo o insertar el código con la instrucción include de php.
Hemos usado el formato svg para crear mapas interactivos muy sencillos; ahora iremos ampliando las posibilidades de este formato.
En el apunte "SVG: Mapa de España", mostramos un sencillo efecto rollover (imagen de sustitución), también utilizando el lenguaje XLink hemos creado enlaces y añadido títulos (igual que los mapas de imágenes); pero vamos a intentar añadir más vistosidad.
El efecto en la provincia de Málaga, no solo cambia el color de la provincia, también muestra un icono previamente ocultado con estilos. De la misma manera podríamos tener escondidos otros elementos salpicados por el resto del mapa.
Hemos reservado para la capital (con borde negro) una novedad en esta serie de apuntes, la inserción de elementos svg dentro de una imagen svg. Son cinco (5) estrellas que se ubicarán en la esquina superior derecha del dibujo.
Nos ayudaremos del código que habíamos usado en el apunte Generando estrellas svg. El código de la estrella de cinco (5) puntas lo hemos copiado e insertado dentro de nuestro mapa con distintas coordenadas y otro tamaño. Para poder ubicar la misma estrella, con los mismos puntos y dimensiones, pero en distintos sitios y un tamaño menor, hemos usado otros tantos elementos svg, tan solo variando la coordenada x.
Por último, crearemos dinámicamente una provincia -Gerona-, y la enlazaremos al apunte sobre el Congreso que aconteció el año 2011. Detallaremos esta inserción dinámica en un futuro apunte.
Volviendo a las imágenes SVG, vamos a crear nuestro mapa de forma dinámica. Advertimos que este apunte (al menos lo que intentamos mostrar) depende de que javascript esté habilitado en nuestro navegador (aunque sea lo habitual, está bien aclararlo; es más, aconsejamos hacer páginas navegables sin la dependencia de javascript).
Para inserta las provincias españolas, pulse el siguiente botón: ... y veremos aparecer pausadamente y de forma caótica todas las provincias españolas.
Vemos que se ha formado el mapa íntegramente, aunque no tenemos enlaces y el efecto de imagen de sustitución se consigue con estilos, que aunque sea correcto, podemos conseguir el efecto con código svg+xml. Así que modificaremos nuestro código.
En principio hemos usado las coordenadas que teníamos guardadas en el fichero provincias.txt, que aunque tenga el formato de los ficheros php solo puede ser visto como código fuente, o también puede ser insertado con una instrucción "include" del lenguaje; y es eso lo que hemos hecho en el fichero "provincias.php", que genera un fichero svg con la imagen que vemos a continuación: (pulse sobre la miniatura para ampliarla).
Al tratarse de un fichero svg, a la vez es un fichero xml, que nos vale para obtener datos con la tecnología Ajax. El código que genera la imagen lo vemos a continuación:
<?php $fill = (isset($_GET["fill"])) ? $_GET["fill"]:"green"; $ancho = (isset($_GET["ancho"])) ? $_GET["ancho"] : 625; $alto = 571 * $ancho / 625; include("provincias.txt"); header("Content-type: image/svg+xml"); $svg =<<< svg <?xml version="1.0" encoding="ISO-8859-1" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN" "http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="$ancho" height="$alto" preserveAspectRatio="none" viewBox="0 0 625 571"> svg; foreach($provincias as $p => $item) { $pr = htmlspecialchars($p); $svg .=<<< p <polygon id="$p" fill="$fill" stroke="black" points="$item" /> p; } echo <<< mapa $svg </svg> mapa; ?>
Teniendo las provincias y sus coordenadas en nuestro fichero xml+svg, tan solo nos queda insertarlas con javascript.
A diferencia de elementos normales html, los elementos xml se crean con "document.createElementNS", y los atributos se asignan con setAttributeNS. La coletilla "NS" viene de namespace o Espacio de nombres, y será el primero de los parámetros que tendrá la instrucción createElementNS.
Antes de continuar, explicaremos cómo hemos llegado a esto. El código que habíamos utilizado es el siguiente:
provincias = []; function suma_provincia() { if (provincias.length > 0) { p = provincias.pop(); provincia = p.provincia; puntos = p.puntos; nueva = document.createElementNS(svgns, "polygon"); nueva.setAttributeNS(null, "id", provincia); nueva.setAttributeNS(null, "fill", "lime"); nueva.setAttributeNS(null, "points", puntos); nueva.setAttributeNS(null, "stroke", "black"); tag("crear_mapa_din").appendChild(nueva); setTimeout(suma_provincia, Math.floor(Math.random() * 200)); } else { tag("espera").style.display = "none"; tag("restar_provincias").disabled = false; } } function obtener_provincias(continuar) { ajax = objetoAjax(); ajax.open("get", "ficheros/provincias.php", true); ajax.onreadystatechange = function() { if (ajax.readyState == 4) { resultado = ajax.responseXML; polys = resultado.getElementsByTagName("polygon"); for (i = 0, total = polys.length; i < total; i++) provincias.push({"provincia": polys[i].getAttribute("id"), "puntos": polys[i].getAttribute("points")}); window[continuar](); } } ajax.send(null); } function crear_mapa() { tag("espera").style.display = "inline"; tag("sumar_provincias").disabled = true; tag("sumar_provincias_link").disabled = true; obtener_provincias("suma_provincia"); }
Podemos ver que el espacio de nombres es usado en la creación del elemento "polygon", pero no debe especificarse en los atributos del elemento. Pero con los enlaces debemos usar otra táctica.
Hasta ahora hemos usado "null" para espacio de nombres de los atributos de los polígonos, pero si bien al insertar una etiqueta "a", seguimos creándola con el espacio de nombres svg; tanto el atributo href como el title es parte del lenguaje XLink, así que al definirlos, debemos asociarlos a esa otra librería.
svgns = "http://www.w3.org/2000/svg"; xlinkns = "http://www.w3.org/1999/xlink"; function suma_provincia_link() { if (provincias.length > 0) { p = provincias.caos(); provincia = p.provincia; puntos = p.puntos; nueva = document.createElementNS(svgns, "polygon"); nueva.setAttributeNS(null, "id", provincia); nueva.setAttributeNS(null, "fill", "lime"); nueva.setAttributeNS(null, "points", puntos); nueva.setAttributeNS(null, "stroke", "black"); enlace = document.createElementNS(svgns, "a"); enlace.setAttributeNS(xlinkns, "href", "diario.jocker.php?extra=info_hispano&provincia=" + escape(provincia)); enlace.setAttributeNS(xlinkns, "title", provincia); setenlace = document.createElementNS(svgns, "set"); setenlace.setAttributeNS(null, "attributeName", "fill"); setenlace.setAttributeNS(null, "begin", "mouseover"); setenlace.setAttributeNS(null, "end", "mouseout"); setenlace.setAttributeNS(null, "from", "lime"); setenlace.setAttributeNS(null, "to", "blue"); nueva.appendChild(setenlace); enlace.appendChild(nueva); tag("crear_mapa_din").appendChild(enlace); setTimeout(suma_provincia_link, Math.floor(Math.random() * 300)); } else { tag("espera_link").style.display = "none"; tag("restar_provincias").disabled = false; } } function crear_mapa_link() { tag("espera_link").style.display = "inline"; tag("sumar_provincias").disabled = true; tag("sumar_provincias_link").disabled = true; obtener_provincias(suma_provincia_link); }
Para probar el código expuesto, en primer lugar limpiaremos el mapa que acabamos de crear pulsando en el botón .
Y ahora podemos crear la nueva versión pulsando el botón que vemos a continuación:
En próximos apuntes veremos más cosas sobre estas imágenes.
Veremos como podemos resolver un sudoku introduciendo los números por teclado. No intentaremos resolverlo con código, ya que nuestra intención es otra: la interacción.
Podemos escoger entre los sudokus en miniatura, o incluír cada número pulsando sobre cada casilla. En este caso no hacemos ninguna verificación.
Algunos de los ejemplos los hemos visto ya en anteriores apuntes.
Para responder a un evento hay que asociarlo de la misma forma que con cualquier elemento html. Hemos asignado un identificador "id" a cada casilla de forma que la primera tiene id="casilla_0_0" y la última : id="casilla_8_8"; y dentro de cada casilla tenemos un elemento "text" (texto) con similar identificador, reemplazando "casilla_" por "txt_":
function tag(id) {return document.getElementById(id);} function poner_evento(elemento, evento, f) { if (document.addEventListener) elemento.addEventListener(evento, f, true); else if (document.attachEvent) elemento.attachEvent("on" + evento, f); else elemento["on" + evento] = f; } // De la inicialización de la página, nos interesan estas pocas líneas. for (i = 0; i < 9; i++) for (j = 0; j < 9; j++) { tag("txt_" + i + "_" + j).appendChild(document.createTextNode("")); poner_evento(tag("casilla_" + i + "_" + j), "click", entrada); }
Podemos resumir en estas pocas líneas que a cada casilla le asociamos al evento "click" (onclick) la función "entrada()" que mostramos a continuación:
function entrada() { id = this.id; coleta = id.substr(8); xy = coleta.split("_"); n_actual = tag("txt_" + coleta).firstChild.data; n = prompt("nuevo valor para la casilla [" + xy + "] : ", n_actual)[0]; if ("123456789".indexOf(n) != -1) { sudoku = document.forms.form_sudoku.inicio.value; valores = sudoku.split(""); posi = parseInt(xy[0]) * 9 + parseInt(xy[1]); valores[posi] = n; document.forms.form_sudoku.inicio.value = valores.join(""); tag("txt_" + coleta).replaceChild(document.createTextNode(n), tag("txt_" + coleta).firstChild); } }
Sobre este último código, solo reseñaremos que tanto la lectura como escritura de nodos de texto se hace de la misma manera que hemos hecho hasta el momento.
Hemos hecho un par de animaciones rotando nuestro logo y las manecillas de un reloj analógico. Ahora nos toca volver a animar nuestro logo pero con movimientos lineales. Básicamente moveremos la imagen en tres direcciones:
Para obtener las tres (3) animaciones con un solo fichero, debemos pasarle como parámetro el tipo (t) y los valores serán:
El código lo mostramos en este enlace al extra: logo_animado_path_svg (donde se puede comentar y valorar), y junto a estas líneas:
// descripción para la sindicación: /* Este comodín sirve para animar el logo con tres (3) movimientos lineales. */ $tipo = (isset($_GET["t"])) ? $_GET["t"]:"v"; if (!in_array($tipo, array("v", "h", "d"))) $tipo = "v"; if ($tipo == "v") $poner =<<< vertical <use x="0" y="-40" transform="rotate(180 20 -20)" xlink:href="#logo_svg" /> <animateMotion path="M 0 0 V 40 Z" dur="2s" repeatCount="indefinite" /> vertical; elseif ($tipo == "h") $poner =<<< horizontal <use x="-40" y="0" xlink:href="#logo_svg" /> <animateTransform attributeName="transform" attributeType="XML" type="translate" from="0" to="40" dur="2s" repeatCount="indefinite" /> horizontal; else $poner =<<< diagonal <use x="0" y="-40" xlink:href="#logo_svg" /> <use x="-40" y="0" xlink:href="#logo_svg" /> <use x="-40" y="-40" xlink:href="#logo_svg" /> <animateMotion path="M 0 0 L 40 40 M 0 0 Z" dur="2s" repeatCount="indefinite"/> diagonal; header("Content-type: image/svg+xml"); echo <<< svg <?xml version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="40" height="40" preserveAspectRatio="yes"> <defs> <image id="logo_svg" width="40" height="40" xlink:href="http://www.pepemolina.com/logo.png" /> </defs> <g> <use x="0" y="0" xlink:href="#logo_svg" /> $poner </g> </svg> svg;
Vemos nuevos elementos en nuestra imagen svg que comentaremos a continuación.
Entre las novedades vemos el elemento "defs" (definiciones), y dentro definimos un elemento image, con identificador (id) "logo_svg".
La imagen, tal como indica su id, es nuestro logo y tiene definidos los atributos de tamaño. Luego vemos una etiqueta "g" donde agrupamos un elemento nuevo: "use", donde se "usa" la imagen definida en la sección defs; y también vemos una variable php cuyo valor es según el tipo de animación, uno o tres elementos "use" similares pero con distintos valores de posicionamiento (x, y). También se agrupa la etiqueta de la animación (animateMotion o animateTransform).
Ya habíamos usado animateTransform en nuestras anteriores animaciones y eran del tipo "rotate" (rotación); en este caso hemos usado translate (traslado), y su uso es similar, así que no nos detendremos en explicarlo. En el caso de animateMotion encontramos atributos similares como dur (duración) y repeateCount (repeticiones), pero las coordenadas se definen con el atributo "path", cuyo contenido es el mismo del atributo "d" (data/datos) de la etiqueta de igual nombre: "path".
Para entender, al menos básicamente, los valores del recorrido, debemos pensar que cada recorrido es una serie de letras o comandos seguidos de coordenadas. Empezamos con el comando M (moveTo/mover a coordenada), y en ambos casos sigue con "0 0"; o sea que el inicio de nuestro recorrido es la coordenada (0, 0), pero para no confundirnos, iremos por partes. El primer recorrido:
Comprobamos que comienza desde el origen que ya comentamos, y sigue con la instrucción "V" (línea vertical) con el valor 40. Un solo valor porque no varía la coordenada x; o sea pasa de (0, 0) a (0, 40). Luego vemos la instrucción "Z" (close path) que indica que hemos terminado y cerramos el recorrido (volviendo al origen (0, 0).
El otro recorrido "path" es diagonal:
También iniciamos el recorrido en la coordenada (0, 0), pero la siguiente instrucción es "L" (LineTo/línea), seguido de las coordenadas (40, 40), o sea desde la esquina superior izquierda a la esquina inferior derecha. Y antes de cerrar el recorrido nos movemos (M) al origen, consiguiendo el efecto de un movimiento contínuo en diagonal... si omitiésemos el último comando "M", o lo hubiésemos reemplazado por un comando "L", el efecto sería similar al recorrido vertical antes descrito.
Vamos a empezar con la imagen que vemos a nuestra derecha, que ya nos ha servido en el apunte: SVG: Preliminares para iniciar una serie de apuntes sobre este formato vectorial. Recordemos los pasos que hemos dado para obtener la misma imagen en formato svg.
En primer lugar hemos comprobado que la imagen tiene colores planos y bien vistosos: azul
Al seleccionar una imagen para capturar sus áreas, encontraremos la paleta de colores de esa imagen, y en este caso, junto a los colores que habíamos indicado, se suma el color de fondo, que a pesar de ser transparente, se muestra blanco.
Ya con nuestra imagen mostrada en nuestra aplicación tan solo tenemos que pinchar en la forma que deseemos capturar para que obtengamos el resultado esperado.
Para crear la imagen en formato svg, tal como vemos en este párrafo, podemos usar un fichero externo y enlazarlo con la típica etiqueta "img", o incrustar el código directamente en la página web donde querramos mostrarla; ese código lo podemos ver desde el código fuente de la página (aunque lo mostraremos en este apunte).
Pero las coordenadas también pueden servirnos para crear las imágenes dinámicamente. Entre las posibilidades para crear esas imágenes, existe la posibilidad de generarlas en un servidor por ejemplo con el lenguaje php y sus librerías GD. Y con javascript tenemos otras posibilidades como las imágenes svg y canvas, cuyas posibilidades comentaremos a continuación.
Aquí distintos códigos para obtener el mismo resultado visual:
SVG incrustado: Se puede usar directamente en los navegadores modernos (con soporte para este tipo de imágenes), las etiquetas "svg". También puede ser un fichero externo con la cabecera xml, y el resto del código presentado; enlazándolo con las etiquetas "img", "embed", "iframe" u "object".
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="300px" height="100px" class="flotado_izquierdo" > <polygon fill="#0000ff" points="29,8,29,30,8,30,8,8" /> <polygon fill="#ff0000" points="82,7,85,7,86,6,86,7,88,9,88,10,90,12,90,13,91,14,91,15,93,17,93,18,95,20,95,21,97,23,97,24,93,28,93,29,88,34,88,35,84,39,83,39,82,38,80,38,79,37,77,37,76,36,75,36,74,35,72,35,71,34,70,34,69,33,67,33,66,32,65,32,65,25,66,24,66,12,67,11,70,11,71,10,74,10,75,9,77,9,78,8,81,8" /> <polygon fill="#ffff00" points="143,59,144,59,145,60,146,60,147,61,148,61,149,60,151,60,152,59,153,59,154,58,155,58,166,47,166,46,168,44,168,43,169,42,169,40,171,38,171,35,172,34,172,33,173,32,173,31,174,30,174,26,175,25,175,24,177,22,177,19,178,18,178,17,179,16,179,15,183,11,183,10,184,10,185,9,191,9,194,12,194,18,193,19,193,20,191,22,191,23,189,25,189,28,188,29,188,30,186,32,186,34,185,35,185,37,184,38,184,40,183,41,183,43,182,44,182,45,181,46,181,47,180,48,180,49,179,50,179,51,178,52,178,53,177,54,177,55,174,58,174,59,166,67,165,67,164,68,162,68,161,69,160,69,159,70,158,70,155,73,143,73,141,71,139,71,138,70,136,70,135,69,134,69,133,68,132,68,130,66,130,64,129,63,129,62,128,61,128,60,127,59,127,41,128,40,128,39,129,38,129,37,131,35,131,34,133,32,134,32,135,31,137,31,138,30,140,30,141,29,150,29,151,30,152,30,152,31,153,32,153,38,151,40,146,40,145,41,144,41,140,45,140,46,139,47,139,53,140,54,140,56" /> <polygon fill="#00ff00" points="206,49,207,50,209,50,213,54,213,56,214,57,214,62,213,63,213,65,209,69,207,69,206,70,201,70,200,69,198,69,194,65,194,63,193,62,193,57,194,56,194,54,198,50,200,50,201,49" /> </svg>
PHP + librería GD: En servidores con soporte para el lenguaje php, puede tenerse el código presentado en un fichero con extensión "php" (por ejemplo: figuras.php), enlazándolo con una etiqueta img (también valdrían las alternativas del caso anterior).
<?php $formas = array( array("color" => "0000ff", "puntos" => "29,8,29,30,8,30,8,8"), array("color" => "ff0000", "puntos" => "82,7,85,7,86,6,86,7,88,9,88,10,90,12,90,13,91,14,91,15,93,17,93,18,95,20,95,21,97,23,97,24,93,28,93,29,88,34,88,35,84,39,83,39,82,38,80,38,79,37,77,37,76,36,75,36,74,35,72,35,71,34,70,34,69,33,67,33,66,32,65,32,65,25,66,24,66,12,67,11,70,11,71,10,74,10,75,9,77,9,78,8,81,8"), array("color" => "ffff00", "puntos" => "143,59,144,59,145,60,146,60,147,61,148,61,149,60,151,60,152,59,153,59,154,58,155,58,166,47,166,46,168,44,168,43,169,42,169,40,171,38,171,35,172,34,172,33,173,32,173,31,174,30,174,26,175,25,175,24,177,22,177,19,178,18,178,17,179,16,179,15,183,11,183,10,184,10,185,9,191,9,194,12,194,18,193,19,193,20,191,22,191,23,189,25,189,28,188,29,188,30,186,32,186,34,185,35,185,37,184,38,184,40,183,41,183,43,182,44,182,45,181,46,181,47,180,48,180,49,179,50,179,51,178,52,178,53,177,54,177,55,174,58,174,59,166,67,165,67,164,68,162,68,161,69,160,69,159,70,158,70,155,73,143,73,141,71,139,71,138,70,136,70,135,69,134,69,133,68,132,68,130,66,130,64,129,63,129,62,128,61,128,60,127,59,127,41,128,40,128,39,129,38,129,37,131,35,131,34,133,32,134,32,135,31,137,31,138,30,140,30,141,29,150,29,151,30,152,30,152,31,153,32,153,38,151,40,146,40,145,41,144,41,140,45,140,46,139,47,139,53,140,54,140,56"), array("color" => "00ff00", "puntos" => "206,49,207,50,209,50,213,54,213,56,214,57,214,62,213,63,213,65,209,69,207,69,206,70,201,70,200,69,198,69,194,65,194,63,193,62,193,57,194,56,194,54,198,50,200,50,201,49") ); function rgbColor($fondo) { $red = (int) hexdec(substr($fondo, 0, 2)); $green = (int) hexdec(substr($fondo, 2, 2)); $blue = (int) hexdec(substr($fondo, 4, 2)); return array($red, $green, $blue); } $imagen = imagecreate(300, 100); $transpa = imagecolorallocate($imagen, 254, 254, 254); imagefill($imagen, 0, 0, $transpa); imagecolortransparent($imagen, $transpa); foreach ($formas as $f => $i) { $color = rgbColor($i["color"]); $_rr = (int) $color[0]; $_rg = (int) $color[1]; $_rb = (int) $color[2]; $_relleno = imagecolorallocate($imagen, $_rr, $_rg, $_rb); $puntos = explode(",", $i["puntos"]); $npuntos = count($puntos) / 2; imagefilledpolygon($imagen, $puntos, $npuntos, $_relleno); } header("Content-type: image/png"); imagepng($imagen); imagedestroy($imagen); ?>
SVG dinámico: Así como mostramos con el ejemplo anterior un array con los colores y coordenadas de las distintas figuras que tenemos, podemos tener un array similar en javascript y crear el fichero svg.
formas = [ {"color": "#0000ff", "puntos": "29,8,29,30,8,30,8,8"}, {"color": "#ff0000", "puntos": "82,7,85,7,86,6,86,7,88,9,88,10,90,12,90,13,91,14,91,15,93,17,93,18,95,20,95,21,97,23,97,24,93,28,93,29,88,34,88,35,84,39,83,39,82,38,80,38,79,37,77,37,76,36,75,36,74,35,72,35,71,34,70,34,69,33,67,33,66,32,65,32,65,25,66,24,66,12,67,11,70,11,71,10,74,10,75,9,77,9,78,8,81,8"}, {"color": "#ffff00", "puntos": "143,59,144,59,145,60,146,60,147,61,148,61,149,60,151,60,152,59,153,59,154,58,155,58,166,47,166,46,168,44,168,43,169,42,169,40,171,38,171,35,172,34,172,33,173,32,173,31,174,30,174,26,175,25,175,24,177,22,177,19,178,18,178,17,179,16,179,15,183,11,183,10,184,10,185,9,191,9,194,12,194,18,193,19,193,20,191,22,191,23,189,25,189,28,188,29,188,30,186,32,186,34,185,35,185,37,184,38,184,40,183,41,183,43,182,44,182,45,181,46,181,47,180,48,180,49,179,50,179,51,178,52,178,53,177,54,177,55,174,58,174,59,166,67,165,67,164,68,162,68,161,69,160,69,159,70,158,70,155,73,143,73,141,71,139,71,138,70,136,70,135,69,134,69,133,68,132,68,130,66,130,64,129,63,129,62,128,61,128,60,127,59,127,41,128,40,128,39,129,38,129,37,131,35,131,34,133,32,134,32,135,31,137,31,138,30,140,30,141,29,150,29,151,30,152,30,152,31,153,32,153,38,151,40,146,40,145,41,144,41,140,45,140,46,139,47,139,53,140,54,140,56"}, {"color": "#00ff00", "puntos": "206,49,207,50,209,50,213,54,213,56,214,57,214,62,213,63,213,65,209,69,207,69,206,70,201,70,200,69,198,69,194,65,194,63,193,62,193,57,194,56,194,54,198,50,200,50,201,49"} ]
Y con esos datos generar la imagen por ejemplo con un simple botón como el que mostramos a continuación:
A continuación el código:
function generar_svg() { var xmlns = "http://www.w3.org/2000/svg"; tag_svg = document.createElementNS(xmlns, "svg"); tag_svg.style.width = "300px"; tag_svg.style.height = "100px"; tag_svg.style.border = "1px solid black"; for (i = 0, ti = formas.length; i < ti; i++) { tag_polygon = document.createElementNS(xmlns, "polygon"); tag_polygon.setAttributeNS(null, "fill", formas[i].color); tag_polygon.setAttributeNS(null, "points", formas[i].puntos); tag_svg.appendChild(tag_polygon); } tag("espacio_svg").appendChild(tag_svg); this.disabled = true; // deshabilitamos el botón... }
canvas dinámico: El mismo array de formas que hemos definido para crear las figuras svg dinámicamente nos servirá para crear la imagen canvas también con otro botón:
El código es parecido al principio, pero al no existir polígonos en canvas hay que fabricar su recorrido:
function generar_canvas() { tag_canvas = document.createElement("canvas"); tag_canvas.width = "300"; tag_canvas.height = "100"; tag_canvas.style.border = "1px solid black"; for (i = 0, ti = formas.length; i < ti; i++) { forma = tag_canvas.getContext("2d"); forma.fillStyle = formas[i].color; puntos = formas[i].puntos.split(","); y = puntos.pop(); x = puntos.pop(); forma.beginPath(); forma.moveTo(x, y); while(puntos.length > 0) { y = puntos.pop(); x = puntos.pop(); forma.lineTo(x, y); } forma.closePath(); forma.fill(); } tag("espacio_canvas").appendChild(tag_canvas); this.disabled = true; }
Con un código muy parecido al último mostrado se podrían crear recorridos "path" para imágenes svg.
Tal vez nos pueda interesar obtener un recorrido path desde las coordenadas de un polígono. El resultado visual será igual, pero la etiqueta path puede servirnos para otras cosas que veremos más adelante:
Ahora sí que nos encontramos con un código casi calcado al anterior:
function generar_svg_path() { tag_svg = document.createElementNS(xmlns, "svg"); tag_svg.style.width = "300px"; tag_svg.style.height = "100px"; tag_svg.style.border = "1px solid black"; for (i = 0, ti = formas.length; i < ti; i++) { puntos = formas[i].puntos.split(","); y = puntos.pop(); x = puntos.pop(); path = ["M " + x + " " + y]; while(puntos.length > 0) { y = puntos.pop(); x = puntos.pop(); path.push("L " + x + " " + y); } path.push("Z"); tag_path = document.createElementNS(xmlns, "path"); tag_path.setAttributeNS(null, "fill", formas[i].color); tag_path.setAttributeNS(null, "d", path.join(" ")); tag_svg.appendChild(tag_path); } tag("espacio_svg_path").appendChild(tag_svg); this.disabled = true; }
La lógica es similar, pero el recorrido en vez de generarse con una instrucción por trazo a partir de unas coordenadas iniciales, hemos guardado en un array los distintos trazos, el primero con una instrucción M (Move -> mover a coordenadas absolutas), y el resto de trazos mediante usando la instrucción L (Line -> línea hacia coordenada absoluta); terminando con una instrucción Z (cerrar recorrido absoluto).
Conseguir fondos en colores degradados (o gradientes) cada vez es más fácil. Hace algunos años hemos dedicado algunas páginas y artículos al respecto que comentaremos a continuación.
En las viejas "FAQs Javascript" de los "Foros del Web" (véase "Mis recomendaciones", a la izquierda de esta página) habíamos compartido un mensaje sobre "Fondo degradado" en páginas web, donde abusando de tablas creábamos ese efecto.
Un evidente signo de progreso ha sido la implementación de una aplicación que generaba imágenes para poder ser descargadas de la web: Gradientes (php + librerías GD), donde los gradientes eran imágenes de 1 pixel de altura o anchura (según el caso que fuera) y la otra dimensión era la longitud que necesitásemos.
Los resultados se conseguían de una forma muy sencilla, simplemente separando los componentes y calcular el incremento por cada uno de ellos (incluso negativos. El siguiente es el código javascript insertado en otra de las FAQs del foro:
function decahex(n) { return hexa.charAt(n / 16) + hexa.charAt(n % 16); } function colorHexa(c) { return "#" + decahex(c[0]) + decahex(c[1]) + decahex(c[2]); } function transitar(ini, fin, pasos) { var dato = desglose(ini); var rIni = dato[0]; var gIni = dato[1]; var bIni = dato[2]; var intermedios = new Array(pasos); intermedios[0] = dato; var dato = desglose(fin); intermedios[pasos - 1] = dato; var rFin = dato[0]; var gFin = dato[1]; var bFin = dato[2]; var rMed = (rFin - rIni) / (pasos - 1); var gMed = (gFin - gIni) / (pasos - 1); var bMed = (bFin - bIni) / (pasos - 1); for (var i = 1; i < pasos - 1; i ++) { var rgb = new Array(3); rgb[0] = parseInt(rIni + (rMed * i)); rgb[1] = parseInt(gIni + (gMed * i)); rgb[2] = parseInt(bIni + (bMed * i)); intermedios[i] = rgb; } for (i = 0; i < intermedios.length; i ++) intermedios[i] = colorHexa(intermedios[i]); return intermedios; } function desglose(color) { if (color.length != 6) return "poblema"; else { devolver = new Array(3); devolver[0] = hexadec(color.substr(0, 2)); devolver[1] = hexadec(color.substr(2, 2)); devolver[2] = hexadec(color.substr(4, 2)); } return devolver; } var hexa = "0123456789abcdef"; function hexadec(x) { x = x.toLowerCase(); return parseInt(hexa.indexOf(x.charAt(0))) * 16 + parseInt(hexa.indexOf(x.charAt(1))) }
Podemos ver en funcionamiento el código, desde el siguiente enlace: Efecto fundido en texto, que aunque trate de otro asunto, genera los colores necesarios para un gradiente entre dos colores y un número de pasos estipulado.
En la actualidad disponemos de otras opciones que deberían generar los mismos resultados. Nosotros plantearemos fondos aplicando estilos y las imágenes SVG.
De los tres gradientes que hemos expuesto, el primero es una imagen generada con un código similar al que expusimos al principio de este apunte. El segundo de los casos es simplemente una capa con estilos; que es precisamente lo que nos interesa, pero a día de hoy no es estándar, aunque es de esperar que pronto lo sea.
El último de los casos se trata de una imagen svg cuyo código es:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0" y="0" width="200" height="10" preserveAspectRatio="none" viewBox="0 0 200 10" id="gradientes_svg"> <defs> <linearGradient id="rojo_azul_svg" x1="0%" y1="0%" x2="100%" y2="0%" > <stop offset="0%" stop-color="red"/> <stop offset="100%" stop-color="blue"/> </linearGradient> </defs> <rect fill="url(#rojo_azul_svg)" width="100%" height="100%" /> </svg>
Nos falta usar canvas, para no depender del uso de javascript.
Juguemos a encontrar diferencias entre las dos imágenes que vemos junto a este párrafo... Ambas tienen el mismo color, el mismo borde y la misma forma. Entonces... ¿Hay diferencias?
Pues si que hay una sutil diferencia, la primera imagen está construida por un círculo y la segunda por un camino o recorrido "path". A continuación mostramos el código de esas imágenes:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="50px" height="50px"> <circle fill="#ff6666" cx="50" cy="50" r="49" stroke="#000000" transform="scale(.5)"/> </svg> <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="50px" height="50px"> <path fill="#ff6666" stroke="#000000" d="M 50, 50 m -49, 0 a 49, 49 0 1, 0 98, 0 a 49, 49 0 1, 0 -98, 0" transform="scale(.5)"/> </svg>
Y ¿para qué puede interesarnos hacer un circulo con un camino "path"? Veamos un sencillo ejemplo:
Junto a estas líneas vemos que "El mundo gira...", una imagen que hemos sacado de nuestra galería, que representa a nuestro planeta girando permanentemente. Pero el texto con el que hemos etiquetado a nuestra imagen también gira de la misma manera.
Sobre el uso de recorridos para alinear textos debemos tener en cuenta información adicional como el tipo de alineación y el lugar donde empezaremos el recorrido.
El código de las dos imágenes etiquetadas con el nombre del dominio "pepemolina.com", del párrafo anterior es el siguiente:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="80px" height="80px" style="padding: 1em; border: 1px solid blue; margin: 0 0 .5em 1em; float: right;" preserveAspectRatio="none" viewBox="0 0 100 100"> <use xlink:href="#vuelta_horaria" fill="pink" stroke="blue"/> <image xlink:href="diario.imagen.php?id=12&max=100" x="0" y="0" width="100%" height="100%" /> <text x="0" y="0" style="font-weight: bold"> <textpath xlink:href="#vuelta_horaria" text-anchor="middle" startoffset="25%"> <tspan dy="-4"> PEPEMOLINA.COM </tspan> </textpath> </text> </svg> <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100px" height="50px" style="padding: 1em; border: 1px solid blue; margin: 0 0 .5em 1em; float: right;" preserveAspectRatio="none" viewBox="0 0 100 50"> <text x="0" y="0" style="font-weight: bold;"> <textpath xlink:href="#media_vuelta_inversa" text-anchor="middle" startoffset="50%" method="align" > <tspan dy="10"> PEPEMOLINA.COM </tspan> </textpath> </text> <use xlink:href="#media_vuelta_inversa" fill="none" stroke="black"> </svg>
Previamente habíamos definido los recorridos junto al fondo en degradado del círculo de una de ellas en otra etiqueta svg:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="0" height="0"> <defs> <path id="vuelta_horaria" d="M 50, 50 m -50 0 a 50, 50 0 1, 1 100, 0 a 50, 50 0 1, 1 -100, 0"/> <path id="vuelta_inversa" d="M 50, 50 m -50 0 a 50, 50 0 1, 0 100, 0 a 50, 50 0 1, 0 -100, 0"/> <path id="media_vuelta_horaria" d="M 0, 50 a 50, 50 0 1, 1 100, 0"/> <path id="media_vuelta_inversa" d="M 0, 0 a 50, 50 0 1, 0 100, 0"/> <radialGradient id="fondo_svg" cx="50%" cy="50%" r="50%"> <stop offset="60%" style="stop-color: white; stop-opacity: 1" /> <stop offset="80%" style="stop-color: gold; stop-opacity: 1" /> <stop offset="100%" style="stop-color: magenta; stop-opacity: 1" /> </radialGradient> </defs> </svg>
Con respecto a la alineación centrada del texto en un recorrido, debemos poner el atributo "text-anchor" con el valor "middle" y el atributo "starOffset" con el valor "50%". Notese que hemos asignado el valor "25%" en un recorrido circular para centrar el texto en la parte superior del recorrido.
La manera de obtener los recorridos circulares está basada en el algoritmo:
<path d=" M cx cy m -r, 0 a r,r 0 1,0 (r * 2),0 a r,r 0 1,0 -(r * 2),0 " />
La discusión donde obtuvimos el algoritmo es "Circle drawing with SVG's arc path".
Después de añadir a nuestro Mapa de España las ciudades autónomas de Ceuta y Melilla hemos hecho algunas reflexiones:
A pesar de que nos ha sido de mucha utilidad el mapa usado, tuvimos que añadir Ceuta y Melilla de manera "muy artesanal", cuando existen otros mapas con sus coordenadas mucho más precisas. También notamos la ausencia del resto del continente europeo, aparentando ser una gran isla, y no un país integrado en su continente.
Intentaremos saber si nuestro navegador permite el formato svg simplemente cargando la imagen que vamos a usar, y que se encuentra en la wikipedia: Provincias de España.svg. Si se carga correctamente podemos crear una imagen nueva basándones en las características de la recientemente cargada
Pinchando sobre el botón que acompaña este párrafo (si todo va bien) realizaremos una petición Ajax para obtener los datos básicos de la etiqueta svg, junto a las distintas etiquetas "path" y "polygon" para dibujar sus siluetas. No olvidemos que los ficheros SVG son también XML (responseXML).
Antes de analizar los resultados, vemos el código que hemos usado:
svgns = "http://www.w3.org/2000/svg"; _colores_ = ["red", "lime", "blue", "green", "aqua"]; _color_ = 0; function _obtener_tags_svg_(url, donde) { ajax = objetoAjax(); ajax.open("get", url, true); ajax.onreadystatechange = function() { if (ajax.readyState == 4) { resultado = ajax.responseXML; // elemento raíz _svg_ = resultado.getElementsByTagName("svg")[0]; xmlns = _svg_.getAttribute("xmlns"); ancho = _svg_.getAttribute("width"); alto = _svg_.getAttribute("height"); box = _svg_.getAttribute("viewBox"); _imagen_ = document.createElementNS(svgns, "svg"); _imagen_.setAttribute("xmlns", xmlns); _imagen_.setAttribute("width", ancho); _imagen_.setAttribute("height", alto); _imagen_.setAttribute("viewBox", box); _imagen_.style.border = "1px solid black"; tag(donde).appendChild(_imagen_); // buscando formas (polygon, path) _tags_ = resultado.getElementsByTagName("*"); for (i = 0, total = _tags_.length; i < total; i++) { switch(_tags_[i].tagName.toLowerCase()) { case "polygon": _puntos_ = _tags_[i].getAttributeNS(null, "points"); _figura_ = document.createElementNS(svgns, "polygon"); _figura_.setAttributeNS(null, "points", _puntos_); _figura_.setAttributeNS(null, "stroke", _colores_[(_color_++ % _colores_.length)]); _figura_.setAttributeNS(null, "stroke-width", "1"); _figura_.setAttributeNS(null, "fill", "none"); _imagen_.appendChild(_figura_); break; case "path": _path_ = _tags_[i].getAttributeNS(null, "d"); _figura_ = document.createElementNS(svgns, "path"); _figura_.setAttributeNS(null, "d", _path_); _figura_.setAttributeNS(null, "stroke", _colores_[(_color_++ % _colores_.length)]); _figura_.setAttributeNS(null, "stroke-width", "1"); _figura_.setAttributeNS(null, "fill", "none"); _imagen_.appendChild(_figura_); break; }// switch }// for } } ajax.send(null); }
A partir del resultado que vemos, sabiendo que solo hemos añadido siluetas, nos resulta raro encontrarnos con textos. Evidentemente se han insertado con polígonos o recorridos (path). Podríamos reconocerlos y eliminarlos, pero al ver el resto de las siluetas, encontramos algunas líneas incorrectas, así que buscaremos otros mapas que nos puedan interesar más.
Haciendo una búsqueda en la Wikipedia, hemos encontrado otros mapas que mostramos a continuación; Pulsando sobre cada uno de ellos se mostrarán las siluetas de sus formas en el "Espacio para mostrar otro mapa":
Si todo ha ido bien y podemos ver las diferencias en las siluetas, nos decantaremos por el tercero de los mapas, que en un futuro apunte trataremos de estudiar más a fondo.
Entre las reflexiones hechas en el apunte SVG vs. Ajax, hemos decidido utilizar el mapa que se ve a continuación. Para mostrarlo hemos filtrado las etiquetas polygon y path; y hemos usado un color para cada etiqueta: rojo para "path" y verde para "polygon". El resultado, aunque parece bastante bueno, nos ha movido a estudiar más a fondo el mapa escogido.
Notamos a simple vista una tonalidad no esperada en la provincia de Cáceres, razón por la cual revisamos las etiquetas originales de la imagen para ver si encontrábamos algo extraño, y ¡eureka! (*). Vemos dentro de la lista de , la de "polyline", que básicamente es igual al polígono, así que arreglamos este problema tratando esta etiqueta igual que "polygon".
También vemos una forma rara en África de tono rojizo, por lo que podemos deducir que es una etiqueta "path", aunque viendo el mapa original deducimos que se trata de la línea fronteriza entre los dos países africanos: Marruecos y Argelia.
Al ver el fondo en tonalidad verdosa, y aún más intenso el recuadro de las islas canarias, deducimos que son cuadrados hechos con polígonos.
Antes de continuar describiremos unas sencillas herramientas para poder analizar más a fondo el mapa.
A la vez que generamos el mapa que estamos mostrando, hemos generado una variable de tipo objeto con los datos básicos que nos interesa: Tamaño y lista de formas que representan provincias y otros países. Con esos datos actualizamos un formulario que podemos mostrar y ocultar con el control que aparece a continuación: .
El formulario que aparece al pulsar el botón (y que puede volver a ocultarse volviéndolo a pulsar), nos muestra un control que nos permite seleccionar las formas. Seguidamente vemos el nombre que tiene la forma activa, que originalmente coincide con la etiqueta "svg" de la misma (path, polygon o polyline).
Luego encontramos el selector de estilos, que sirve para diferenciar la apariencia de las formas seleccionadas. El siguiente control es útil para reconocer los elementos pequeños, como Ceuta y Melilla. Aunque el "zoom" permite una ampliación de 20x, con una escala pequeña es suficiente.
Con esos controles que hemos descrito hemos obtenido un que evidentemente nos da motivos de reflexión.
Tanto las islas Canarias, como las Baleares están unidas en una sola etiqueta (las primeras citadas en dos), y nos interesa separarlas. Las fronteras entre provincias mostradas con elementos path no coinciden con los polígonos
Nos queda por comentar que hemos añadido un par de comportamientos a las formas del mapa: se pueden seleccionar simplemente pinchando sobre ella y al posar el puntero del ratón nos muestra el nombre que lleva. Creemos que son utilidades muy propicias para nominar cada elemento.
Las imágenes SVG también pueden servirnos para personalizar los fondos de nuestras páginas, o de algunos de sus elementos. En un mensaje de los foros, pensé que este formato de imágenes escalables podría ser ideal para dar la respuesta al tema "letras en diagonal (para el background)". Un estilo de fondo similar al de la respuesta es el que hemos puesto para el cuerpo de ésta página:
body { background: url("data:image/svg+xml;iso-8859-1, <svg xmlns='http://www.w3.org/2000/svg' width='300' height='300' x='0' y='0'> <text x='150' y='150' style='font: bold 36px arial; dominant-baseline: middle; fill: red; text-anchor: middle; opacity: .2' transform='rotate(-45,150,150)'> pepemolina.com </text> </svg>" ); }
En el código original no existen saltos de línea ni tabuladores.
Otro elemento svg que estamos usando de fondo es el mapa de España que aparece junto al título del apunte, que es el mismo del apunte: Mapas interactivos svg, y su código hemos modificado levemente para permitir opacidad: mapa_comunidades_hispanas.
De todos modos, despreocupándonos del tamaño al escalarla, tenemos una imagen del 300x300 (digamos que nos referimos a puntos), donde el texto lo ubicamos en el centro (150x150). Para el centrado horizontal usamos el estilo/atributo text-anchor, dándole el valor "middle" (mitad), y usamos el mismo valor para el centrado vertical en el estilo/atributo dominant-baseline.
Dejamos de lado el estilo de las letras, solo nos queda rotar nuestro texto: "transform='rotate(-45, 150, 150)'".
Sobre el mapa de España en la esquina superior derecha del espacio reservado para el apunte, solo comentaremos que hemos modificado el código que genera ese mapa, añadiendo la posiblidad de aplicarle opacidad.
Si revisamos nuestros grupos de apuntes, hemos tratado distintos tipos de ficheros svg. Un reloj analógico, figuras geométricas, mapas, textos con distintas sinuosidades, gradientes, animaciones... incluso postales digitales.
Tenemos nuestras postales digitales también en formato svg:
Podemos incrustarlas en nuestras páginas siguiendo los siguientes pasos:
Pero aún hay más...
Podemos tener las imagenes incrustadas sin la dependencia de Internet, eliminando de la línea de comandos del navegador el texto "&url=si".
También podemos escalarla (no nos olvidemos que la "s" de "svg" se debe a "eScalable") añadiendo "?&porcentaje=" + el porcentaje del escalado. Por ejemplo añadiendo "&porcentaje=50" nos mostrará la misma postal en la mitad de su tamaño... cambiando 50 por 200 será el doble de grande respecto a la original; etc.
Hace algunos años las revistas sobre temas informáticos nos inundaron de dibujitos conocidos como "clipart" (o clip-art).
Aquellos dibujos digitales eran sobre todo imágenes animadas en formato gif, o vectoriales wmf. Los primeros podrían ser útiles en presentaciones en ordenadores, y los segundos eran muy importantes en diseño gráfico para impresión.
Para obtener el mismo dibujo, pero en formato svg existen algunas aplicaciones para su conversión. La más conocida y de libre uso es Inkscape. Y sin ser libre existen aplicaciones de edición vectorial entre los que destacamos CorelDRAW.
Otras aplicaciones interesantes para la conversión son:
Usando estas aplicaciones hemos notado que en algunas ocasiones no funcionaba correctamente así que pensamos usar Inkscape, pero ayudados por un fichero de sistema por lotes.
Primero hemos creado un par de carpetas en nuestro sistema "localhost": clipart_wmf y clipart_svg. Luego hemos copiado el sistema de archivos wmf a la carpeta "clipart_wmf", y hemos creado un fichero php con el siguiente código:
<?php $l = Array(); function hacer($origen, $destino) { global $l; function recorrer($co, $cd) { global $l; if (!is_dir($cd)) mkdir($cd); $puntos = Array(".", ".."); $d = dir($co); $cuenta = 0; while (false !== ($entry = $d->read())) { if (!in_array($entry, $puntos)) if (is_dir("$co/$entry")) { recorrer("$co/$entry", "$cd/$entry"); } else { if (strpos(strtolower($entry), ".wmf")) { $base = substr($entry, 0, strrpos(strtolower($entry), ".wmf")).".svg"; if (!is_file("$cd/$base")) array_push($l, "c:\Inkscape\Inkscape ".str_replace("/", "\\", $co)."\\$entry --export-plain-svg=".str_replace("/", "\\",$cd)."\\$base"); $cuenta++; } } } echo "carpeta: $co ($cuenta dibujos)<br/>\n"; } if (is_dir($origen)) { recorrer($origen, $destino); file_put_contents("exportar.bat", implode("\n", $l)); } else echo "no existe carpeta $origen"; } hacer("dibus_wmf", "dibus_svg"); ?>
El código recorre la carpeta de ficheros wmf y por cada carpeta leída en origen, genera la misma carpeta en el destino; y por cada fichero wmf encontrado se añade a un array una línea de texto que luego se volcará a un fichero "exportar.bat".
Luego ejecutamos "exportar.bat", y si todo funciona correctamente, obtendremos los resultados deseados.
Según el número de ficheros y carpetas del sistema que tengamos, podría abortarse la página, así que deberíamos seguir la premisa: "divide y vencerás".
Podemos hacer la conversión por etapas, cancelando el proceso manualmente; en tal caso deberíamos generar nuevamente el fichero por lotes "exportar.bat", o eliminar las líneas de texto del fichero en cuestión referentes a los dibujos ya creados.
Hemos visto con el apunte anterior, que mapear una imagen ahora es fácil y preciso gracias a nuestra aplicación para Capturar áreas en imágenes. Ahora intentaremos hacerlo más bonito y divertido mostrando la evolución del trazado de cada área.
Vemos a continuación el mismo mapamundi del apunte Mapear una imagen paso a paso (que ya hemos mencionado), junto a unos controles que luego comentaremos.
A continuación indicaremos las características de los controles que hemos añadido al mapamundi.
La selección de áreas sencillamente aplica los parámetros seleccionados en el resto de controles. Si el trazo está hecho, simplemente se deshace, con el tiempo actualizado, y con el mismo trazo que fue creado. En el caso de haber una variación de otros parámetros, no se procesarán hasta la generación de un nuevo trazo.
El Tiempo en segundos es lo que tardará en dibujarse o desdibujarse el polígono que elijamos. Puede dibujarse el el mínimo tiempo (1 segundo) y desdibujarse en el máximo (10).
El Tipo de dibujo que tenemos activo es "punto a punto" donde se incluye un punto más visible en cada coordenada del área que se seleccione. La siguiente opción es el trazo de línea, que es muy parecida a las siguientes dos opciones, ambas se refieren al trazado de polígonos; y se diferencian porque el trazo de tarta se inicia desde el centro del polígono.
Sobre el Color de relleno, se aplicará a las figuras que se creen nuevas; y en el caso de que sea ddel tipo "punto a punto", el relleno se aplicará a cada punto y no al área.
Las áreas nuevas se crean encima de las viejas, así que podemos ver como se han detectado los puntos en nuestra aplicación. Usando un tiempo mayor, mejor se aprecia ese desarrollo.
Sobre el tipo de dibujo, en vez de comentar, invito a probarlo. Nótese la diferencia entre el trazo de líneas y de polígonos.
También invito a descubrir la diferencia entre el color transparente y la ausencia de color (sin color).
Puede verse el código javascript del apunte.
Hacer zoom en una imagen SVG es más fácil de lo que se puede pensar. Recomendamos para obtener el mapa nuestra aplicación "Capturar áreas en imágenes".
Sabiendo las coordenadas y tamaño de lo que queremos ampliar, simplemente tenemos que poner esos datos en el atributo "viewBox" de la etiqueta "svg".