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.
<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:
<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:
<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:
<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:
<!-- 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:
<!-- 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.