Notxor tiene un blog

Defenestrando la vida

Interactuando con SVG

2021-10-30

Estos días de atrás comencé a dar unas clases extracurriculares1 sobre robótica en un colegio de Zaragoza. Es un colegio concertado laico que da buen servicio a una población con un amplio asentamiento de población migrante y de otros colectivos en peligro de exclusión. El proyecto me pareció interesante y, aparte del dinero que pagan ─que es poco, porque son dos horas a la semana─, me atrajo en cuanto me lo propusieron. Todo lo que hago me lo suelo tomar en serio y estas clases no son una excepción. Preparándolas me he llegado a liar con presentaciones e interacciones con elementos educativos que hasta ahora no había tocado. Por ejemplo, me hice una imagen svg de la tarjeta micro2 que utilizaremos en el aula. Sin embargo, quería que esa imagen fuera interactiva y en este artículo os cuento un poco de mis investigaciones sobre añadir interacción a los gráficos vectoriales desde javascript. Como veis, otra vez hago de psicólogo metido a programador.

El formato SVG

No creo descubrir a nadie cómo es el formato SVG, si digo que dicho formato es, básicamente, una clase de XML concebida para guardar objetos gráficos en su estructura. Hay mucha información sobre él a través de Internet. Podemos escribir el código XML directamente en un buffer de Emacs o editarlo como un gráfico vectorial con Inkscape. Por ejemplo, podemos escribir el siguiente código:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg viewbox="0 0 250 100" width="250" height="100">
    <!-- Creo un círculo -->
    <circle cx="50" cy="50" r="40" stroke="#006600" stroke-width="4" fill="#00cc00"/>
</svg>

El resultado se puede visualizar directamente en Emacs, como he dicho, pero lo podemos abrir también en Inkscape para una edición gráfica más cómoda:

Captura-pantalla_inkscape-circulo.png

Si lo abrimos en Emacs podemos alternar entre el gráfico

Captura-pantalla-svg-vista.png

y el código que lo genera, sólo pulsando C-c C-c.

Captura-pantalla-svg-codigo.png

¿Cuándo elegir un modo u otro de edición? Pues depende fundamentalmente del usuario. Para mi gusto, cuando los gráficos son sencillos, como el anterior, hacerlo con el texto de forma directa tampoco es muy complicado. Sin embargo, cuando se avanza en complejidad el manejo del gráfico en un buffer de texto y poder imaginarse el resultado que acarreará el texto que estamos escribiendo, se puede complicar mucho y para ello es recomendable utilizar un editor como Inkscape.

Sin embargo, si vamos a incluir javascript en el archivo, al final lo tendremos que editar en modo texto, pues Inkscape no permite, por lo menos todavía, añadir código de forma directa.

Incluir gráficos vectoriales en html

Dentro de una etiqueta img

La principal ventaja de incluir gráficos vectoriales en nuestra página, es que se puede escalar sin problemas de pixelado. Además, el formato .svg es un estándar soportado por la mayoría de los navegadores. Quizá haya problemas con los más antiguos, pero incluso Internet Explorer comenzó a soportarlo desde su versión 9.

El caso es que hay varias formas de incrustar contenido svg en un fichero html. De primeras pensé que daría igual cualquiera de las maneras que hay. Puesto que la forma más simple de editar un gráfico vectorial es con alguna aplicación que facilite la tarea como Inkscape, lo utilicé para dibujar la tarjeta por sus dos lados:

Sorry, your browser does not support SVG.

También tengo imágenes separadas para ilustrar los eventos que se producen. Lo que quería conseguir eran algunos efectos simples, como que se iluminaran los leds cuando pasara el ratón por encima o que al «pulsar uno de los botones» se iluminaran todos.

El gráfico así generado es de buena calidad y se visualiza correctamente independientemente del tamaño de la imagen generada. El problema es que cuando se importa como se haría con cualquier fichero gráfico, no se puede acceder a los objetos internos. Probé a meterle el javascript dentro de la imagen. El problema es que algunos eventos los realizaba correctamente y otros no. Por ejemplo, no me captura bien el onclick.

Necesitaba probar otros métodos para hacerlo interactivo, pero dada la complejidad del svg que necesitaba, no me atreví a hacerlo mediante código y preferí mantener los gráficos dibujados con Inkscape aunque lo que hice fue copiar directamente luego el contenido del fichero gráfico en una página html dentro de una etiqueta svg. Esta etiqueta dentro de un fichero html5 se comporta como cualquier otra etiqueta y puede contener etiquetas tanto svg como html. En el ejemplo de más adelante lo veremos mejor ilustrado.

La etiqueta svg a pelo

Esta opción es la más funcional para lo que quiero hacer. ¿Qué ventajas tiene?

  • Se carga rápidamente porque el código está en el interior del mismo html.
  • Se puede dar formato a los objetos desde la hoja de estilos css.
  • Los objetos internos del svg son accesibles desde DOM.

Por contra, no es tan impermeable como cargar un fichero de gráficos externos: hay que tener cuidado con que no nos cambie los tipos de letra u otras opciones. Si el fichero gráfico es muy complejo y optas por copiar el código XML interno, después de haberlo hecho o modificado con Inkscape, tienes que quitar algunas etiquetas exclusivas del editor, no aportan nada, el navegador las ignorará, pero no dejan de ser peso muerto en el mismo gráfico.

Interacción

Para ilustrar el artículo, he hecho un pequeño documento html para interactuar con objetos svg desde el contenido del documento. No he querido complicarlo demasiado. Apenas son dos objetos rectangulares y un objeto input que sirve para establecer el redondeo de las esquinas de dichos rectángulos. Cuando dicho redondeo de esquinas es igual al ancho del objeto, parece un círculo, mientras que cuando el valor de redondeo es 0 la figura es un perfecto cuadrado. Además en uno de ellos se invierten los valores, de modo que mientras uno es redondo el otro es cuadrado. Cuando el valor del input sea 20 (el valor medio) ambos cuadrados serán idénticos.

<!DOCTYPE html>
<html>
    <head>
        <title>SVG la cuadratura del círculo</title>
        <meta charset="UTF-8">
    </head>
    <body>
        <h1 align="center">La cuadratura del círculo</h1>
        <!-- Se pueden utilizar estilos para el objeto SVG -->
        <style>
         .verde {
             stroke: #006600;
             fill  : #00cc00;
         }
         .rojo {
             stroke: #660000;
             fill  : #cc0000;
         }
        </style>
        <!-- Se pueden agrupar elementos SVG en otros objetos HTML -->
        <p align="center">
          <svg viewBox="0 0 250 100" width="250" height="100"> 
            <a target="_blank"
               xlink:href="https://www.w3.org/Graphics/SVG/">
              <rect id="circulo" x="50" y="10" rx="40" ry="40" height="80" width="80" stroke-width="4" class="verde"/>
            </a>
            <rect id="cuadrado" x="150" y="10" rx="0" ry="0" height="80" width="80" stroke-width="4" class="rojo"/>
          </svg>
        </p>
        <div align="center">
            <!-- Las interacciones pueden realizarse a través de formularios -->
            <input type="range" id="rango" min="0" max="40" value="40" oninput="cuadratura(this.value)" width="250">
            <p id="etiqueta">40</p>
        </div>
        <script>
         function cuadratura(value) {
             // Establece el valor de redondeo de las esquinas de los dos
             // objetos del SVG. Obsérvese que se obtienen mediante `Id`
             var circulo = document.getElementById("circulo");
             var cuadrado = document.getElementById("cuadrado");
             var etiqueta = document.getElementById("etiqueta");
             circulo.setAttribute("rx", value);
             circulo.setAttribute("ry", value);
             cuadrado.setAttribute("rx", 40 - value);
             cuadrado.setAttribute("ry", 40 - value);
             etiqueta.innerHTML = value;
         }
        </script>
    </body>
</html>

Dicho código genera la siguiente página:

Captura-pantalla-cuadratura-del-circulo.png

Se pueden apreciar tres partes fundamentales en el código:

  • Una parte de estilos (sólo para mostrar que, efectivamente, se pueden afectar los estilos del svg desde los estilos de la página).
  • Una parte svg donde se define el viewbox y un par de formas, que serán las que cambien con los valores del formulario que hay justo a continuación.
  • Un script que modifica los distintos objetos de la página: los dos internos al svg y la etiqueta html que muestra el valor.

No he querido hacer un formulario completo para esto, sólo he puesto un objeto input de tipo range para hacer las modificaciones. Además en ese mismo elemento se llama al javascript, concretamente a la función cuadratura() con el propio value del input como parámetro. También se puede observar que en el código svg se pueden añadir etiquetas html, como por ejemplo, la etiqueta <a> para enlazar a otras direcciones web. Si pinchas en el objeto verde te abrirá la página oficial de la organización w3 sobre los gráficos svg. Es sencillo, por tanto poder hacer cualquier gráfico interactivo, aunque sea sólo a base de poner enlaces a otras páginas dentro del contenido svg.

Si no quieres copiar el código anterior en un fichero html y trastearlo, puedes interactuar con este código embebido en el mismo artículo.

40

Conclusiones

Estos días he aprendido mucho de las estructuras internas que se manejan en un svg. Mucho del código que he trasteado lo he hecho con Emacs viendo cómo los cambios en el texto se convertían en cambios gráficos.

Nunca me había puesto a meterle código a un gráfico vectorial, pero no es nada complicado hacerlo.

Footnotes:

1

Las extraescolares de toda la vida, pero que las cambian el nombre por aquello de que parezcan más modernas.

Categoría: svg javascript web

Comentarios

Debido a algunos ataques mailintencionados a través de la herramienta de comentarios, he decidido no proporcionar dicha opción en el Blog. Si alguien quiere comentar algo, me puede encontrar en Mastodon y en Diaspora con el nick de Notxor.

También se ha abierto un grupo en Telegram, para usuarios de esa red.

Disculpen las molestias.