增加颜色生成器类

This commit is contained in:
hym 2025-11-21 22:55:02 +08:00
parent cd3774770e
commit e2e3dd6952
1 changed files with 185 additions and 0 deletions

View File

@ -0,0 +1,185 @@
export class ColorGenerator {
// 生成适合地图显示的区分度高的颜色
static generateDistinctColors(count) {
const colors = [];
for (let i = 0; i < count; i++) {
// 使用黄金角分布算法,确保颜色分布均匀
const hue = (i * 137.5) % 360; // 黄金角约等于 137.5 度
const saturation = 70 + Math.random() * 20; // 70-90% 饱和度
const lightness = 45 + Math.random() * 20; // 45-65% 亮度
const hsl = { h: hue, s: saturation, l: lightness };
colors.push(HSLColorGenerator.toCSS(hsl));
}
return colors;
}
// 生成分类数据颜色(确保相邻类别颜色差异明显)
static generateCategoricalColors(count, startHue = 0) {
const hueStep = 360 / count;
const colors = [];
for (let i = 0; i < count; i++) {
const hue = (startHue + i * hueStep) % 360;
const hsl = {
h: hue,
s: 80 + Math.random() * 15, // 80-95% 饱和度
l: 50 + Math.random() * 15 // 50-65% 亮度
};
colors.push(HSLColorGenerator.toCSS(hsl));
}
return colors;
}
// 生成连续数据颜色(渐变)
static generateSequentialColors(
count,
startColor = '#f7fbff',
endColor = '#08306b'
) {
const colors = [];
// 解析起始颜色和结束颜色
const startRGB = this.parseHexColor(startColor);
const endRGB = this.parseHexColor(endColor);
for (let i = 0; i < count; i++) {
const ratio = i / (count - 1);
const r = Math.round(startRGB.r + (endRGB.r - startRGB.r) * ratio);
const g = Math.round(startRGB.g + (endRGB.g - startRGB.g) * ratio);
const b = Math.round(startRGB.b + (endRGB.b - startRGB.b) * ratio);
colors.push(`#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`);
}
return colors;
}
// 生成发散颜色(用于有正负值的数据)
static generateDivergingColors(
count,
negativeColor = '#d73027',
neutralColor = '#ffffbf',
positiveColor = '#1a9850'
) {
const colors = [];
const midPoint = Math.floor(count / 2);
// 负值部分
const negRGB = this.parseHexColor(negativeColor);
const neutralRGB = this.parseHexColor(neutralColor);
for (let i = 0; i < midPoint; i++) {
const ratio = i / midPoint;
const r = Math.round(negRGB.r + (neutralRGB.r - negRGB.r) * ratio);
const g = Math.round(negRGB.g + (neutralRGB.g - negRGB.g) * ratio);
const b = Math.round(negRGB.b + (neutralRGB.b - negRGB.b) * ratio);
colors.push(`#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`);
}
// 正值部分
const positiveRGB = this.parseHexColor(positiveColor);
for (let i = midPoint; i < count; i++) {
const ratio = (i - midPoint) / (count - midPoint - 1);
const r = Math.round(neutralRGB.r + (positiveRGB.r - neutralRGB.r) * ratio);
const g = Math.round(neutralRGB.g + (positiveRGB.g - neutralRGB.g) * ratio);
const b = Math.round(neutralRGB.b + (positiveRGB.b - neutralRGB.b) * ratio);
colors.push(`#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`);
}
return colors;
}
// 解析十六进制颜色
static parseHexColor(hex) {
hex = hex.replace('#', '');
if (hex.length === 3) {
hex = hex.split('').map(c => c + c).join('');
}
return {
r: parseInt(hex.substring(0, 2), 16),
g: parseInt(hex.substring(2, 4), 16),
b: parseInt(hex.substring(4, 6), 16)
};
}
}
export class HSLColorGenerator {
// 随机 HSL 颜色
static randomHSL() {
return {
h: Math.floor(Math.random() * 360),
s: Math.floor(Math.random() * 101),
l: Math.floor(Math.random() * 101)
};
}
// 转换为 CSS 字符串
static toCSS(color) {
if (color.alpha !== undefined) {
return `hsla(${color.h}, ${color.s}%, ${color.l}%, ${color.alpha})`;
}
return `hsl(${color.h}, ${color.s}%, ${color.l}%)`;
}
// 生成鲜艳的颜色(高饱和度)
static randomVibrantHSL(minSaturation = 80, minLightness = 40) {
return {
h: Math.floor(Math.random() * 360),
s: minSaturation + Math.floor(Math.random() * (101 - minSaturation)),
l: minLightness + Math.floor(Math.random() * (61 - minLightness))
};
}
// 生成柔和的颜色(低饱和度)
static randomPastelHSL(maxSaturation = 40, minLightness = 70) {
return {
h: Math.floor(Math.random() * 360),
s: Math.floor(Math.random() * (maxSaturation + 1)),
l: minLightness + Math.floor(Math.random() * (101 - minLightness))
};
}
// 转换为 RGB
static toRGB(hsl) {
const h = hsl.h / 360;
const s = hsl.s / 100;
const l = hsl.l / 100;
let r, g, b;
if (s === 0) {
r = g = b = l;
} else {
const hue2rgb = (p, q, t) => {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
};
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
return {
r: Math.round(r * 255),
g: Math.round(g * 255),
b: Math.round(b * 255),
alpha: hsl.alpha
};
}
}