一、径向渐变的核心原理
1. 基本定义
径向渐变通过两个圆(起始圆和结束圆)定义渐变范围:
- 起始圆:圆心
(x0, y0),半径r0(通常较小,甚至为0) - 结束圆:圆心
(x1, y1),半径r1(通常较大) - 颜色过渡发生在两个圆之间的所有中间圆上,而非简单的线性投影。
2. 颜色计算的数学本质
这个公式是**径向渐变(Radial Gradient)的核心数学定义。它的由来基于“中间圆族”**的几何插值原理。
2.1. 核心思想:圆与圆的插值
径向渐变由两个圆定义:
- 起始圆(Circle 0):圆心 ,半径 。
- 结束圆(Circle 1):圆心 ,半径 。
当渐变参数 从 0 变化到 1 时,图形系统会在两个圆之间生成无数个**“中间圆”。对于任意一个 值(),这个中间圆的圆心和半径是通过线性插值**计算出来的:
-
中间圆的圆心 : (几何意义:圆心从起点圆心沿直线移动到终点圆心)
-
中间圆的半径 : (几何意义:半径从起始半径线性变化到终点半径)
公式
确实可以直接从向量方程推导而来。
这种写法在数学上被称为仿射组合(Affine Combination)或线性插值(Linear Interpolation / Lerp),它是向量加法与数乘运算的直接结果。
1. 向量推导过程
假设我们有两个点(或向量):
- 起点 ,坐标为
- 终点 ,坐标为
我们要找这两点连线上的任意一点 ,其位置由参数 控制。
步骤 1:定义方向向量 从 指向 的向量 为:
步骤 2:向量位移方程 点 可以看作是从起点 出发,沿着方向 移动了一段距离。这段距离是总长度的 倍(其中 )。 向量方程为: 或者写作: (这里 是原点, 表示位置向量)
步骤 3:展开并合并同类项 将 代入方程:
提取公因式 :
步骤 4:分解到 X 轴 如果我们只看 X 分量:
同理,Y 分量为:
2. 为什么系数是 和 ?
这体现了**权重(Weight)**的概念:
-
当 时: 起点的权重是 100%,终点的权重是 0%。结果是起点。
-
当 时: 起点的权重是 0%,终点的权重是 100%。结果是终点。
-
当 时: 两者权重各占 50%。结果是中点。
关键性质:两个系数之和恒为 1。 这在几何上保证了结果点 始终落在 和 的连线上(如果是凸组合,则在线段上)。
3. 总结
你看到的 就是向量方程 的代数展开形式。
- 向量形式更直观地表达了“从起点沿方向移动”的物理意义。
- 标量形式(即你问的公式)更便于计算机直接进行坐标计算,因为它把 X 和 Y 解耦了,且不需要显式构建向量对象。
所以在径向渐变中,圆心 的运动轨迹,本质上就是一个匀速直线运动的向量插值过程。
2.2. 圆的标准方程
在平面几何中,一个圆心为 ,半径为 的圆,其标准方程为:
2.3. 公式的组装
为了找到点 对应的颜色,我们需要知道该点落在哪个 值的中间圆上。
我们将第1步中推导出的中间圆圆心和中间圆半径代入第2步的圆方程中:
-
代入圆心:
-
代入半径:
合并后,就得到了你看到的公式:
2.4. 这个公式意味着什么?
这个方程建立了一个关于 的二次方程关系。
- 左边:表示点 到“当前时刻 的圆心”的距离的平方。
- 右边:表示“当前时刻 的圆”的半径的平方。
求解过程: 当渲染引擎处理像素 时,它会解这个方程求出 。
- 如果解出的 ,说明点 刚好落在渐变过程 50% 位置的那个圆环上,因此取颜色渐变条 50% 处的颜色。
- 如果方程无解(例如点 在渐变范围之外),则根据
spreadMethod(如pad,reflect,repeat)来决定颜色。
2.5 总结
这个公式的来源就是**“圆的定义”** + “线性插值”。它描述了径向渐变本质上是一个圆心在移动、半径在缩放的动态圆族覆盖过程。
简单来说,径向渐变并不是简单的颜色混合,而是形状的过渡。系统通过计算一系列“中间圆”,来确定任意像素点 处于渐变的哪个阶段(即 值)。
对于任意点 P(x, y),其颜色由参数 t 决定,t 需满足以下条件:
点 P 位于由起始圆到结束圆线性插值得到的中间圆上,即:
(x - [(1-t)·x0 + t·x1])² + (y - [(1-t)·y0 + t·y1])² = [(1-t)·r0 + t·r1]²
这是一个关于 t 的二次方程,解出 t 后才能确定颜色位置。
关键点:
- 当两圆同心(
x0=x1, y0=y1)时,公式简化为t = (d - r0)/(r1 - r0)(d为点P到圆心的距离)。 - 当两圆不同心时,必须解二次方程,无法简化为线性投影计算。
二、用户结论的错误分析
以下依据 Canvas 2D API 规范(W3C 标准)及主流浏览器实现进行纠正。
❌ 错误的核心公式分析
原公式:
proj = (点P相对向量) · (归一化轴向)
t = (proj - r1) / (r2 - r1)
错误根源:
- 物理模型错误:径向渐变的颜色由“位于两个圆之间的同心圆族”决定,而非“垂直于渐变轴的一条直线”。
- 数学计算错误:当两圆不同心时,
t是二次方程的解,无法通过一次线性投影计算得出。
✅ 径向渐变的正确原理与公式
径向渐变由两个圆定义:
- 起点圆:
C0 = (x0, y0),半径r0 - 终点圆:
C1 = (x1, y1),半径r1
对于画布上的任意一点 P(x, y),其对应的渐变插值参数 t 需满足:
点 P 必须位于以 (1-t)C0 + t·C1 为圆心、半径为 (1-t)r0 + t·r1 的圆周上。
代入方程得:
[ x - (1-t)x0 - t·x1 ]² + [ y - (1-t)y0 - t·y1 ]² = [ (1-t)r0 + t·r1 ]²
展开后得到标准二次方程:
A·t² + B·t + C = 0
其中:
dx = x1 - x0,dy = y1 - y0,dr = r1 - r0A = dx² + dy² - dr²B = 2·[ (x - x0)·(-dx) + (y - y0)·(-dy) + r0·dr ]C = (x - x0)² + (y - y0)² - r0²
t 的有效取值:
- 解二次方程,取满足
0 ≤ t ≤ 1的较大实根(规范要求:当点位于重叠区域时,取外侧圆对应的 t)。 - 若
t < 0→ 使用 0% 颜色。 - 若
t > 1→ 使用 100% 颜色。
⚡ 关键区别对照表
| 特征 | 原错误结论(线性逻辑) | 实际径向渐变逻辑 |
|---|---|---|
| 两圆同心 | 碰巧正确(t = (dist - r0)/(r1 - r0)) | 公式退化为线性比例,特例 |
| 两圆偏心 | 错误(投影值产生非线性误差) | 必须解二次方程,结果与投影值无直接关系 |
| 相交情况 | 无法处理双解情况 | 通过取最大实根正确选择外侧圆颜色 |
| 内含情况 | 可能导致 t 超出 [0,1] 时颜色映射错误 | 按规范处理扩展模式 |
🔍 典型反例验证
设:
- 起点圆:
(0, 0, r0=10) - 终点圆:
(100, 0, r1=50) - 待测点 P:
(50, 0)(位于圆心连线上)
错误投影法:
proj = 50,t = (50 - 10)/(50 - 10) = 1.0 → 显示终点颜色。
实际二次方程计算:
代入方程 (50 - 100t)² = (10 + 40t)² → 解得 t ≈ 0.36 或 t ≈ -1.14(舍去负值)。
→ 实际显示过渡色,而非终点色。误差极大。
📝 总结纠正
- 颜色分布原理:由两个圆定义的圆锥曲面族决定,必须解二次方程。
- 与位置关系:强烈相关。同心是唯一可简化为线性距离计算的特例;偏心、相交、相离均需完整解方程。
- 开发实践:浏览器底层图形库(Skia、CoreGraphics、Direct2D)均已实现正确的二次方程求解逻辑。开发者无需手写公式,但理解原理有助于处理复杂阴影、光照模拟等效果。
三、径向渐变的正确计算逻辑
1. 标准流程
- 定义中间圆族:
对每个t ∈,中间圆的圆心为( (1-t)·x0 + t·x1, (1-t)·y0 + t·y1 ),半径为(1-t)·r0 + t·r1。 - 求解点P对应的
t:
代入点P(x,y)到中间圆方程,解二次方程:其中:A·t² + B·t + C = 0A = (x1-x0)² + (y1-y0)² - (r1-r0)²B = 2[(x-x0)(x0-x1) + (y-y0)(y0-y1) + r0(r1-r0)]C = (x-x0)² + (y-y0)² - r0²
- 确定有效
t:- 若方程有解,取 满足
t ∈的最大实根(因渐变从内向外扩散)。 - 若无解,则点P在渐变区域外,按
spreadMethod规则处理(默认取最近边界色)。
- 若方程有解,取 满足
2. 颜色映射规则
t ≤ 0→ 显示colorStop(0)的颜色(起始圆内侧)。0 < t < 1→ 在相邻色标间线性插值颜色。t ≥ 1→ 显示colorStop(1)的颜色(结束圆外侧)。
这个公式本质上是在描述**“点与圆的几何位置关系”**。
它之所以能用来计算颜色,是因为它将**“寻找颜色”的问题转化为了“求解方程”**的数学问题。
以下是该公式的来源推导,以及它与“方程有解”和“颜色映射”之间的逻辑关联:
1. 公式是怎么来的?(几何插值原理)
径向渐变的本质是两个圆之间的形状过渡。
- 起始圆(Circle 0):圆心 ,半径 。
- 结束圆(Circle 1):圆心 ,半径 。
当渐变从 0 进行到 1 时,中间会生成无数个**“过渡圆”**。对于任意一个进度 (),这个过渡圆的属性是两个圆的线性插值:
- 过渡圆的圆心:
- 过渡圆的半径:
我们知道,圆的标准方程是 (其中 是圆心, 是半径)。 将上面的过渡圆属性代入标准方程,就得到了你看到的公式:
一句话总结来源: 这个公式就是**“时刻 的那个圆的方程”**。
2. 和“方程有解”是怎么关联的?
在渲染图像时,屏幕上的每一个像素点 都是已知的,而 是未知的。我们需要知道这个点到底落在哪个过渡圆上。
我们将公式展开,它实际上是一个关于 的一元二次方程:
这里的“方程有解”对应着以下物理意义:
情况 A:方程有解( 存在)
这意味着点 确实位于从起始圆到结束圆的过渡轨迹上。
- 我们解出 (通常取 之间的实数解)。
- 这个 值直接告诉我们要取渐变色条上的哪个位置的颜色。
- 例如:解出 ,说明该点位于渐变的正中间,颜色就是渐变色的 50% 处。
情况 B:方程无解(或无实数解)
这意味着点 不在任何过渡圆上。这通常发生在渐变范围之外。
- 例如:如果两个圆是同心圆,且 ,那么在 之外的区域,或者 之内的区域(取决于具体定义),可能无法通过 的插值圆覆盖。
- 这时就需要用到你提到的颜色映射规则(即
spreadMethod):- Pad(填充):无解时,取最近的边界颜色( 或 的颜色)。
- Reflect(反射):无解时,模拟镜像延伸。
- Repeat(重复):无解时,循环渐变。
3. 关联总结表
| 步骤 | 数学表达 | 物理/视觉含义 |
|---|---|---|
| 1. 定义 | 定义了一个随时间 变化的圆环。 | |
| 2. 求解 | 把像素坐标 代入,解出 | 询问系统:“这个像素点是在渐变过程的哪一刻出现的?” |
| 3. 判定 | 有解 () | 在渐变内。该点的颜色 = 渐变色条在 处的颜色。 |
| 4. 映射 | 无解 (或 超出范围) | 在渐变外。根据规则(Pad/Reflect/Repeat)决定颜色。 |
结论: 这个公式是判定器。它通过计算点 是否满足某个 时刻的圆方程,来反推该点应该显示什么颜色。方程的解 ,就是颜色查找的索引值。
四、与线性渐变的本质区别
| 特性 | 线性渐变 | 径向渐变 |
|---|---|---|
| 定义方式 | 起点 (x0,y0) 和终点 (x1,y1) | 起始圆 (x0,y0,r0) 和结束圆 (x1,y1,r1) |
| 计算基础 | 点在方向向量上的线性投影 | 点与双圆的几何关系(二次方程) |
| 位置关系影响 | 仅由方向向量决定,与位置无关 | 直接依赖两圆圆心/半径关系 |
| 典型场景 | 水平/垂直/对角渐变 | 光晕、球体、焦点效果 |
总结
径向渐变的颜色分布必须通过双圆几何关系解二次方程确定,其计算逻辑高度依赖两圆的圆心位置和半径,无法简化为线性投影。用户提出的公式仅适用于线性渐变,不适用于径向渐变。实际开发中,浏览器会自动处理复杂的数学计算,开发者只需通过 createRadialGradient(x0,y0,r0,x1,y1,r1) 定义两个圆即可。