Saltar al contenido

Desenfoque, sombra y brillo: efectos completos

feGaussianBlur, feOffset, feFlood, feMerge y feDropShadow combinados en cinco efectos que se pegan y funcionan: sombra exterior, sombra interior, resplandor, sombra larga y borde suave.

feGaussianBlur y la desviación típica

feGaussianBlur toma stdDeviation, que es la desviación típica de la gaussiana, no un radio. Acepta un valor (isotrópico) o dos (stdDeviation="6 2" desenfoca más en horizontal).

La correspondencia con el «radio de desenfoque» que usan las herramientas de diseño no es uno a uno. Una gaussiana tiene soporte infinito, pero el 99,7 por ciento de su masa cae dentro de tres desviaciones típicas. En la práctica:

Texto
alcance visible ≈ 3 * stdDeviation

Un stdDeviation="4" se extiende unas 12 unidades. Ese es el número que hay que usar para dimensionar la región del filtro y para comparar con el blur-radius de una sombra de CSS, que se define de otra forma: en CSS, el radio de desenfoque equivale aproximadamente al doble de la desviación típica.

Texto
stdDeviation ≈ blur-radius de CSS / 2

Un box-shadow: 0 4px 12px se replica con stdDeviation="6", no con 12.

edgeMode controla qué pasa en el borde de la región: duplicate repite el píxel del borde, wrap envuelve, none (el valor por omisión) trata el exterior como transparente. Para un desenfoque sobre una imagen que llena su región, duplicate evita el halo transparente en los bordes.

Efecto 1: sombra exterior

feDropShadow es la abreviatura que hace el trabajo en una línea. Por dentro equivale a una cadena de cuatro primitivas: feGaussianBlur sobre SourceAlpha, feOffset, feFlood con flood-color, feComposite in, y un feMerge con el original encima.

HTML
<filter id="sombra" x="-40%" y="-40%" width="180%" height="180%"        color-interpolation-filters="sRGB">  <feDropShadow dx="0" dy="6" stdDeviation="8"                flood-color="#11111b" flood-opacity="0.45" /></filter>

Úsala siempre que puedas: es más corta, la implementación está optimizada, y es la única de este artículo que no necesita explicación.

Cuándo no basta: cuando quieres varias sombras apiladas, o cuando la sombra tiene que salir de otra cosa que no sea el alfa del elemento. Para varias sombras se encadenan varios feDropShadow, pero cuidado: el segundo toma como entrada el resultado del primero, así que la segunda sombra sombrea también a la primera. Para sombras independientes hay que hacerlo a mano:

HTML
<filter id="dos-sombras" x="-50%" y="-50%" width="200%" height="200%"        color-interpolation-filters="sRGB">  <feDropShadow in="SourceGraphic" dx="0" dy="2"  stdDeviation="2"                flood-color="#11111b" flood-opacity="0.35" result="s1" />  <feDropShadow in="SourceGraphic" dx="0" dy="12" stdDeviation="10"                flood-color="#11111b" flood-opacity="0.25" result="s2" />  <feMerge>    <feMergeNode in="s2" />    <feMergeNode in="s1" />    <feMergeNode in="SourceGraphic" />  </feMerge></filter>

Cada feDropShadow parte de SourceGraphic explícitamente, y el feMerge las apila en el orden correcto. Es la traducción exacta de un box-shadow con dos capas.

Efecto 2: sombra interior

No hay primitiva para esto y la construcción es instructiva. La idea: coger el alfa del elemento, desplazarlo y desenfocarlo, restarlo del alfa original para quedarse con el borde interior, teñirlo, y ponerlo encima.

HTML
<filter id="interior" x="-20%" y="-20%" width="140%" height="140%"        color-interpolation-filters="sRGB">  <!-- 1. Silueta desplazada y desenfocada -->  <feOffset in="SourceAlpha" dx="0" dy="4" result="desplazado" />  <feGaussianBlur in="desplazado" stdDeviation="4" result="difuso" />  <!-- 2. Lo que esta dentro de la silueta pero fuera del difuso -->  <feComposite in="SourceAlpha" in2="difuso" operator="out" result="borde" />  <!-- 3. Teñirlo -->  <feFlood flood-color="#11111b" flood-opacity="0.55" result="tinta" />  <feComposite in="tinta" in2="borde" operator="in" result="sombra" />  <!-- 4. Encima del original -->  <feComposite in="sombra" in2="SourceGraphic" operator="over" /></filter>

El paso clave es el operator="out": conserva la parte de SourceAlpha donde difuso no tiene alfa, lo que da exactamente la franja interior del borde superior. Si inviertes el signo de dy, la sombra pasa al borde inferior.

Nótese que el resultado ya está recortado a la silueta, así que el over final no necesita más recorte.

Efecto 3: resplandor de color

Un halo del color que elijas, alrededor de la forma, sin teñir el interior.

HTML
<filter id="resplandor" x="-60%" y="-60%" width="220%" height="220%"        color-interpolation-filters="sRGB">  <feGaussianBlur in="SourceAlpha" stdDeviation="7" result="difuso" />  <feFlood flood-color="#89b4fa" flood-opacity="1" result="color" />  <feComposite in="color" in2="difuso" operator="in" result="halo" />  <!-- Quitar del halo la parte que cae dentro de la silueta -->  <feComposite in="halo" in2="SourceAlpha" operator="out" result="halo-exterior" />  <feMerge>    <feMergeNode in="halo-exterior" />    <feMergeNode in="halo-exterior" />    <feMergeNode in="SourceGraphic" />  </feMerge></filter>

Dos detalles. El segundo feComposite con out es lo que hace que el halo no manche el interior de la forma, y es la diferencia entre un resplandor limpio y uno que apaga el color del objeto.

Y el feMergeNode repetido: apilar la misma capa dos veces intensifica el halo, porque la composición over de una capa semitransparente consigo misma aumenta su opacidad efectiva. Es la forma barata de subir la intensidad sin recalcular el desenfoque, y se puede repetir tres o cuatro veces.

Efecto 4: sombra larga

La sombra plana y alargada del estilo de diseño de hace unos años. Se construye apilando desplazamientos progresivos del alfa.

HTML
<filter id="larga" x="-10%" y="-10%" width="200%" height="200%"        color-interpolation-filters="sRGB">  <feOffset in="SourceAlpha" dx="2"  dy="2"  result="d1" />  <feOffset in="SourceAlpha" dx="4"  dy="4"  result="d2" />  <feOffset in="SourceAlpha" dx="6"  dy="6"  result="d3" />  <feOffset in="SourceAlpha" dx="8"  dy="8"  result="d4" />  <feOffset in="SourceAlpha" dx="10" dy="10" result="d5" />  <feMerge result="acumulado">    <feMergeNode in="d1" /><feMergeNode in="d2" /><feMergeNode in="d3" />    <feMergeNode in="d4" /><feMergeNode in="d5" />  </feMerge>  <feFlood flood-color="#11111b" flood-opacity="0.25" result="tinta" />  <feComposite in="tinta" in2="acumulado" operator="in" result="sombra" />  <feComposite in="sombra" in2="SourceGraphic" operator="over" /></filter>

Con cinco desplazamientos la sombra tiene escalones visibles en los bordes diagonales; con doce y un paso de 1 unidad queda continua. Es un ejemplo claro del compromiso entre calidad y número de primitivas, y del hecho de que un filtro con veinte primitivas es perfectamente legal y perfectamente caro.

Efecto 5: borde suave por dentro

Una viñeta que oscurece los bordes de una imagen sin tocar el centro. Se hace desenfocando el alfa hacia dentro con feMorphology, que se ve en la lección de ruido, o más simplemente con una máscara. Pero hay una variante puramente de filtro que merece conocerse: desenfocar el alfa y usarlo como alfa del original.

HTML
<filter id="difuminado" x="-20%" y="-20%" width="140%" height="140%"        color-interpolation-filters="sRGB">  <feGaussianBlur in="SourceAlpha" stdDeviation="10" result="alfa-suave" />  <feComposite in="SourceGraphic" in2="alfa-suave" operator="in" /></filter>

El resultado es el elemento original con los bordes desvanecidos, sin desenfocar su contenido. Es la diferencia entre difuminar el borde y difuminar la imagen, y es un efecto que la gente intenta con feGaussianBlur sobre SourceGraphic y sale mal.