HSV 色相饱和明度
HSV 与 HSL 是两种不同的色彩模型,Konva 各提供了一个滤镜。
它们的前两个参数同名同值,区别在第三个:HSV 是 value(明度),
HSL 是 luminance(亮度)。
node.cache();
node.filters([Konva.Filters.HSV]);
node.hue(200); // 色相旋转角度,默认 0
node.saturation(1.5); // 饱和度增量,默认 0
node.value(0.3); // 明度增量,默认 0
什么时候该选 HSV 而不是 HSL
选择的依据只有一个:你想让"变亮"这件事如何影响色彩浓度。
用 rgb(200,80,40)(一个偏橙的红)做对照,两边都加 0.3:
| 结果 | 绿通道变化 | |
|---|---|---|
HSL 的 luminance(0.3) | 238,118,78 | 80 → 118 |
HSV 的 value(0.3) | 246,98,49 | 80 → 98 |
HSL 的 luminance 本质是向白色靠拢——各通道一起往上抬, 通道之间的差距被压缩,颜色因此变淡、发灰。 上表里绿通道涨了 38,红绿差从 120 缩到 120,整体明显"粉"了。
HSV 的 value 是抬高明度上限——它更多地按比例缩放, 通道间的相对关系保留得更好,颜色依然浓。绿通道只涨了 18。
实践上的对应关系:
- 要提亮但保持鲜艳(照片调色、让暗部看清)→ HSV 的
value - 要做褪色、朦胧、被雾笼罩的效果 → HSL 的
luminance - 要做禁用态的灰化 → HSL 的
luminance配合负的saturation, 比 HSV 更容易做出均匀的灰
不要和 HSL 同时启用
hue 和 saturation 这两个属性由 HSL 和 HSV 共同注册,是同一个属性。
同时把两个滤镜放进 filters(),它们会各读一次同样的值、各做一次变换。
实测 rgb(200,80,40):HSL 单独 hue(120) 与 HSV 单独 hue(120)
结果完全相同(都是 102,83,255);两个一起挂、仍然只设 hue(120),
得到 40,159,25——接近单独旋转 240° 的效果。
完整的实测对照表见 HSL 页。
常见问题
只想调明度,不想动色相,需要设 hue 吗?
不需要。hue 默认是 0,不设就不旋转。
但要小心属性是挂在节点上的、会被记住。
如果这个节点之前用过 HSL 滤镜并设过 hue,
换成 HSV 之后那个 hue 值依然在,会继续生效。
换滤镜时如果行为不符预期,先 node.hue(0) 显式归零。
value 能设负数吗?
能,负值让画面变暗。value(-0.5) 会明显压暗,
这是做"失焦""禁用""背景虚化"效果的常用手段。
比起用 Brightness 乘一个小于 1 的数,
value 的好处是暗部不会一起被压死——
乘法对已经很暗的像素影响是成比例的,容易让暗部细节全部糊成黑块。
HSV 和 HSL 哪个更快?
两者都是逐像素、无邻域采样,成本几乎相同, 选哪个应该完全基于视觉效果而不是性能。
真正影响性能的是缓存面积和是否每帧重建缓存,见下面。
性能提示
HSV 的计算量略高于 Brightness——每个像素要在 RGB 与 HSV 之间往返转换,
包含若干次比较和除法。但它仍属于便宜的逐像素滤镜,
在静态场景下几乎不需要考虑优化。
需要留意的是三种典型的浪费:
在动画里重复 cache()。 这是最常见也最致命的错误。
缓存一次,之后每帧只改 hue() / value() 再 layer.draw()。
每帧 cache() 意味着每帧都要分配一张离屏画布并重绘节点,
帧率会从 60 掉到个位数。
给整个图层加缓存。 layer.cache() 建的是与舞台等大的画布。
只有真正需要滤镜的那个节点才该缓存。
缓存分辨率过高。 默认 pixelRatio 是设备像素比,
2x 屏上面积是四倍,滤镜耗时也是四倍。
调色类滤镜对分辨率不敏感(它不改变形状,只改变颜色),
cache({ pixelRatio: 1 }) 在多数场景下肉眼无差别,
却能省掉四分之三的计算。这是本章里性价比最高的一条优化。
如果只是要一个整体的色相偏移而不需要精细控制,
CSS 滤镜的 hue-rotate() 走浏览器原生实现,
比任何 JS 逐像素循环都快。