Los modos de fusión
Usar los dieciséis modos de mezcla separables y no separables que el canvas comparte con CSS y con los programas de diseño, y saber qué hace cada familia.
Las cuatro familias
Los que oscurecen: multiply, darken, color-burn. El resultado nunca es más claro que ninguna de las dos capas. El blanco es neutro: mezclar con blanco no cambia nada.
Los que aclaran: screen, lighten, color-dodge. El resultado nunca es más oscuro. El negro es neutro.
Los de contraste: overlay, hard-light, soft-light. Combinan los dos anteriores según el valor del píxel base: oscurecen las zonas oscuras y aclaran las claras. El gris medio es neutro.
Los de comparación y componentes: difference, exclusion, hue, saturation, color, luminosity.
Los doce primeros son separables: operan canal a canal, de forma independiente. Los cuatro últimos son no separables: descomponen el color en tono, saturación y luminosidad, y toman unos componentes de una capa y otros de la otra.
Los que se usan
multiply multiplica los valores normalizados. El resultado es como superponer dos transparencias. Es el modo de las sombras y del papel: cualquier zona blanca desaparece y las oscuras se refuerzan.
// Sombra proyectada realista sobre un fondo con texturactx.globalCompositeOperation = 'multiply';ctx.fillStyle = 'rgba(0,0,0,0.35)';ctx.beginPath();ctx.ellipse(cx, cy + 60, 70, 18, 0, 0, Math.PI * 2);ctx.fill();ctx.globalCompositeOperation = 'source-over';Frente a dibujar la sombra con source-over y alfa, multiply conserva la textura del fondo debajo, que es lo que hace que una sombra se vea integrada y no pegada.
screen es el inverso: multiplica los complementarios. Es el modo de la luz añadida y de los reflejos, y a diferencia de lighter no satura tan rápido, con lo que da un resultado más suave.
overlay y soft-light son los modos de gradación de color. Aplicar una capa de color con soft-light sobre una fotografía es la forma estándar de virar una imagen sin destruir su rango tonal.
// Virado calido sobre una fotoctx.drawImage(foto, 0, 0);ctx.globalCompositeOperation = 'soft-light';const g = ctx.createLinearGradient(0, 0, 0, alto);g.addColorStop(0, '#f9e2af');g.addColorStop(1, '#89b4fa');ctx.fillStyle = g;ctx.fillRect(0, 0, ancho, alto);ctx.globalCompositeOperation = 'source-over';difference da el valor absoluto de la diferencia entre ambos. Su uso más interesante no es estético sino de diagnóstico: comparar dos imágenes. Las zonas idénticas quedan negras y las distintas se iluminan.
// Diff visual entre dos renderizadosctx.drawImage(referencia, 0, 0);ctx.globalCompositeOperation = 'difference';ctx.drawImage(actual, 0, 0);ctx.globalCompositeOperation = 'source-over';// Todo lo que no sea negro es una diferenciaEs la técnica que está detrás de las herramientas de regresión visual, y en canvas cuesta tres líneas.
luminosity y color son la pareja no separable más útil. luminosity toma la luminosidad del origen y el tono y la saturación del destino; color hace lo contrario. Juntos permiten recolorear una imagen conservando su modelado de luz:
// Recolorear conservando el detalle de luzctx.drawImage(objeto, 0, 0);ctx.globalCompositeOperation = 'color';ctx.fillStyle = '#cba6f7';ctx.fillRect(0, 0, ancho, alto);ctx.globalCompositeOperation = 'source-over';Tabla de referencia
| Modo | Familia | Neutro | Uso típico |
|---|---|---|---|
multiply |
Oscurece | Blanco | Sombras, tinta, papel |
darken |
Oscurece | Blanco | Quedarse con el más oscuro por canal |
color-burn |
Oscurece | Blanco | Oscurecer con más contraste |
screen |
Aclara | Negro | Luces, reflejos, brillos |
lighten |
Aclara | Negro | Quedarse con el más claro por canal |
color-dodge |
Aclara | Negro | Destellos intensos |
overlay |
Contraste | Gris medio | Aumentar contraste general |
hard-light |
Contraste | Gris medio | Como overlay con las capas invertidas |
soft-light |
Contraste | Gris medio | Virado suave, gradación de color |
difference |
Comparación | Negro | Comparar imágenes, inversión |
exclusion |
Comparación | Negro | Como difference con menos contraste |
hue |
Componentes | — | Cambiar el tono conservando el resto |
saturation |
Componentes | — | Ajustar saturación desde una capa |
color |
Componentes | — | Recolorear conservando la luz |
luminosity |
Componentes | — | Aplicar el modelado de luz de otra capa |
Combinar modos y capas
Los efectos interesantes casi nunca son un modo suelto: son varias capas con modos distintos. Y como cada modo afecta a todo el canvas, la composición por capas es obligatoria.
/** Devuelve un canvas con la foto virada y con un brillo superpuesto. */function componer(foto, ancho, alto) { const base = document.createElement('canvas'); base.width = ancho; base.height = alto; const c = base.getContext('2d'); // Capa 1: la foto c.drawImage(foto, 0, 0, ancho, alto); // Capa 2: virado con soft-light c.globalCompositeOperation = 'soft-light'; const virado = c.createLinearGradient(0, 0, ancho, alto); virado.addColorStop(0, '#f9e2af'); virado.addColorStop(1, '#585b70'); c.fillStyle = virado; c.fillRect(0, 0, ancho, alto); // Capa 3: vinetado con multiply c.globalCompositeOperation = 'multiply'; const vineta = c.createRadialGradient( ancho / 2, alto / 2, Math.min(ancho, alto) * 0.3, ancho / 2, alto / 2, Math.max(ancho, alto) * 0.75); vineta.addColorStop(0, '#ffffff'); vineta.addColorStop(1, '#6c6f85'); c.fillStyle = vineta; c.fillRect(0, 0, ancho, alto); // Capa 4: destello con screen c.globalCompositeOperation = 'screen'; const destello = c.createRadialGradient( ancho * 0.25, alto * 0.2, 0, ancho * 0.25, alto * 0.2, ancho * 0.5); destello.addColorStop(0, 'rgba(249,226,175,0.5)'); destello.addColorStop(1, 'rgba(249,226,175,0)'); c.fillStyle = destello; c.fillRect(0, 0, ancho, alto); c.globalCompositeOperation = 'source-over'; return base;}Ese es el aspecto real de un filtro fotográfico: cuatro capas, cuatro modos, cero manipulación de píxeles. Todo lo hace el compositor, que además puede acelerarlo por hardware, y el coste es de cuatro rellenos en lugar de millones de iteraciones en JavaScript.