在写《色彩空间(一):色彩空间基础》文章的时候,为了让读者能够通过鼠标交互式地直观感受三维立体的 RGB 色彩空间,同时也为了给文章提供图片素材,我制作了一个小项目 Color Cube 来配合文章输出。前端开发水平实在有限,这个 Side Project 耗了我一整天的时间,过程中遇到了一些难题,觉得蛮有意思的,写篇小博客记录一下,如果今后有人遇到类似问题,希望可以帮到他(如果我的网站 SEO 还不错,如果他运气还挺好能搜到这篇文章)。

项目演示地址

下图为最终效果:

Color Cube

思路

对于真实的 RGB 色彩空间而言,立方体内的每一个横截面都是具备完整色彩的,不过我们作为演示的话没必要这么复杂,只要把立方体的外壳做出来就可以起到同样视觉效果了。

那么思路就比较清晰了:

  1. 首先需要一个立方体,添加 JS 鼠标拖拽交互;
  2. 然后绘制立方体的 6 个面,通过背景图的方式贴在立方体上。
  3. 添加坐标轴。

立方体

实际上立方体的制作并不复杂,因为已经有不少前辈做过类似的内容,通过一番搜索,找到一个比较符合我需求的立方体,支持鼠标拖拽,代码也很简洁,虽然 JS 部分勉强看明白。

Demo 演示地址

不过它并不完美,我顺便修复了立方体在鼠标拖动时的初始化角度,解决了第一次拖拽时的抖动问题;修改了鼠标拖动时的参数,解决了拖拽扭动方向与鼠标方向相反的问题;

六个面

问题的关键难点在于六个面的制作,这块耗费了我大量的时间。

制作六个面之前,需要先知道这六个面分别长什么样,为了简化推导过程,我在网上找到了一个前辈通过 SketchUp 草图大师制作的色彩立方体(下载地址),可以通过拖拽旋转的方式观察六个面的色彩指导我绘制每个面的贴图。六个面的外观如下:

color cube surface

难点一:交融混合渐变

我们都知道,常见的设计工具(比如 Sketch)提供的渐变方法包括:线性渐变(Linear)、径向渐变(Radial)以及角度渐变(Angular) 三种,但是以上渐变方式都无法满足我们需要的效果。

我们以上图第一块为例来观察一下,图中左侧从上到下是从青色到绿色的渐变,右侧从上到下是白色到黄色的渐变,而上侧从左到右又是从青色到白色的渐变,下侧从左到右是从绿色到黄色的渐变。它不是一个从 A 颜色到 B 颜色的单纯的线性渐变,横向纵向都有渐变关系又彼此交融。有没有小伙伴知道该如何实现这种渐变方式?可以暂停思考一下🤔。

经过一番思考与尝试之后,我发现可以通过半透明蒙版的方式将两个渐变叠加在一起来实现上述效果。我们可以在 Sketch 中创建 3 个图层:最上面的图层为从上到下青色到绿色的渐变;第二个图层为从左到右透明渐变图层,透明度从 100% 到 0,作为最上面图层的遮罩蒙版;最下面的图层为从上到下白色到黄色的渐变;然后将三个图层叠加在一起,就能实现我们想要的交融混合渐变效果。

Sketch 的蒙版有一个模式是 Alpha Mask,也就是刚刚说的半透明蒙版;默认蒙版模式是 Outline Mask 蒙版,也就是轮廓蒙版;Outline Mask 是以矢量元素的路径轮廓作为蒙版,与透明度无关,而 Alpha Mask 则支持透明度。切换方式:首先将一个元素设定为 Mask 蒙版,然后在 LayerMaskMask Mode 中选择 Alpa Mask 切换为半透明蒙版。

demo

完成后导出图片即可。

实际上经过我测试发现,即使不使用透明渐变,使用从白到黑的渐变,在导出SVG/EPS/PDF矢量格式的时候,从导出预览中可以看到导出效果和使用不透明渐变效果是一致的。这一点和 Adobe 家产品也一致,我们知道在 Ps 和 Ai 中使用蒙版的时候,黑色代表 “完全不显示”,白色代表 “完全展示”,从黑色到白色的渐变,相当于从完全透明到不透明的渐变。

难点二:SVG 矢量格式导出

上面的部分原本进展很顺利,导出 JPG 和 PNG 都没问题,但偏偏在导出 SVG 矢量格式的时候出了问题:完成交融混合渐变的图形设计,点击导出,选择 SVG 格式,生成的 SVG 图片最终效果却并不是混合渐变的,只有顶部图层的单一线性渐变效果,而在导出预览中查看却没问题,这是怎么回事?!

Google 了一下,在 reddit 上发现之前也有人问过这类问题,确实是 Sketch 的问题。但是我确实不太想使用 PNG 或者 JPG 位图,一方面是体积大,另一方面非矢量,对于一个 CSS 实现的立方来说,不是很理想。

于是我尝试通过之前积累的 SVG XML 语法,通过代码的方式调试 Sketch 输出的 SVG 文件来解决这个问题,但这个代码有些复杂,尝试了很久,依旧无果,但可以肯定的是,通过调整代码一定能解决这个问题。

再次陷入困境时,通过我强大的 Google 能力,找到了一个前端大神的博客,她也早在几年前就做过类似的事情,比较清奇的是,她设计 SVG 的方式是直接撸代码,不经过设计工具,甚至她为了防止繁琐重复的工作,又通过 SASS(可以通过脚本解析成 CSS 的语言)制作了一个脚本来输出剩下的 5 个面的 SVG。一边捂着膝盖,一边通过调试的方式研究了下她的示例代码:

<svg xmlns="http://www.w3.org/2000/svg" width="200px" height="200px">

<linearGradient id="yellow-white" x1="0" x2="0" y1="0" y2="1">
	<stop stop-color="yellow" />
	<stop offset="1" stop-color="white" />
</linearGradient>
<linearGradient id="magenta-red" x1="0" x2="0" y1="0" y2="1">
	<stop stop-color="red" />
	<stop offset="1" stop-color="magenta" />
</linearGradient>
<linearGradient id="gradient" x1="0" x2="1" y1="0" y2="0">
	<stop stop-color="white" />
	<stop offset="1" stop-color="black" />
</linearGradient>
<mask id="gradient-mask">
	<rect width="100%" height="100%" fill="url(#gradient)"/>
</mask>

<rect width="100%" height="100%" fill="url(#yellow-white)"/>
<rect width="100%" height="100%" fill="url(#magenta-red)" mask="url(#gradient-mask)"/>

</svg>

由于 CSS 立方体建模方式的问题,我需要的 6 个面的渐变方式和她的并不完全一致,所以她提供的六个面并不能直接用于我的项目,否则会导致面与面之间拼接混乱,颜色衔接不上。

不过相比于 Sketch 直接生成的 SVG 代码,她的代码在结构上易读性好了很多,代码逻辑和 Sketch 中的图层逻辑也是一致的,理解起来很容易。因此在她的基础上,通过修改里面参数的方式,我迅速搞定了另外 5 个面。

其实设计师不需要太抗拒代码,我相信大家愿意的话都能做到我这一步,而且如果你懂一点 CSS 和 SVG 的代码,你会发现设计为你打开了一个新的大门。

补充

  1. 六个面 SVG 的下载地址:Color-Cube,有同学需要的话请自取。
  2. 为了增强可读性,在代码中我使用的是 CSS 颜色名 而非色值,发现一个有趣事情,#00FF00 纯绿色色值对应的 CSS 颜色名 叫 Lime 而非 Green,Green 对应的色值为 #008000,相比 Lime 色彩更深邃。
  3. 在使用 SVG 以 data:image/svg+xml 作为 background 的时候,需要把 SVG 代码中的 # 转为对应的 UTF-8 UrlEncode 编码 %23,否则无法展示。
  4. 新版 Ai 中有一个叫 “任意形状渐变” 的功能,可以在不使用蒙版的情况下快速实现交融混合渐变的效果,但这个功能的定位似乎更倾向于插画的绘制,在渐变的位置上无法做到数学精准定位,所以还不适合做本项目的渐变方式,但这个功能渐变更加自由,更加强大,也更简单。
  5. Ai 中使用 Alpha Mask 绘制的渐变图形是可以成功导出 SVG 的,不过当时陷入了 Sketch 的坑里,忘记了使用 Ai...
  6. 上面提供下载的 SVG 使用代码绘制,在网页展示正常,但拖入 Ai 的时候会报错,拖入 Sketch的时候只显示单一渐变色彩。
  7. 制作交融混合渐变图形的时候,建议使用同色相或者相邻色相的颜色,这样看起来会更自然通透。
  8. 刚才提到的大神是一位程序媛,后来我才知道我在构建个人博客网站时用到的 Prism.js 也是她做的,我们在线版调整动画的曲线工具 cubic-bezier 也是她做的,就连我之前买来从未看过的书《CSS揭秘》也是她写的,她叫 Lea Verou,是设计 CSS 语言的委员之一,W3C CSS 工作组特邀专家。