在Vue项目开发中,由于图表上要显示的线较多,需要尽可能地生成一些对比较强的颜色,防止不同的线混杂在一起分辨不清,于是用js写了一个类来实现需求。
主要思想:
利用HSL(HSV也可以)通过色相值决定色彩的结构来实现在一个环上随机取点,并且要尽可能地均匀分布在环上;
如果要进一步地区分颜色,再在明亮度和饱和度上进行一个随机;
但是由于仍然存在随机到相似颜色的可能性,所以要设置好一个随机种子,每次执行随机都是同样的结果。
js的原生随机方法Math.random()并不支持设置种子,因此需要根据线性同余法的原则写一个支持设置种子的随机数生成器。
封装为js类:
export default class LinearCongruentialGenerator {
constructor(seed, a = 1664525, c = 1013904223, m = Math.pow(2, 32)) {
this.seed = seed;
this.a = a;
this.c = c;
this.m = m;
this.current = seed;
}
// 生成下一个随机数
next() {
this.current = (this.a * this.current + this.c) % this.m;
return this.current;
}
// 生成一个在 [0, 1) 范围内的随机数
nextFloat() {
return this.next() / this.m;
}
// 返回当前种子下该索引迭代次数的随机数
randomFloatByIndex(index) {
if(Number.isInteger(index) && index >= 0){
let num = this.seed;
for(let i = 0;i < index + 1;i++){
num = (this.a * num + this.c) % this.m;
}
return num / this.m;
}
else{
throw new Error('invalid random index!');
}
}
}
然后再写一个继承类,基于随机数产生随机颜色:
export default class RandomColorGenerator extends LinearCongruentialGenerator{
constructor(seed = 2, a = 1664525, c = 1013904223, m = Math.pow(2, 32)){
super(seed,a,c,m);
}
// 获取当前种子生成的随机颜色序列中对应索引的颜色
getColor(index){
const hue = (index * 137.5) % 360; // 使用黄金角(137.5度)来均匀分布色相
const saturation = 50 + super.randomFloatByIndex(index) * 50; // 生成饱和度
const lightness = 25 + super.randomFloatByIndex(index) * 50; // 生成亮度
return `hsl(${hue}, ${saturation}%, ${lightness}%)`;
}
// 随机获得一个颜色
getRandomColor(){
const randNum = super.nextFloat();
const hue = (index * 137.5) % 360; // 使用黄金角(137.5度)来均匀分布色相
const saturation = 50 + randNum * 50; // 生成饱和度
const lightness = 25 + randNum * 50; // 生成亮度
return `hsl(${hue}, ${saturation}%, ${lightness}%)`;
}
}
这里绑定默认的种子为2,也支持在创建类时手动输入。
可以通过index找到指定随机迭代次数的结果,也可以直接执行方法获得一个随机颜色。
产出的颜色效果如下图:
区分度还是比较明显的