Web 图形合成技术:Blending 与 Porter-Duff Compositing

Web 图形渲染中,当多个图层重叠时,需要将它们组合成最终显示的图像。本文基于 W3C Compositing and Blending 规范,系统讲解 Blending 和 Porter-Duff Compositing 两大核心技术。从数学原理到实际应用,深入解析 Web 图形合成的完整体系。

符号与公式约定

理解图形合成的数学公式需要先明确符号约定。本章定义文中使用的核心术语和数学符号,包括颜色表示方式、Alpha 通道处理以及合成过程中的中间结果。

核心术语

Source 是当前要绘制的图层,通常是前景元素。它包含颜色信息和 Alpha 通道。

Backdrop 是 Source 后面的图层内容,是 Source 要与之合成的对象。Backdrop 是之前所有图层合成的累积结果。

Destination 是渲染目标,即画布上的像素缓冲区。合成的最终结果会写入 Destination。

Alpha 通道表示像素的不透明度,取值范围为 [0, 1]。Alpha 为 0 表示完全透明,Alpha 为 1 表示完全不透明。

预乘 Alpha 与非预乘 Alpha

颜色可以用两种形式表示:

非预乘 Alpha (Non-premultiplied Alpha) 表示颜色分量和 Alpha 通道独立存储。例如,半透明红色表示为 (R=1.0, G=0, B=0, α=0.5),颜色值不受 Alpha 影响。

预乘 Alpha (Premultiplied Alpha) 表示颜色分量已乘以 Alpha 值。同样的半透明红色表示为 (r=0.5, g=0, b=0, α=0.5),其中 r = R × α。预乘形式在图形硬件中更高效,简化了合成运算。

两种形式之间的转换:

javascript 复制代码
// 非预乘转预乘
const cs = Cs * αs;
const cb = Cb * αb;

// 预乘转非预乘(需处理 α = 0 的情况)
const Cs = αs > 0 ? cs / αs : 0;
const Cb = αb > 0 ? cb / αb : 0;

符号定义

本文使用以下符号约定:

符号 含义 取值范围
Cs Source 的非预乘颜色 (R, G, B) [0, 1]
Cb Backdrop 的非预乘颜色 (R, G, B) [0, 1]
Cr Blending 后的结果颜色 (R, G, B) [0, 1]
Co 最终输出的非预乘颜色 (R, G, B) [0, 1]
cs Source 的预乘颜色 (r, g, b) [0, 1]
cb Backdrop 的预乘颜色 (r, g, b) [0, 1]
cr Blending 后的预乘颜色 (r, g, b) [0, 1]
co 最终输出的预乘颜色 (r, g, b) [0, 1]
αs Source 的 Alpha 值 [0, 1]
αb Backdrop 的 Alpha 值 [0, 1]
αo 输出的 Alpha 值 [0, 1]
Fa Source 的 Porter-Duff 因子 [0, 1]
Fb Backdrop 的 Porter-Duff 因子 [0, 1]

关键符号说明:

  • Cr (Result color) :Blending 阶段计算出的中间结果颜色。它是 Backdrop 颜色 Cb 和 Source 颜色 Cs 通过 Blending 函数混合后的结果,会在 Porter-Duff Compositing 阶段作为新的 Source 颜色使用。
  • Fa 和 Fb:Porter-Duff Compositing 的控制因子,决定 Source 和 Backdrop 对最终结果的贡献程度。

颜色值裁剪

合成计算过程中,颜色值可能超出 [0, 1] 范围。需要进行裁剪 (clamp) 处理:

javascript 复制代码
// 裁剪到有效范围
const clamp = (value) => Math.max(0, Math.min(1, value));

Co = clamp(Co);
αo = clamp(αo);

某些 Blending 模式(如 color-dodge)可能产生大于 1 的值,裁剪确保最终输出的颜色在显示设备的有效范围内。

合成模型

图形合成是一个严格的两阶段流程:先进行 Blending 计算重叠区域的颜色混合,再使用 Alpha 合成公式处理 Alpha 通道的影响。本章介绍通用合成公式及其特例 Simple Alpha Compositing。

通用合成公式

完整的合成流程严格按照以下顺序执行:

ini 复制代码
输入: Source (Cs, αs), Backdrop (Cb, αb)
     ↓
[1. Blending]
     Cr = (1 - αb) × Cs + αb × B(Cb, Cs)
     ↓
[2. Alpha Compositing]
     co = Fa × (Cr × αs) + Fb × (Cb × αb)
     αo = Fa × αs + Fb × αb
     ↓
输出: Result (Co, αo)

第 1 步:Blending

计算混合后的颜色 Cr,对每个颜色通道(R/G/B)应用 Blending 函数 B(Cb, Cs)

javascript 复制代码
// 非预乘形式
Cr = (1 - αb) × Cs + αb × B(Cb, Cs);

B(Cb, Cs) 定义了 Backdrop 和 Source 颜色的混合方式。不同的 Blending 模式对应不同的 B 函数实现:

  • normalB(Cb, Cs) = Cs(无混合,保持 Source 原色)
  • multiplyB(Cb, Cs) = Cb × Cs(正片叠底)
  • screenB(Cb, Cs) = Cb + Cs - Cb × Cs(滤色)

αb 控制 Blending 的影响程度:当 Backdrop 透明(αb=0)时保持 Source 原色,当 Backdrop 不透明(αb=1)时完全混合。

第 2 步:Alpha Compositing

Cr 作为新的 Source 颜色,使用因子 FaFb 控制 Source 和 Backdrop 的贡献程度:

javascript 复制代码
// 预乘形式
co = Fa × (Cr × αs) + Fb × (Cb × αb);
αo = Fa × αs + Fb × αb;

FaFb 是合成因子,通过不同的取值实现不同的合成效果。例如:

  • Fa = 1, Fb = 1 - αs:Source 覆盖 Backdrop(source-over)
  • Fa = αb, Fb = 0:Source 裁剪到 Backdrop 形状(source-in)
  • Fa = 1 - αb, Fb = 1:Backdrop 覆盖 Source(destination-over)

合并公式

将两个步骤合并,得到完整的合成公式:

javascript 复制代码
// 完整的合成公式(预乘形式)
co = Fa × B(Cb, Cs) × αs × αb + Fa × (1 - αb) × cs + Fb × cb;
αo = Fa × αs + Fb × αb;

这个公式清楚地展示了三部分的贡献:

  • Fa × B(Cb, Cs) × αs × αb:Blending 结果对重叠区域的贡献
  • Fa × (1 - αb) × cs:Source 在 Backdrop 透明区域的贡献
  • Fb × cb:Backdrop 根据合成因子的贡献

Simple Alpha Compositing

Simple Alpha Compositing 是通用公式的最常用特例,对应参数:

  • Blending 模式:normal,即 B(Cb, Cs) = Cs
  • 合成因子:Fa = 1, Fb = 1 - αs

代入通用公式得到:

javascript 复制代码
// 预乘形式
co = cs + cb × (1 - αs);
αo = αs + αb × (1 - αs);

这就是默认的图层叠加方式(source-over):Source 以其不透明度覆盖在 Backdrop 上方。当 Source 完全不透明(αs = 1)时,Backdrop 完全被遮盖;当 Source 完全透明(αs = 0)时,输出就是 Backdrop。

关键要点:

  • Blending 在前,Alpha Compositing 在后,顺序不可颠倒
  • Blending 通过函数 B 计算颜色,Alpha Compositing 通过因子 FaFb 控制区域和 Alpha 通道
  • Simple Alpha Compositing 是 B=Cs, Fa=1, Fb=1-αs 的特例
  • 两个阶段通过中间结果 Cr 连接

Porter-Duff Compositing Operators

Porter-Duff Compositing Operators 由 Thomas Porter 和 Tom Duff 在 1984 年提出,定义了通过 Alpha 通道控制图层合成区域的代数运算体系。本章介绍 Porter-Duff 操作符的数学原理、13 种标准操作符以及它们与 Compositing Groups 的交互行为。

操作符原理

Porter-Duff 操作符基于 Alpha 通道的线性合成。核心思想是通过两个因子 FaFb 控制 Source 和 Backdrop 对最终结果的贡献程度:

javascript 复制代码
co = Fa × cs + Fb × cb;
αo = Fa × αs + Fb × αb;

Porter-Duff 理论将两张图像的像素空间分为四个逻辑区域:

  1. Source 独占区域:Source 不透明,Backdrop 透明(αs > 0, αb = 0)
  2. Backdrop 独占区域:Backdrop 不透明,Source 透明(αs = 0, αb > 0)
  3. 重叠区域:两者都不透明(αs > 0, αb > 0)
  4. 空白区域:两者都透明(αs = 0, αb = 0)

为了精确控制这四个区域,FaFb 的取值被限制在与 Alpha 值相关的离散集合中:

css 复制代码
Fa ∈ {0, αb, 1 - αb, 1}
Fb ∈ {0, αs, 1 - αs, 1}

这种限制确保了操作符具有明确的几何意义:

  • Fa = 0:Source 不贡献
  • Fa = 1:Source 完全贡献
  • Fa = αb:Source 仅在 Backdrop 存在的区域贡献
  • Fa = 1 - αb:Source 仅在 Backdrop 不存在的区域贡献

Fb 的含义与 Fa 对称。

13 种 Porter-Duff 操作符

从数学上看,FaFb 的取值集合可以产生 4 × 4 = 16 种组合。W3C 规范定义了其中 13 种具有实际意义的操作符:

操作符 Fa Fb 效果描述
clear 0 0 清除所有内容
copy 1 0 仅显示 Source
destination 0 1 仅显示 Backdrop
source-over 1 1 - αs Source 覆盖 Backdrop(默认)
destination-over 1 - αb 1 Backdrop 覆盖 Source
source-in αb 0 Source 裁剪到 Backdrop 形状
destination-in 0 αs Backdrop 裁剪到 Source 形状
source-out 1 - αb 0 Source 在 Backdrop 外的部分
destination-out 0 1 - αs Backdrop 在 Source 外的部分
source-atop αb 1 - αs Source 在 Backdrop 上方(限定在 Backdrop 区域)
destination-atop 1 - αb αs Backdrop 在 Source 上方(限定在 Source 区域)
xor 1 - αb 1 - αs 仅显示非重叠区域
lighter 1 1 相加(需要 clamp)

未定义的 3 种组合(Fa=αb, Fb=αsFa=αb, Fb=1Fa=1, Fb=αs)缺乏明确的语义或实际应用价值,因此被排除在规范之外。

W3C 选择这 13 种操作符的原则是:

  1. 完备性:覆盖所有常见的图像合成需求(覆盖、裁剪、遮罩、异或等)
  2. 对称性 :提供 Source 和 Destination 的对称操作(如 source-in 对应 destination-in
  3. 语义清晰:每个操作符都有明确的视觉效果和应用场景

常见应用场景

遮罩效果 :使用 source-indestination-in

javascript 复制代码
const canvas = document.getElementById("myCanvas");
const ctx = canvas.getContext("2d");

// 绘制遮罩形状(圆形)
ctx.arc(100, 100, 50, 0, Math.PI * 2);
ctx.fill();

// 使用 source-in 将图像裁剪到圆形
ctx.globalCompositeOperation = "source-in";
ctx.drawImage(image, 0, 0);

擦除效果 :使用 destination-out

javascript 复制代码
// 绘制底图
ctx.drawImage(image, 0, 0);

// 使用 destination-out 擦除
ctx.globalCompositeOperation = "destination-out";
ctx.arc(100, 100, 30, 0, Math.PI * 2);
ctx.fill(); // 擦除圆形区域

发光叠加 :使用 lighter

javascript 复制代码
// 绘制第一个光源
ctx.drawImage(light1, 0, 0);

// 使用 lighter 叠加第二个光源
ctx.globalCompositeOperation = "lighter";
ctx.drawImage(light2, 50, 50);

Blending 混合模式

Blending 控制重叠区域的颜色如何混合。与 Porter-Duff Compositing Operators 通过 FaFb 控制区域选择不同,Blending 通过函数 B(Cb, Cs) 改变颜色的计算方式。本章介绍 Blending 函数、与 Alpha Compositing 的结合、可分离和不可分离混合模式,以及 Group isolation 对 Blending 的影响。

Blending 函数

Blending 的核心是 Blending 函数 B(Cb, Cs),它定义了 Backdrop 颜色 Cb 和 Source 颜色 Cs 如何计算出混合后的颜色 Cr

javascript 复制代码
// Blending 公式(非预乘形式)
Cr = (1 - αb) × Cs + αb × B(Cb, Cs);

αb 控制 Blending 函数的影响程度:

  • αb = 0(Backdrop 透明)时,Cr = Cs(保持 Source 原色)
  • αb = 1(Backdrop 不透明)时,Cr = B(Cb, Cs)(完全混合)

不同的 Blending 模式对应不同的 B 函数实现。Blending 模式分为两大类:

  • 可分离混合模式 (Separable Blend Modes)B(Cb, Cs) 对 RGB 每个通道独立计算
  • 不可分离混合模式 (Non-separable Blend Modes)B(Cb, Cs) 需要同时考虑所有颜色通道,涉及 RGB 与 HSL 色彩空间转换

Blending 与 Alpha Compositing

在完整的合成流程中,Blending 计算出 Cr 后,需要与 Porter-Duff Compositing 结合。最常见的简化模式是:Blending 之后进行 source-over 合成

这种模式对应参数:

  • Blending 模式:任意 B(Cb, Cs) 函数(如 multiply、screen 等)
  • Alpha Compositing 因子:Fa = 1, Fb = 1 - αs(source-over)

完整公式为:

javascript 复制代码
// 第 1 步:Blending
Cr = (1 - αb) × Cs + αb × B(Cb, Cs);

// 第 2 步:source-over Alpha Compositing
co = Cr × αs + Cb × αb × (1 - αs);
αo = αs + αb × (1 - αs);

合并为单一公式:

javascript 复制代码
// Blending + source-over 合成(预乘形式)
co = B(Cb, Cs) × αs × αb + (1 - αb) × cs + (1 - αs) × cb;
αo = αs + αb × (1 - αs);

这个公式清楚地展示了三部分的贡献:

  • B(Cb, Cs) × αs × αb:Blending 结果对重叠区域的贡献
  • (1 - αb) × cs:Source 在 Backdrop 透明区域的贡献
  • (1 - αs) × cb:Backdrop 在 Source 透明区域的贡献

这是 Web 平台最常用的合成模式,通过 CSS 的 mix-blend-mode 属性或 Canvas 的 globalCompositeOperation 属性使用。

可分离混合模式

可分离混合模式的 B(Cb, Cs) 函数对每个颜色通道独立应用。

混合模式 B(Cb, Cs) 公式 效果描述 常见用途
normal Cs 无混合 默认
multiply Cb × Cs 正片叠底 阴影、纹理叠加
screen Cb + Cs - Cb × Cs 滤色 发光效果
overlay Cb ≤ 0.5 ? 2×Cb×Cs : 1 - 2×(1-Cb)×(1-Cs) 叠加 图像增强
darken min(Cb, Cs) 变暗 暗部合成
lighten max(Cb, Cs) 变亮 亮部合成
color-dodge Cs ≥ 1 ? 1 : min(1, Cb / (1 - Cs)) 颜色减淡 强光效果
color-burn Cs ≤ 0 ? 0 : 1 - min(1, (1 - Cb) / Cs) 颜色加深 加深效果
hard-light Cs ≤ 0.5 ? 2×Cb×Cs : 1 - 2×(1-Cb)×(1-Cs) 强光 高对比度效果
soft-light 复杂公式(详见 W3C 规范) 柔光 柔光效果
difference |Cb - Cs| 差值 差异检测
exclusion Cb + Cs - 2×Cb×Cs 排除 柔和差异

代码示例:使用 multiply 混合模式

javascript 复制代码
const canvas = document.getElementById("myCanvas");
const ctx = canvas.getContext("2d");

// 绘制 Backdrop(蓝色矩形)
ctx.fillStyle = "blue";
ctx.fillRect(50, 50, 100, 100);

// 使用 multiply 混合模式绘制 Source(红色圆形)
ctx.globalCompositeOperation = "multiply";
ctx.fillStyle = "red";
ctx.arc(100, 100, 50, 0, Math.PI * 2);
ctx.fill();
// 重叠区域:blue × red = 暗紫色

不可分离混合模式

不可分离混合模式需要在 HSL 色彩空间中操作。辅助函数:

javascript 复制代码
// 计算亮度 (Luminosity)
Lum(C) = 0.3 × C.red + 0.59 × C.green + 0.11 × C.blue;

// 计算饱和度 (Saturation)
Sat(C) = max(C.red, C.green, C.blue) - min(C.red, C.green, C.blue);

// 设置亮度:保持色相和饱和度,修改亮度
SetLum(C, lum);

// 设置饱和度:保持色相和亮度,修改饱和度
SetSat(C, sat);
混合模式 B(Cb, Cs) 公式 效果描述 常见用途
hue SetLum(SetSat(Cs, Sat(Cb)), Lum(Cb)) 使用 Source 色相 色彩调整
saturation SetLum(SetSat(Cb, Sat(Cs)), Lum(Cb)) 使用 Source 饱和度 饱和度调整
color SetLum(Cs, Lum(Cb)) 使用 Source 色相和饱和度 着色效果
luminosity SetLum(Cb, Lum(Cs)) 使用 Source 亮度 亮度调整

Compositing Groups

Compositing Groups 影响元素如何与背景交互。理解 Groups 对于掌握复杂的视觉效果至关重要,它决定了子元素能否看到外部背景,以及 Group 如何作为整体与外部合成。

Group Invariance(组不变性)

Group Invariance 是 Compositing Groups 的核心原则:一个 Group 作为整体与外部 Backdrop 进行合成的结果,应该与 Group 内部的具体实现细节无关。

具体来说,Group Invariance 要求:

  1. Group 内部的元素先相互合成,生成 Group 的最终图像
  2. Group 作为单个图层与外部 Backdrop 进行合成
  3. Group 内部元素的顺序、数量、合成方式对外部不可见

这个原则确保了模块化和封装性。例如,一个包含多个子元素的 <div> 可以作为一个整体与页面其他部分进行合成,而不会因为内部结构变化而影响最终效果。

Group Invariance 的数学表达:

javascript 复制代码
// Group 内部合成
let groupResult = initialBackdrop;
for (const element of groupElements) {
  groupResult = composite(groupResult, element);
}

// Group 作为整体与外部合成
const finalResult = composite(externalBackdrop, groupResult);

组不变性的数学基础

Compositing 操作满足结合律,这是组不变性的数学基础:

javascript 复制代码
// 三个元素 A、B、C
composite(composite(A, B), C) === composite(A, composite(B, C));
// (A + B) + C = A + (B + C)

Simple Alpha Compositing 满足组不变性

对于 Simple Alpha Compositing (source-over),组不变性总是成立,因为其公式是线性的:

javascript 复制代码
// source-over 是线性操作
co = cs + cb × (1 - αs);
αo = αs + αb × (1 - αs);

这意味着:

  • 可以先把 A 和 B 合成,再与 C 合成 → (A + B) + C
  • 也可以先把 B 和 C 合成,再与 A 合成 → A + (B + C)
  • 两种方式结果相同

因为满足结合律,所以可以安全地将元素分组:先在 Group 内部合成,再与外部合成,不会改变最终结果。

Blending 打破组不变性

某些 Blending 模式会打破组不变性,因为 Blending 函数 B(Cb, Cs) 是非线性的。例如 multiply 模式:

javascript 复制代码
B(Cb, Cs) = Cb × Cs;

// 非线性导致不满足结合律
B(B(Ca, Cb), Cc) ≠ B(Ca, B(Cb, Cc));

考虑完整的 Blending 公式:

javascript 复制代码
Cr = (1 - αb) × Cs + αb × B(Cb, Cs);

由于 αbB 的非线性特性,不同的分组方式会导致不同的结果:

javascript 复制代码
// 情况 1:B 作为 Group 先与 C 合成
// B 看到的 Backdrop 是 C,αb 来自 C
groupResult = blend(B, C);
final = blend(groupResult, A);

// 情况 2:B 直接与外部 A 合成
// B 看到的 Backdrop 是 A,αb 来自 A
final = blend(blend(A, B), C);

// 两种情况结果不同

Isolated Groups 的必要性

为了在使用 Blending 时保持确定性,CSS 中设置 mix-blend-mode 会自动创建新的层叠上下文和 Compositing Group:

css 复制代码
.element {
  mix-blend-mode: multiply;
  /* 自动创建 Compositing Group,确保混合顺序的确定性 */
}

通过创建 Compositing Group,系统明确定义:

  1. 混合顺序:Group 内的元素按文档顺序依次混合
  2. Backdrop 来源 :Blending 函数 B(Cb, Cs) 中的 Cb 来自 Group 的 initialBackdrop
  3. 隔离范围:如果是 Isolated Group,initialBackdrop 为透明,元素只与 Group 内部混合

实际例子:

html 复制代码
<div style="background: red;">
  <div style="background: green; mix-blend-mode: multiply;">
    <div style="background: blue; mix-blend-mode: multiply;"></div>
  </div>
</div>
  • 绿色层设置了 multiply,创建 Compositing Group
  • 蓝色层在绿色层的 Group 内,先与绿色混合
  • 绿色 Group 的结果再与红色背景混合
  • 混合顺序:blue → green → red,确定且不可改变

这就引出了 Isolated Groups 和 Non-isolated Groups 的区别:它们的 initialBackdrop 不同,决定了 Group 内元素能否看到外部背景。

Isolated 和 Non-isolated Groups

Isolated Groups 和 Non-isolated Groups 的核心区别在于 initialBackdrop 的取值:

Isolated Groups(隔离组)

初始 Backdrop 为全透明黑色 rgba(0, 0, 0, 0),Group 内部元素看不到外部背景:

javascript 复制代码
let groupResult = { r: 0, g: 0, b: 0, α: 0 }; // 透明背景

for (const element of groupElements) {
  groupResult = composite(groupResult, element);
}

// Group 作为整体与外部合成
const finalResult = composite(externalBackdrop, groupResult);

Non-isolated Groups(非隔离组)

初始 Backdrop 使用外部 Backdrop,Group 内部元素可以看到外部背景:

javascript 复制代码
let groupResult = externalBackdrop; // 使用外部背景

for (const element of groupElements) {
  groupResult = composite(groupResult, element);
}

const finalResult = groupResult;

创建 Isolated Group 的方式

根据 W3C 规范:

  • CSS:所有创建层叠上下文 (stacking context) 的操作都会创建 Isolated Group
  • SVG:opacity、filters、3D transforms、blending、masking 会创建 Isolated Group
  • 显式创建:使用 isolation: isolate 属性

默认情况下,HTML 元素形成 Non-isolated Group。

Isolated vs Non-isolated 对比

假设有一个绿色圆形使用 multiply 混合模式叠加在红色方块和蓝色背景上:

html 复制代码
<!-- Non-isolated Group(默认) -->
<div style="background: blue; padding: 50px;">
  <div style="background: red; width: 100px; height: 100px;"></div>
  <div
    style="background: green; border-radius: 50%; width: 80px; height: 80px;
              margin: -60px 0 0 40px; mix-blend-mode: multiply;"
  ></div>
</div>

<!-- Isolated Group -->
<div style="background: blue; padding: 50px;">
  <div style="isolation: isolate;">
    <div style="background: red; width: 100px; height: 100px;"></div>
    <div
      style="background: green; border-radius: 50%; width: 80px; height: 80px;
                margin: -60px 0 0 40px; mix-blend-mode: multiply;"
    ></div>
  </div>
</div>

Non-isolated Group 的合成过程:

javascript 复制代码
// 初始 Backdrop 是蓝色背景
let result = blue;

// 红色方块与蓝色背景合成
result = composite(result, red); // → 蓝色背景上的红色方块

// 绿色圆形使用 multiply 模式与当前结果合成
// 绿色圆形会与红色方块和蓝色背景都发生混合
result = composite_with_blend(result, green, multiply);
// 重叠红色区域:green × red = 暗红色
// 重叠蓝色区域:green × blue = 暗蓝色

Isolated Group 的合成过程:

javascript 复制代码
// Group 内部:初始 Backdrop 是透明
let groupResult = transparent;

// 红色方块与透明背景合成
groupResult = composite(groupResult, red); // → 红色方块(无背景)

// 绿色圆形使用 multiply 模式与红色方块合成
// 绿色圆形只能看到红色方块,看不到外部蓝色背景
groupResult = composite_with_blend(groupResult, green, multiply);
// 重叠区域:green × red = 暗红色
// 非重叠区域:保持原色

// Group 结果与蓝色背景合成
result = composite(blue, groupResult);
// 最终:蓝色背景 + 红色方块 + 暗红色重叠区域(绿色圆形不会与蓝色混合)

关键区别:

  • Non-isolated:绿色圆形与红色方块和蓝色背景都发生 multiply 混合
  • Isolated:绿色圆形只与红色方块发生 multiply 混合,看不到蓝色背景

实际应用

Compositing 和 Blending 技术在 Web 平台的三个主要领域有广泛应用:Canvas 2D API、HTML/CSS、SVG。本章介绍如何在这些平台使用这些技术,以及性能优化建议。

平台能力对比

  • Canvas 2D:同时支持 Porter-Duff 操作符和 Blending 模式
  • CSS :仅支持 Blending 模式(通过 mix-blend-modebackground-blend-mode
  • SVG :通过滤镜元素支持 Porter-Duff 操作符(feComposite)和 Blending 模式(feBlend

重要说明 :在所有 Web 平台(Canvas 2D、CSS、SVG)中,Blending 模式都采用 Blending + source-over 的合成方式,即 Blending 计算后使用 source-over 进行 Alpha Compositing(Fa=1, Fb=1-αs)。

Canvas 2D

Canvas 2D 通过 globalCompositeOperation 属性控制 Compositing 和 Blending。

globalCompositeOperation 属性

该属性接受字符串值,指定合成或混合模式。支持的值包括:

  • Porter-Duff 操作符source-over(默认), source-in, source-out, source-atop, destination-over, destination-in, destination-out, destination-atop, lighter, copy, xor, destination
  • Blending 模式multiply, screen, overlay, darken, lighten, color-dodge, color-burn, hard-light, soft-light, difference, exclusion, hue, saturation, color, luminosity

CSS

CSS 仅支持 Blending 模式,不支持 Porter-Duff 操作符。提供以下属性控制 Blending:

mix-blend-mode

控制元素与其 Backdrop 的混合方式。元素会与下方的所有内容进行混合。

  • 语法:mix-blend-mode: <blend-mode>
  • 支持的值:normal(默认), multiply, screen, overlay, darken, lighten, color-dodge, color-burn, hard-light, soft-light, difference, exclusion, hue, saturation, color, luminosity
  • 自动创建 Compositing Group(非 normal 时)

background-blend-mode

控制同一元素的多个背景层之间的混合。多个背景按照从上到下的顺序混合。

  • 语法:background-blend-mode: <blend-mode>
  • 支持的值:与 mix-blend-mode 相同
  • 可以指定多个值,对应多个背景层

isolation

显式创建 Isolated Group,隔离子元素的混合效果。

  • 语法:isolation: auto | isolate
  • auto:默认,不创建隔离组
  • isolate:创建 Isolated Group,子元素的混合不会影响外部背景

SVG

SVG 通过滤镜元素实现 Compositing 和 Blending。

feComposite 元素

实现 Porter-Duff Compositing 操作符。

  • 属性:operator 指定操作符类型
  • 支持的值:over, in, out, atop, xor, lighter, arithmetic
  • inin2 属性指定 Source 和 Backdrop
  • arithmetic 模式支持自定义线性组合公式

feBlend 元素

实现 Blending 混合模式。

  • 属性:mode 指定混合模式
  • 支持的值:normal, multiply, screen, overlay, darken, lighten, color-dodge, color-burn, hard-light, soft-light, difference, exclusion, hue, saturation, color, luminosity
  • inin2 属性指定要混合的两个输入源

Isolated Group 的性能影响

Isolated Group 需要离屏渲染,带来额外的性能开销:

  • 浏览器为 Group 分配离屏缓冲区(offscreen buffer)
  • Group 内容先渲染到离屏缓冲区,再合成到主画布
  • 增加内存占用和 GPU 计算负担

过多或嵌套的 Isolated Group 会显著影响渲染性能。

Canvas 2D

Canvas 2D 是即时模式(immediate mode)绘图 API,不会自动创建 Isolated Group。如需隔离效果,需手动使用离屏 Canvas。

HTML/CSS

根据 W3C 规范 Section 3.2,HTML/CSS 中所有创建层叠上下文(Stacking Context)的属性都会创建 Isolated Group,包括 opacitytransformfiltermix-blend-modeisolation: isolate 等。

SVG

根据 W3C 规范 Section 3.3,SVG 中只有特定操作会创建 Isolated Group:

  • opacity
  • filters(滤镜)
  • 3D transforms(2D transforms 不会创建)
  • blending(混合模式)
  • masking(遮罩)
相关推荐
小高0078 小时前
读懂 Tailwind v4:为什么它是现代前端项目的必选项?
前端·javascript·vue.js
我的golang之路果然有问题8 小时前
python中 unicorn 热重启问题和 debug 的 json
java·服务器·前端·python·json
SpringLament8 小时前
从零打造AI智能博客:一个项目带你入门全栈与大模型应用开发
前端·aigc
晴虹8 小时前
lecen:一个更好的开源可视化系统搭建项目--数据、请求、寄连对象使用--全低代码|所见即所得|利用可视化设计器构建你的应用系统-做一个懂你的人
前端·后端·低代码
MOON404☾8 小时前
004.漏洞分析与利用
前端·网络·网络安全·系统安全·firefox
kylezhao20198 小时前
C#根据时间加密和防止反编译
java·前端·c#
愈努力俞幸运8 小时前
volta教程 下载安装使用
前端
冰暮流星8 小时前
javascript短路运算
开发语言·前端·javascript
qq_419854058 小时前
移动端开发:h5应用开发
前端