Saltar al contenido

El sistema de coordenadas de usuario y las unidades

Saber qué es exactamente un número sin unidad en SVG, qué unidades admiten los atributos, y contra qué se resuelve un porcentaje, que no es lo que parece.

La unidad de usuario

XML
<circle cx="50" cy="50" r="40"/>

Esos tres números no son píxeles: son unidades de usuario. Sin ninguna transformación, una unidad de usuario equivale a un píxel de CSS, que a su vez es una unidad abstracta que el navegador convierte en píxeles físicos según la densidad de la pantalla. Con un viewBox que escale, la equivalencia cambia por el factor correspondiente, y con transformaciones anidadas cambia otra vez.

De ahí sale la propiedad más útil del sistema: el dibujo se describe en las unidades que le convengan y el tamaño en pantalla se decide en otro sitio. Un mapa puede usar metros como unidad de usuario, un gráfico puede usar euros y días, y un icono puede usar una rejilla de 24. Ninguno tiene que saber a cuántos píxeles se mostrará.

La cadena completa que convierte un número del fichero en un píxel de la pantalla tiene cuatro eslabones y conviene tenerla presente porque cualquiera de ellos puede ser el que te sorprenda:

El número del atributo está en el sistema de coordenadas de su elemento. Las transformaciones de los grupos que lo contienen lo mueven y lo escalan. La transformación del viewBox lo lleva al viewport. La densidad de pantalla lo convierte en píxeles físicos.

Unidades en los atributos

Un atributo de longitud admite un número con una unidad detrás, y las unidades son las de CSS.

Unidad Equivalencia Cuándo tiene sentido
sin unidad Una unidad de usuario Casi siempre: es lo correcto por defecto
px Igual que sin unidad Nunca aporta nada frente a omitirla
pt pc cm mm in Ancladas a 96 píxeles por pulgada Documentos pensados para imprimir
em rem ex ch Relativas a la tipografía Elementos que deben crecer con el texto
% Relativa al viewport Con cuidado; ver la sección siguiente

Dos advertencias que ahorran depuraciones.

Las unidades físicas mienten en pantalla. 1cm no mide un centímetro en tu monitor: mide 96 / 2.54 píxeles de CSS, es decir, unos 37,8. El navegador no sabe el tamaño físico de la pantalla ni la distancia a la que estás. Solo al imprimir se convierten en medidas reales, y por eso son la unidad adecuada para un SVG que va a papel y una fuente de confusión en cualquier otro caso.

Poner unidades dentro del dibujo rompe la abstracción del viewBox. Si mezclas r="40" con stroke-width="2px" no ganas nada, y si mezclas r="40" con stroke-width="2mm" obtienes un grosor que ya no escala igual que el resto y que se comporta de forma distinta al cambiar el tamaño. Dentro del dibujo, números sin unidad; las unidades van en el tamaño del elemento raíz, que es donde el SVG se encuentra con la página.

SVG 2 define además varias de esas geometrías —x, y, r, cx, cy, width, height— también como propiedades de CSS, lo que permitiría animarlas con transiciones. El soporte no es uniforme entre motores, así que como atributos siguen siendo la vía segura, y para animar geometría lo portátil es transformar en lugar de cambiar la geometría.

Los porcentajes y la diagonal normalizada

Un porcentaje en SVG no se resuelve contra el elemento padre, como en CSS, sino contra el viewport actual. Y hay tres reglas según el eje al que pertenezca la longitud:

Longitudes horizontales —x, cx, width, rx— se resuelven contra el ancho del viewport.

Longitudes verticales —y, cy, height, ry— contra el alto del viewport.

Longitudes que no son de ningún eje —r de un círculo, stroke-width, stroke-dashoffset, el radio de un gradiente radial— se resuelven contra un valor que la especificación llama diagonal normalizada:

JS
const diagonalNormalizada = Math.sqrt(ancho * ancho + alto * alto) / Math.SQRT2;

Con un viewport de 800 por 600, la diagonal es 1000 y la diagonal normalizada es 1000 / 1,4142, es decir, unas 707 unidades. Por tanto r="50%" en ese viewport da un radio de 353,5 unidades, no de 400 ni de 300. En un viewport cuadrado la fórmula devuelve exactamente el lado, que es el caso en el que la regla pasa desapercibida; en cuanto el viewport deja de ser cuadrado, aparece un número que nadie espera.

XML
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 600" width="400" height="300">  <rect width="100%" height="100%" fill="#313244"/>  <circle cx="50%" cy="50%" r="50%" fill="#89b4fa" opacity="0.5"/>  <!-- El circulo NO toca los bordes verticales: su radio es 353,5, no 300 --></svg>

La conclusión práctica es breve: los porcentajes están bien para width y height del elemento raíz y para rectángulos de fondo, y son una mala idea para radios y grosores. Para todo lo que sea geometría del dibujo, números en unidades de usuario, que ya escalan solos.

La regla práctica

Toda la lección se resume en un reparto de responsabilidades que conviene adoptar sin excepciones porque hace que todo lo demás encaje.

Dentro del dibujo: números sin unidad. Todas las coordenadas, radios y grosores en unidades de usuario, con el viewBox decidiendo la escala.

En el elemento raíz: tamaño desde el CSS. Ni width ni height en el marcado si el SVG está en línea y debe adaptarse; el CSS decide, y con width: 100% y height: auto el SVG mantiene su proporción.

Nada de porcentajes en la geometría interna. Salvo el rectángulo de fondo, donde width="100%" height="100%" es idiomático y correcto.

Unidades físicas solo en documentos para imprimir, y entonces en el elemento raíz: width="210mm" height="297mm" con un viewBox que use las unidades cómodas para dibujar.

XML
<svg xmlns="http://www.w3.org/2000/svg"     width="210mm" height="297mm" viewBox="0 0 210 297">  <!-- Una unidad de usuario es un milimetro: comodisimo para maquetar en papel -->  <rect x="20" y="20" width="170" height="40" fill="none" stroke="#000" stroke-width="0.5"/>  <text x="24" y="45" font-size="8" font-family="serif">Un titulo de 8 mm</text></svg>

Ese es el único caso en el que las unidades físicas son la respuesta correcta, y funciona muy bien: eliges el viewBox para que una unidad de usuario sea un milímetro, y a partir de ahí todo el dibujo se piensa en milímetros sin escribir la unidad ni una sola vez.