当我第一次开始构建 colorify rocks(我的调色板网站)时,我不知道程序化颜色操作的兔子洞有多深。最初是一个简单的“让我构建一个颜色选择器”项目,后来变成了一次通过颜色理论、数学颜色空间和可访问性考虑的迷人旅程。今天,我想分享我在构建这个工具时学到的东西,以及一些可能对你自己的色彩冒险有所帮助的 python 代码。
只是颜色而已,能有多难呢?
哦,过了我。那时的你多么天真啊!我的旅程始于一个简单的目标:建立一个网站,人们可以在其中生成和保存调色板。容易,对吧?只需获取一个十六进制代码,然后…等等,什么是 hsl?为什么我们需要 rgb?那么 cmyk 到底是什么?
想看看我在说什么吗?查看我们对 #3b49df 的颜色分析
这是我编写的第一段用于处理颜色转换的代码,现在它的简单性让我咯咯笑:
class color: def __init__(self, hex_code): self.hex = hex_code.lstrip('#') # past me: "this is probably all i need!" def to_rgb(self): # my first "aha!" moment with color spaces r = int(self.hex[0:2], 16) g = int(self.hex[2:4], 16) b = int(self.hex[4:6], 16) return f"rgb({r},{g},{b})"
一切都是数学
然后我意识到颜色基本上只是伪装的数学。在色彩空间之间进行转换意味着要深入研究我从高中以来就没有接触过的算法。以下是代码演变成的内容
def _rgb_to_hsl(self): # this was my "mind-blown" moment r, g, b = [x/255 for x in (self.rgb['r'], self.rgb['g'], self.rgb['b'])] cmax, cmin = max(r, g, b), min(r, g, b) delta = cmax - cmin # the math that made me question everything i knew about colors h = 0 if delta != 0: if cmax == r: h = 60 * (((g - b) / delta) % 6) elif cmax == g: h = 60 * ((b - r) / delta + 2) else: h = 60 * ((r - g) / delta + 4) l = (cmax + cmin) / 2 s = 0 if delta == 0 else delta / (1 - abs(2 * l - 1)) return { 'h': round(h), 's': round(s * 100), 'l': round(l * 100) }
颜色之间有关系
我为 colorify rocks 构建的最令人兴奋的功能之一是色彩和谐生成器。事实证明,颜色之间有关系,就像音符一样!以下是我实现色彩和谐的方法:
def get_color_harmonies(self, color): """ this is probably my favorite piece of code in the entire project. it's like playing with a color wheel, but in code! """ h, s, l = color.hsl['h'], color.hsl['s'], color.hsl['l'] return { 'complementary': self._get_complementary(h, s, l), 'analogous': self._get_analogous(h, s, l), 'triadic': self._get_triadic(h, s, l), 'split_complementary': self._get_split_complementary(h, s, l) } def _get_analogous(self, h, s, l): # the magic numbers that make designers happy return [ self._hsl_to_hex((h - 30) % 360, s, l), self._hsl_to_hex(h, s, l), self._hsl_to_hex((h + 30) % 360, s, l) ]
无障碍
最令人大开眼界的是色盲用户提交的反馈。我完全忽略了可访问性!这促使我实现色盲模拟:
def simulate_color_blindness(self, color, type='protanopia'): """ this feature wasn't in my original plan, but it became one of the most important parts of colorify rocks """ matrices = { 'protanopia': [ [0.567, 0.433, 0], [0.558, 0.442, 0], [0, 0.242, 0.758] ], # added more types after learning about different forms of color blindness 'deuteranopia': [ [0.625, 0.375, 0], [0.7, 0.3, 0], [0, 0.3, 0.7] ] } # matrix multiplication that makes sure everyone can use our color palettes return self._apply_color_matrix(color, matrices[type])
随着 colorify rocks 的发展,设计师开始要求更多功能。大的?颜色的色调和色调。这导致了一些有趣的实验:
def get_color_variations(self, color, steps=10): """ This started as a simple feature request and turned into one of our most-used tools """ return { 'shades': self._generate_shades(color, steps), 'tints': self._generate_tints(color, steps), 'tones': self._generate_tones(color, steps) }