Saltar al contenido

line, polyline y polygon: los vértices

La sintaxis tolerante del atributo points, por qué una polyline se rellena de negro sin que se lo pidas, el cierre implícito del polígono, y el papel de los marcadores.

line: la forma que solo existe con trazo

line toma cuatro números: x1, y1, x2, y2. Y tiene una particularidad que no comparte con ninguna otra forma: nunca se rellena. Un segmento no encierra área, así que fill no tiene nada que pintar. Si escribes un line sin stroke, el elemento existe y no se ve.

HTML
<svg viewBox="0 0 200 60" width="400">  <line x1="10" y1="30" x2="190" y2="30" stroke="#89b4fa" stroke-width="2" /></svg>

Esa propiedad tiene una consecuencia que sale de aquí y va a parar al nivel de gradientes: la caja envolvente de un line horizontal tiene altura cero. Y una caja envolvente con una dimensión nula hace que cualquier gradiente o patrón declarado en unidades de caja no se renderice en absoluto. Es la causa más común del gradiente invisible, y se trata a fondo en gradientUnits y los dos sistemas de coordenadas.

Para líneas de rejilla en un gráfico, line es exactamente la herramienta. Para una línea de datos con muchos puntos, no: serían n elementos donde uno bastaría.

points y su parser tolerante

polyline y polygon comparten un único atributo geométrico: points, una lista de pares de coordenadas. La gramática es más permisiva de lo que la mayoría imagina. Son válidos y equivalentes:

HTML
<polyline points="0,0 10,20 20,0" /><polyline points="0 0 10 20 20 0" /><polyline points="0,0,10,20,20,0" /><polyline points="0 , 0   10,20                  20,0" />

Coma, espacio, tabulador y salto de línea son intercambiables como separadores, y una coma puede llevar espacios a los lados. Esa tolerancia es deliberada, porque estos atributos se generan a máquina y nadie quería obligar a un formato exacto. El precio es que un error de un solo número no se nota: si te comes una coordenada, la lista sigue siendo válida y todos los puntos posteriores se desplazan una posición, así que la forma sale retorcida en lugar de no salir.

Qué pasa con una lista de longitud impar depende de la versión: SVG 1.1 declaraba el atributo entero en error y no se renderizaba nada; SVG 2 dice que se descarte el número sobrante y se dibuje el resto. Los motores actuales siguen el segundo comportamiento. Dicho de otra manera: no confíes en que un error de paridad te avise. Si generas points desde datos, valida la longitud tú.

JS
function puntos(pares) {  if (pares.length % 2 !== 0) throw new Error('lista de coordenadas impar');  return pares.join(' ');}

El relleno que nadie pidió

Aquí está la trampa principal del nivel. polyline significa «línea múltiple», pero se rellena por omisión, porque fill es una propiedad heredada cuyo valor inicial es negro y polyline no la exceptúa. Dibujar una serie temporal con polyline sin más produce una mancha negra con la forma del área bajo la curva, cerrada por una recta imaginaria del último punto al primero.

HTML
<svg viewBox="0 0 200 80" width="400">  <!-- Mal: mancha negra -->  <polyline points="10,70 50,30 90,50 130,20 170,45" stroke="#f38ba8" stroke-width="2" />  <!-- Bien -->  <polyline points="10,70 50,30 90,50 130,20 170,45"            fill="none" stroke="#a6e3a1" stroke-width="2"            stroke-linejoin="round" stroke-linecap="round" /></svg>

fill="none" no es una decoración: es obligatorio en toda polyline que represente una línea. La misma regla vale para cualquier path abierto, y es la primera cosa que hay que comprobar cuando una curva sale como un borrón.

polygon sí encierra área por definición, así que su relleno tiene sentido. Su diferencia con polyline es exactamente una: cierra la ruta. El último punto se une al primero con un segmento recto, y ese cierre es un cierre de verdad, no un segmento más.

Marcadores en los vértices

Los tres elementos de este nivel comparten una capacidad que las formas curvas también tienen pero que aquí luce especialmente: los marcadores. Un marker es un trozo de SVG que se instancia en los vértices de una forma, y se conecta con tres propiedades: marker-start, marker-mid y marker-end.

HTML
<svg viewBox="0 0 200 90" width="400">  <defs>    <marker id="punto" viewBox="0 0 10 10" refX="5" refY="5"            markerWidth="5" markerHeight="5">      <circle cx="5" cy="5" r="4" fill="#f9e2af" />    </marker>    <marker id="flecha" viewBox="0 0 10 10" refX="9" refY="5"            markerWidth="6" markerHeight="6" orient="auto-start-reverse">      <path d="M 0 0 L 10 5 L 0 10 z" fill="#f38ba8" />    </marker>  </defs>  <polyline points="15,70 55,30 95,55 135,20 180,40"            fill="none" stroke="#89b4fa" stroke-width="2"            marker-mid="url(#punto)" marker-end="url(#flecha)" /></svg>

Tres detalles que importan. refX y refY dicen qué punto del marcador se alinea con el vértice, así que son las coordenadas del «agujero de la chincheta». markerWidth y markerHeight son el tamaño en unidades de usuario, y por omisión escalan con stroke-width porque markerUnits vale strokeWidth; si quieres un tamaño fijo pon markerUnits="userSpaceOnUse". Y orient="auto" rota el marcador según la tangente; auto-start-reverse hace lo mismo pero invierte el de inicio, que es lo que quieres para una flecha doble.

Los marcadores son la forma correcta de poner los puntos de una serie temporal: un solo elemento polyline con un marker-mid produce un punto en cada vértice, en lugar de n elementos circle. Con quinientos puntos, la diferencia en número de nodos del DOM es la diferencia entre un gráfico fluido y uno que se arrastra.

Un aviso: marker-mid no se aplica al primero ni al último vértice. Si quieres puntos en todos, declara los tres:

HTML
<polyline points="..." fill="none" stroke="#89b4fa"          marker-start="url(#punto)"          marker-mid="url(#punto)"          marker-end="url(#punto)" />