Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

可以改进给题目类别随机分配颜色的算法 #93

Open
xmcp opened this issue May 14, 2021 · 5 comments
Open

可以改进给题目类别随机分配颜色的算法 #93

xmcp opened this issue May 14, 2021 · 5 comments

Comments

@xmcp
Copy link

xmcp commented May 14, 2021

目前hackergame系统给类别分配颜色的途径是 随机生成一个颜色

这样会有两个问题,其一是相邻的类别有几率变成很相似的颜色,难以区分;其二是生成的颜色亮度不均匀,对易读性不友好。

https://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/ 提出可以在 HSV 色彩空间上生成颜色,固定明度和饱和度,每生成一种颜色把相位向前移动 $0.618 * 2\pi$。这样的话,每种颜色之间都有比较大的区分度,亮度也比较均匀。

CSS 没有原生支持 HSV,但我们可以用类似的 HSL。比如,如果令第 i 种类别的颜色是

`hsl(${360 * ((.2 + 0.618*i)%1)}deg, 50%, 35%)`

可以得到这样的效果:

image

这样不仅看起来比较好看,而且省去了现在的 color.js 和 seedrandom.js 两个依赖。

@SmartHypercube
Copy link
Member

当前的生成算法是有意设计的,采用了 CIELUV 色彩空间。HSV 或 HSL 色彩空间没有考虑人眼对色彩的感知,固定 L 分量生成的颜色的亮度并不是相同的。color.js 依赖就是用于计算 CIELUV 色彩空间与 sRGB 色彩空间转换的。

之所以没采用把亮度固定为常数的做法,而是在 0% ~ 50% 随机生成,是因为同一个视觉感受亮度下存在的颜色实在太少了,无论如何生成都不会显得丰富。下图展示了将视觉感受亮度(L* 分量)固定在 35% 时,可以使用的所有颜色(横纵轴分别是 u* 和 v* 分量):
image
如果像示例中那样,不仅固定亮度,还固定饱和度,可用的颜色就更有限了(相当于图上一个以中心为圆心的圆)。

另外,红色盲用户感知不到 u* 分量,如果 L* 分量被固定,将只剩下 v* 这一个维度,颜色更单调。将 L* 分量也随机生成,可以让生成的颜色较为丰富。

用类别名称作为种子,用确定性伪随机数来生成颜色,是因为不然的话类别序号 i 没有好的定义。确定性生成算法有诸多好处,比如不同部署实例上相同名字的类型颜色相同,测试环境和生产环境效果相同,即使删除后重新添加了类别,或者进行了其他调整,也不会导致选手们发现“类别的颜色怎么突然变了”,也不会泄露后台信息(选手看到类别的颜色只有 i = 2 3 4 的,就可以猜测还有一个隐藏的类别 i=1,不知为何没有出现)。

@xmcp
Copy link
Author

xmcp commented May 14, 2021

HSV 或 HSL 色彩空间没有考虑人眼对色彩的感知

这个确实。我使用用 HSL 的原因是它观感可以接受(L 分量不在 50% 附近的时候感知到的亮度差一点问题不大,比如上图的例子,并不存在易读性问题),且编码实现容易(CSS 原生支持)。

同一个视觉感受亮度下存在的颜色实在太少了,无论如何生成都不会显得丰富

其实颜色还挺丰富的,例如网页上这个给出的例子(下图),至少生成十个左右都没什么问题,用来做题目类型绝对够了。而且每次前进 0.618 圈确保了相邻的几个颜色一定相差较大,相隔很远的颜色比较相近也不太容易发现。
image

另外在 0 到 50 之间随机亮度还可能导致一些对比度更高的类别比其他类别更 “显眼”。这在视觉设计上比较奇怪,相当于把语义相同的badge赋予了不同的优先级。

红色盲用户感知不到 u* 分量,如果 L* 分量被固定,将只剩下 v* 这一个维度,颜色更单调

拿红色盲滤镜套了一下,可以看到因为它每次移动了 0.618 圈,虽然一个分量被固定,这个算法在这种计算的情况下还是能做到相邻的颜色区别较大的(间隔2个颜色的Misc和Algorithm颜色相近了,比较遗憾,但原来的算法也会出现个别颜色相近的情况,这个无法完全避免)。
image

另外关于视觉障碍用户的使用体验问题:根据上图chrome那个色彩滤镜的效果,我没有感觉到体验有太大的问题(况且颜色显示并非平台核心功能)。不知道真实用户的感受如何。

确定性伪随机数来生成颜色,是因为不然的话类别序号 i 没有好的定义

目前我的做法是前端按在题目列表中的展示顺序排列,第一个出现的类别 i 为 0,以此类推,这样才能带来“相邻两个颜色一定差别较大”的效果。
确实,如果调整题目顺序,颜色就会变化。但考虑到目前hackergame并没有为类型的颜色赋予语义,而且调整题目顺序并不经常发生,我没有觉得这是一个很大的问题。


总之我提出的这个方法,主要是想解决两个比较影响体验的地方

  1. 原来badge的对比度忽低忽高,导致一些类别看起来比其他类别更显眼
  2. 相邻的颜色有时很像、难以区分

此方法确实没有 “同类别的颜色一定固定”、“对视觉障碍用户友好” 这些特性。个人感觉这些问题并不严重,不知道能不能再改进一下

@xmcp
Copy link
Author

xmcp commented May 14, 2021

(其实只要把 L* 的范围限制在一个比较小的区间内,然后在生成颜色时考虑一下相邻颜色不要太接近,也可以在不抛弃 CIELUV 的前提下解决上面两条)

@SmartHypercube
Copy link
Member

另外在 0 到 50 之间随机亮度还可能导致一些对比度更高的类别比其他类别更 “显眼”。这在视觉设计上比较奇怪,相当于把语义相同的badge赋予了不同的优先级。

我倒是认为这种标签完全可以亮度和对比度不同。其实 HSL 生成的结果(原文的那个最终结果图)看起来亮度和对比度就是不同的,有一些标签明显比别的更“突出”,但五颜六色的感觉还不错啊。另一个参考:GitHub 上 issue labels 的颜色,默认用户头像的颜色,以及其他一些这样随机生成颜色的系统,其实也不固定亮度和对比度的。

而且调整题目顺序并不经常发生

啊这,科大的 hackergame 虽然尽了很大努力不在题目公开后调整顺序或增减题目,但这个项目希望尽量写得通用一些,不要假定这种情况不会发生。目前这个项目的一个 fork 就被用来提供一个长期存在的网站,会不断调整题目和分类的。

所以,如果类别名能被事先确定,保证不增减,并且可以枚举,整个问题都很好办。现在的难点主要还是来自于上游这个代码要写成通用的,应当可以妥善应对各种情况。在这个前提下我感觉选择很少。。。建议下游如果符合“类别名可以确定 + 可以枚举”这个使用场景的话,自行把 JS 中“类别名 -> 颜色”的算法改成一个 switch 语句(或者查表)即可😂

@zzh1996
Copy link
Member

zzh1996 commented May 14, 2021

我觉得这个问题不需要大家讨论出一致的观点,题主可以提一个 PR,把色彩改成可以配置的,一个配置选项是使用哪种色彩空间,另一个配置选项是按顺序分配颜色还是按题目名称的哈希分配颜色。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants