Iconos SVG

Figura
Figura. Iconos SVG

Los SVG (Scalable Vector Graphics) son los Gráficos vectoriales escalables. Se estructuran en un documento XML para representar formas gráficas en dos dimensiones. Tienen un uso muy diverso y especialmente me interesa incorporarlos a este sitio como iconos.

Anteriormente venía utilizando los Sprites CSS para este cometido. Pero los SVG tienen algunas ventajas adicionales, especialmente que se incorporan al DOM como un elemento más. Así es posible darles estilo CSS adicional, respondiendo al tamaño de la fuente de texto o al color de primer plano, por ejemplo. Los Sprites, sin embargo, no dejan de ser imágenes, por lo que no hay muchas formas de interaccionar con ellas. Al incrementar su tamaño perderán resolución. Y no es posible cambiarles el color.

He tomado la mayor parte de ellos de otros sitios que permiten su uso. La mayoría son de la página de Google Material design: Icons. Los de Twitter y GooglePlus se encuentran en sus web Brand Twitter y Button +1 Google respectivamente. No pude encontrar ningún SVG para Facebook en su web y he usado el que aparece en Simple Icons.

Pero también es posible y relativamente fácil diseñar nuestros propios iconos SVG. Son todos los que se encuentran desde el icono CSS que se observa en la Figura. Más abajo comentaré un poco más sobre esto.

Lista de iconos SVG

En este apartado se presenta un ejemplo interactivo insertando todos los iconos SVG que actualmente uso en este sitio. Para ver como se ajustan a CSS, se incorporan un par de controles para cambiar el tamaño de la fuente y el color del contenedor donde está contenida la lista de iconos. Los SVG se ajustan al tamaño de la fuente que heredan del contenedor. Por otro lado aquellos que tiene su propiedad fill con el valor currentcolor tomarán el color del contenedor.

Ejemplo: Interaccionando con los iconos SVG

Font-size:

El estilo anterior se aplicará a la siguiente lista de iconos SVG:

Este ejemplo usa ES6 y modo estricto (en parte). Puedes consultar el código JS original de este ejemplo.

Usando iconos SVG para una página web

Si descargamos un SVG de la página mencionada mas arriba Material Designs: Icons nos vendrá en un archivo de texto con un contenido como el siguiente:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" 
width="48" height="48" fill="#000000">
    <path d="M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z"/>
    <path d="M0 0h24v24H0z" fill="none"/>
</svg>    
    

Vemos que es un XML con un elemento SVG especificándose un ancho y alto de 48px así como un relleno fill con el color negro #000000. En el siguiente ejemplo vamos a usar ese SVG prescidiendo de las medidas y poniéndole como relleno el valor currentcolor:

Ejemplo: Usando un icono SVG directamente

Texto 2em:

El código siguiente es el que desarrolla el ejemplo. Un contenedor tiene tamaño de fuente 2em. El elemento svg, que está dentro de ese contenedor, tiene también esas medidas. Así que el texto duplicará el tamaño de la fuente a la vez que el icono lo volverá a duplicar. Si el contenedor está en otro con fuente de 16 píxeles, el texto será de 32 y el icono será de 64×64 píxeles.

<style>
    #contiene-svg {
        font-size: 2em;
        color: blue;
    }
    #contiene-svg svg {
        width: 2em;
        height: 2em;
    }
</style>
<div id="contiene-svg">Texto 2em:
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" 
    fill="currentcolor">
        <path d="M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z"/>
        <path d="M0 0h24v24H0z" fill="none"/>
    </svg>
</div>
    

El contenedor tiene color azul que es heredado por el texto. Mientras que para el SVG hemos declarado fill="currentcolor" con lo que usa el color actual del elemento que es a su vez el que hereda del contenedor. Esta facilidad para cambiar el estilo de los iconos es una de las razones por la que debemos usarlos.

Insertando un archivo de iconos para un sitio web

La mayor parte de las veces los iconos van a documentar el aspecto visual de un elemento botón o un elemento vínculo. Muchos de estos elementos se replican una o más veces en una o más páginas del sitio. Así que no tendría sentido insertar directamente el XML del SVG dentro de cada uno de esos elementos. Sería preferible tener todos los iconos en un único SVG como un archivo independiente y luego usar un proceso de JavaScript para insertarlos en los elementos correspondientes.

De esa forma conseguimos unificar el estilo de todo el sitio a la vez que realizaríamos los cambios en el diseño de los iconos en un único lugar: el archivo de iconos. Además debemos preparar el botón o vínculo ante la eventualidad de que los iconos no estén disponibles. Veámos esto en ejecución con el siguiente ejemplo.

En primer lugar disponemos de un archivo de iconos, que a efectos de este ejemplo y para simplificar sólo contiene el de la impresora del ejemplo anterior. Le hemos agregado una línea roja al pie para verificar que estamos cargando este nuevo icono y no el del anterior ejemplo:

<svg xmlns="http://www.w3.org/2000/svg" class="dispnone">
    <symbol id="my-icon-print" viewBox="0 0 24 24">
        <path d="M19 8H5c-1.66 0-3 1.34-3 3v6h4v4h12v-4h4v-6c0-1.66-1.34-3-3-3zm-3 11H8v-5h8v5zm3-7c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm-1-9H6v4h12V3z"/>
        <path d="M0 22h24z" stroke="red" stroke-width="2" />
        <path d="M0 0h24v24H0z" fill="none"/>
    </symbol>
    <!-- Siguen otros SYMBOL con el resto de iconos -->
</svg>

El elemento <svg> contiene ahora un elemento <symbol> con la descripción de la forma gráfica, tres <path>, uno de ellos el que he agregado para formar una línea roja al pie del icono. El elemento <symbol> representa una plantilla para el icono de la impresora. Es un elemento que no se renderiza y que sólo sirve para guardar la descripción de un SVG. Esta identificado de forma única para toda la página con id="my-icon-print". Usando esa referencia podemos referirnos a esa forma con otro elemento <use> que veremos más abajo y que se encarga de extraer la plantilla y renderizarla.

Lo anterior vendría a ser el contenido del archivo de iconos, aunque para este ejemplo sólo contiene uno. Vea que le hemos puesto una clase class="dispnone" que aplicará display: none para impedir que el contenido sea renderizado, pues ese archivo lo vamos a insertar en el cuerpo de la página. Realmente no debería renderizarse nada si sólo contiene elementos <symbol>, pero si que se renderizarán los espacios en blanco entre esos elementos. En todo caso conviene ocultar ese contenido cuando lo insertemos en la página.

Ejemplo: Usando un archivo de iconos SVG

Lorem ipsum ad his scripta blandit partiendo, eum fastidii accumsan euripidis in, eum liber hendrerit an. Qui ut wisi vocibus suscipiantur, quo dicit ridens inciderint id. Quo mundi lobortis reformidans eu, legimus senserit definiebas an eos. Eu sit tincidunt incorrupte definitionem, vis mutat affert percipit cu, eirmod consectetuer signiferumque eu per. In usu latine equidem dolores. Quo no falli viris intellegam, ut fugit veritus placerat per.

Este ejemplo usa ES6 y modo estricto. Puedes consultar el código JS original de este ejemplo.

El HTML con el botón y el párrafo es el siguiente. Se observa que dentro del botón hay un elemento <span> con el texto Imprimir. Luego insertaremos el icono con la impresora en el botón y tendremos la posibilidad de ocultar o no ese título. En cualquier caso si por alguna razón fallará la inserción del SVG, aún tendriamos un botón con la leyenda Imprimir, lo que facilita la accesibilidad del componente.

<button type="button" id="imprimir-texto" title="Imprimir">
    <span id="icon-imprimir-texto">Imprimir</span>
</button>
<p id="texto-ejemplo">Lorem ipsum...</p>
    

El script para la inserción se lleva a cabo con la función llenarIconosEjemplo(svg). El argumento svg es el contenido de texto del archivo de iconos, texto que insertaremos dentro de un elemento <aside> al pie del documento. A continuación procederíamos a llenar todos los iconos de la página, aunque para este ejemplo sólo vamos a cargar uno. Creamos un nuevo elemento <span> donde insertamos un <svg> y luego un <use> que nos extraerá la plantilla identificada con #my-icon-print. El lugar de inserción será inmediatamente antes de aquel <span> con la leyenda Imprimir del botón. Ahora es el momento de ocultar o no esa leyenda, optando en este ejemplo por ocultarla.

function llenarIconosEjemplo(svg){
    //Elemento para meter todos los del archivo de iconos
    let aside = document.createElement("aside");
    aside.id = "source-iconos-ejemplo";
    aside.innerHTML = svg;
    document.body.appendChild(aside);
    //Inserta el icono PRINT
    let span = document.createElement("span");
    span.innerHTML = '<svg class="my-icon" fill="currentcolor">' +
                     '<use href="#my-icon-print"></use></svg>';
    let spanBoton = document.getElementById("icon-imprimir-texto");
    spanBoton.parentElement.insertBefore(span, spanBoton);
    //Oculta el texto del botón
    spanBoton.style.display = "none";
}

Llevaríamos a cabo la ejecución de la función anterior desde el window.onlad. Usamos otra función pedirRuta() que nos hace una petición XHR a la ruta del archivo de iconos. Cuando se reciba llamamos a llenarIconosEjemplo(). Vea de paso como adjudicamos un evento al botón para imprimir parte de una página.

window.onload = function() {
    pedirRuta("/como-se-hace/svg-iconos/ejemplos/iconos.html", 
        llenarIconosEjemplo);
    document.getElementById("imprimir-texto").
        addEventListener("click", imprimirTexto);
};
function pedirRuta(ruta, ejecutarOk, ejecutarErr){
    let req = new XMLHttpRequest();
    req.open("GET", ruta, true);
    req.onreadystatechange = function(){
        if (req.readyState == 4) {
            if (req.status == 200){
                let text = req.responseText;
                ejecutarOk(text);
            } else if (ejecutarErr) {
                ejecutarErr(req.status);
            }
        }
    };
    req.send();
}
function imprimirTexto(){
    wxG.imprimirTrozo(document.getElementById("texto-ejemplo"), "");
}

Tras la inserción el HTML resultante quedará como el siguiente:

<button type="button" id="imprimir-texto" title="Imprimir">
    <span>
        <svg class="my-icon" fill="currentcolor">
            <use href="#my-icon-print"></use>
        </svg>
    </span>
    <span id="icon-imprimir-texto" style="display: none;">Imprimir</span>
</button>
<p id="texto-ejemplo">Lorem ipsum... </p> 
    

Creando iconos SVG

La especificación Scalable Vector Graphics (SVG) explica todo lo necesario para crear elementos SVG. Sin que sea necesario convertirse en un experto en SVG, podemos aprender un par de cosas para crear iconos simples rápidamente. Este ejemplo muestra todos los iconos disponibles que vimos en el primer ejemplo de esta página. Se ubican en un rejilla de 24×24 en una superfice total de 480×480 píxeles.

Ejemplo: Creando iconos SVG

Este ejemplo usa ES6 y modo estricto. Puedes consultar el código JS original de este ejemplo.

El icono mostrado identificado con svgicon-nevnext es uno muy simple que he creado. Se trata de un triángulo que representa una flecha hacia la derecha. El único elemento que contiene es un path que representa un camino. Otras formas posibles serían line, polyline, polygon, rect, circle, ellipse. También es posible agregar texto con text o tspan. Hay otras cosas en SVG, pero vamos sólo a explicar el camino de ese icono de flecha usando el elemento path:

<path d="M2 2v20l15 -10z"></path>

El atributo "d" representa un conjunto de datos. Los comandos son letras mayúsculas o minúsculas a las que siguen uno o dos números positivos o negativos. Son "Mx y" para mover sin dibujar hasta el punto absoluto (x, y). Con minúscula "mx y" nos moveríamos relativamente (x, y) posiciones desde la posición actual. El comando "Lx y" y "lx y" dibuja una línea desde la posición actual hasta ese punto absoluto o relativo (x, y). Los comandos "Hx", "hx", "Vy" y "vy" dibujan líneas horizontales o verticales respectivamente. Hay otros comandos más complejos, pero con estos tenemos para empezar a modo de introducción.

Si C es un comando y (x, y) son las coordenadas podemos expresarlo como "Cx y" dejando uno o más espacios entre ambas coordenadas. Pero también es posible usar una coma para separarlas como "Cx,y". Quizás es preferible usar espacios, pues algo como "M3,1h16v9.5" puede confundirnos entre la coma de separación de coordenadas y el punto decimal en 9.5.

En nuestro ejemplo vemos que iniciamos el triángulo en el punto (2, 2) con "M2 2". Luego bajamos verticalmente una línea de 20 posiciones relativas con "v20", con lo que estamos en el punto (2, 2+20) = (2, 22). A continuación trazamos una línea con "l15 -10", es decir, una línea desde el punto anterior (2, 22) hasta el nuevo punto (2+15, 22-10) = (17, 12). La última línea desde ese punto al inicio no es necesario trazarla pues el comando "z" cerrará el camino. El atributo fill rellenará esa forma, un triángulo, con el color que deseemos. En el ejemplo le hemos puesto un color semitransparente a un contenedor exterior.

Se observa que trabajando en una rejilla de 24×24 no es díficil crear iconos SVG rectos sencillos como ése, para lo que no es ni siquiera necesario tener una herramienta de edición de SVG. De hecho los que empiezan a partir del icono de CSS inclusive los he creado a mano codificando con path, rect, ellipse o text. Si quieres puedes ver el código SVG de estos en el siguiente botón: