Saltar al contenido

linearGradient y las paradas de color

La geometría del vector de gradiente, cómo se ordenan y se corrigen las paradas, la herencia entre gradientes con href, y por qué transparent no siempre hace lo que esperas.

El vector

linearGradient define un vector con cuatro coordenadas. El color varía a lo largo de la proyección de cada punto sobre ese vector: perpendicular al vector, el color es constante.

HTML
<svg viewBox="0 0 200 100" width="400">  <defs>    <linearGradient id="horizontal">      <stop offset="0"   stop-color="#89b4fa" />      <stop offset="1"   stop-color="#f38ba8" />    </linearGradient>    <linearGradient id="diagonal" x1="0" y1="0" x2="1" y2="1">      <stop offset="0"   stop-color="#a6e3a1" />      <stop offset="1"   stop-color="#cba6f7" />    </linearGradient>  </defs>  <rect x="5"   y="5" width="90" height="90" fill="url(#horizontal)" />  <rect x="105" y="5" width="90" height="90" fill="url(#diagonal)" /></svg>

Los valores por omisión son x1="0%", y1="0%", x2="100%", y2="0%": un vector horizontal de izquierda a derecha. Eso explica por qué un linearGradient sin coordenadas siempre sale horizontal.

Para un gradiente vertical, el vector es x1="0" y1="0" x2="0" y2="1". Para uno a 45 grados, x1="0" y1="0" x2="1" y2="1" si el elemento es cuadrado; si no lo es, el ángulo aparente se deforma, y esa deformación es una de las dos consecuencias del sistema de coordenadas que se explica en gradientUnits y los dos sistemas.

gradientTransform aplica una transformación al espacio del gradiente, y es la forma limpia de rotar sin recalcular coordenadas:

HTML
<linearGradient id="g45" gradientTransform="rotate(45 0.5 0.5)">  <stop offset="0" stop-color="#94e2d5" />  <stop offset="1" stop-color="#fab387" /></linearGradient>

La rotación se hace alrededor del centro del espacio unitario, de ahí el 0.5 0.5.

Las paradas

Cada stop tiene offset (0 a 1, o un porcentaje), stop-color y stop-opacity. Los tres son propiedades de presentación, así que se pueden escribir desde CSS, y eso permite tematizar un gradiente sin tocar el marcado.

Tres reglas de la especificación que conviene saber de memoria:

Uno: los offset se recortan al rango 0 a 1. Un offset="1.5" vale 1.

Dos: si una parada tiene un offset menor que la anterior, se corrige al valor de la anterior. Es decir, la lista se fuerza a ser no decreciente. Esto no es un error que puedas cometer sin consecuencias, es un mecanismo que se usa a propósito.

Tres: dos paradas con el mismo offset producen una transición dura. Sale de la regla anterior y es la forma de hacer bandas de color sólidas:

HTML
<linearGradient id="bandas">  <stop offset="0"    stop-color="#a6e3a1" />  <stop offset="0.33" stop-color="#a6e3a1" />  <stop offset="0.33" stop-color="#f9e2af" />  <stop offset="0.66" stop-color="#f9e2af" />  <stop offset="0.66" stop-color="#f38ba8" />  <stop offset="1"    stop-color="#f38ba8" /></linearGradient>

Tres franjas nítidas con un solo elemento de relleno. Es la técnica para una barra de umbrales en un gráfico, o para un indicador de progreso por tramos.

Sin ninguna parada, el elemento se pinta como si tuviera fill="none". Con una sola parada, se pinta de ese color sólido. Son casos límite útiles: un gradiente generado por código puede colapsar a color plano sin ramificar.

Herencia entre gradientes con href

Un gradiente puede heredar las paradas de otro. Es la manera de tener una paleta única y varias orientaciones sin duplicar la lista:

HTML
<defs>  <linearGradient id="paleta">    <stop offset="0"   stop-color="#89b4fa" />    <stop offset="0.5" stop-color="#cba6f7" />    <stop offset="1"   stop-color="#f38ba8" />  </linearGradient>  <linearGradient id="vertical" href="#paleta" x1="0" y1="0" x2="0" y2="1" />  <linearGradient id="diagonal" href="#paleta" x1="0" y1="0" x2="1" y2="1" /></defs>

La herencia alcanza a las paradas y también a los atributos que el gradiente hijo no declare: gradientUnits, spreadMethod, gradientTransform. Los que sí declara ganan.

En SVG 1.1 esto se escribía xlink:href y hacía falta declarar el espacio de nombres. SVG 2 lo simplificó a href y los motores actuales lo aceptan. Si mantienes un fichero antiguo verás la forma con prefijo, y funciona igual; en fichero nuevo no hay razón para usarla.

Un gradiente radial puede heredar las paradas de uno lineal y viceversa: lo que se hereda es la lista de colores, no la geometría.

El bug de la zona gris

Un degradado de un color a transparente parece trivial:

HTML
<!-- Mal: pasa por gris --><linearGradient id="mal">  <stop offset="0" stop-color="#89b4fa" />  <stop offset="1" stop-color="transparent" /></linearGradient>

Y produce un degradado que en la zona intermedia se ensucia hacia el gris. La razón: transparent es negro con alfa cero. La interpolación se hace componente a componente, así que en el punto medio el color es el promedio entre azul y negro, con alfa 0,5. El resultado es un azul apagado, casi gris.

La solución es declarar la parada final con el mismo color y opacidad cero:

HTML
<!-- Bien: el matiz se mantiene --><linearGradient id="bien">  <stop offset="0" stop-color="#89b4fa" stop-opacity="1" />  <stop offset="1" stop-color="#89b4fa" stop-opacity="0" /></linearGradient>

Ahora los dos extremos tienen el mismo componente de color y solo varía el alfa, así que el matiz es constante en todo el recorrido.

Este es exactamente el mismo problema que en CSS produce la «franja gris» de un linear-gradient(blue, transparent), y que en CSS moderno se puede evitar con transparent porque el algoritmo cambió para interpolar en espacio premultiplicado. En SVG no ha cambiado: la interpolación de paradas sigue haciéndose sin premultiplicar, así que la regla del stop-opacity sigue siendo obligatoria.