class ColorTool {
rgba = {
r: 0,
g: 0,
b: 0,
a: 1
};
hsb = {
h: 0,
s: 100,
b: 100
};
// 构造函数
constructor(color,colorPanel, Discoloration, callback) {
this.callbackConstructor(color,colorPanel, Discoloration, callback)
}
#hsbToRgb(hsb) {
// 归一化输入值
const H = (hsb.h % 360) / 60; // 色相: 0-360° → 0-6
const S = Math.max(0, Math.min(1, hsb.s / 100)); // 饱和度: 0-100% → 0-1
const B = Math.max(0, Math.min(1, hsb.b / 100)); // 亮度: 0-100% → 0-1
// 核心转换算法
const C = B * S; // 色度
const X = C * (1 - Math.abs((H % 2) - 1));
const m = B - C; // 明度调整值
let r, g, b;
// 根据色相扇区计算临时RGB值
if (H >= 0 && H < 1) [r, g, b] = [C, X, 0];
else if (H < 2) [r, g, b] = [X, C, 0];
else if (H < 3) [r, g, b] = [0, C, X];
else if (H < 4) [r, g, b] = [0, X, C];
else if (H < 5) [r, g, b] = [X, 0, C];
else[r, g, b] = [C, 0, X];
// 调整明度并转换到0-255范围
return {
r: Math.round((r + m) * 255),
g: Math.round((g + m) * 255),
b: Math.round((b + m) * 255)
};
}
#rgbToHex(rgb) {
return [rgb.r, rgb.g, rgb.b]
.map(c => c.toString(16).padStart(2, '0'))
.join('');
}
#hexToRgb(hex) {
// 移除#号并统一处理短格式
let hexStr = hex.startsWith('#') ? hex.slice(1) : hex;
if ([3, 4].includes(hexStr.length)) {
hexStr = hexStr.replace(/./g, '$&$&').substring(0, 6);
}
// 解析并验证十六进制值
const num = parseInt(hexStr, 16);
if (isNaN(num) || hexStr.length < 6) {
return { r: 0, g: 0, b: 0 }; // 非法输入返回黑色
}
// 位运算提取RGB通道
return {
r: (num >> 16) & 0xFF, // 确保高8位
g: (num >> 8) & 0xFF, // 确保中8位
b: num & 0xFF // 确保低8位
};
}
#hexToHsb(hex) {
return this.#rgbToHsb(this.#hexToRgb(hex));
}
#rgbToHsb(rgb) {
const max = Math.max(rgb.r, rgb.g, rgb.b);
const min = Math.min(rgb.r, rgb.g, rgb.b);
const diff = max - min;
const b = max * (100 / 255); // 明度
// 灰度色处理 (diff=0)
if (diff < 1e-5) {
return { h: 0, s: 0, b: b };
}
const s = (diff / max) * 100; // 饱和度
const segment = 60 / diff; // 预计算公共因子
// 计算色相核心值
let h;
if (max === rgb.r) {
h = (rgb.g - rgb.b) * segment;
} else if (max === rgb.g) {
h = (rgb.b - rgb.r) * segment + 120;
} else {
h = (rgb.r - rgb.g) * segment + 240;
}
// 规范化色相到 [0, 360)
h = (h + 360) % 360;
return { h, s, b };
}
#rgbToHsl(rgb) {
const RGB_NORM_FACTOR = 1 / 255; // 归一化因子
// 归一化处理
const rn = rgb.r * RGB_NORM_FACTOR;
const gn = rgb.g * RGB_NORM_FACTOR;
const bn = rgb.b * RGB_NORM_FACTOR;
// 计算极值和差值
const max = Math.max(rn, gn, bn);
const min = Math.min(rn, gn, bn);
const delta = max - min;
// 明度计算
const l = (max + min) * 50; // 直接映射到 [0,100]
// 灰度色处理 (delta ≈ 0)
if (delta < 1e-5) {
return { h: 0, s: 0, l };
}
// 饱和度计算 (优化分支)
const s = delta / (1 - Math.abs(2 * (max + min) / 2 - 1)) * 100;
// 色相计算 (减少条件判断)
let h;
const segment = 60 / delta; // 预计算公共因子
if (max === rn) {
h = ((gn - bn) * segment + 360) % 360;
} else if (max === gn) {
h = (bn - rn) * segment + 120;
} else {
h = (rn - gn) * segment + 240;
}
return { h, s, l };
}
#hslToRgb(h = 0, s = 0, l = 0) {
// 归一化处理
s /= 100;
l /= 100;
// 归一化色相到 [0,360)
h = (h % 360 + 360) % 360;
// 计算中间变量(优化重复计算)
const chroma = (1 - Math.abs(2 * l - 1)) * s;
const x = chroma * (1 - Math.abs((h / 60) % 2 - 1));
const m = l - chroma / 2;
let r, g, b;
// 优化分支逻辑(减少条件判断)
const segment = Math.floor(h / 60);
switch (segment) {
case 0: [r, g, b] = [chroma, x, 0]; break;
case 1: [r, g, b] = [x, chroma, 0]; break;
case 2: [r, g, b] = [0, chroma, x]; break;
case 3: [r, g, b] = [0, x, chroma]; break;
case 4: [r, g, b] = [x, 0, chroma]; break;
default: [r, g, b] = [chroma, 0, x]; // 覆盖 [300,360)
}
// 转换为RGB并四舍五入
return {
r: Math.round(255 * (r + m)),
g: Math.round(255 * (g + m)),
b: Math.round(255 * (b + m))
};
}
#changeColor() {
// 1. 合并转换操作:避免多次访问this.hsb
const rgb = this.#hsbToRgb(this.hsb);
// 2. 使用单次赋值更新rgba对象
Object.assign(this.rgba, rgb);
// 3. 缓存转换结果避免重复计算
const hex = `#${this.#rgbToHex(rgb)}`;
const hsl = this.#rgbToHsl(rgb);
return {
Hex: hex,
Rgb: { ...rgb }, // 返回副本防止外部修改
Hsl: hsl
};
}
PanelCalculation(x, y) {
// 1. 参数处理优化:使用一元加运算符和默认值
const clampedX = Math.min(255, Math.max(0, +(x || 0)))
const clampedY = Math.min(255, Math.max(0, +(y || 0)))
// 2. 避免重复计算:使用常量缓存计算结果
const MAX_VAL = 255
const SCALE_FACTOR = 100 / MAX_VAL
const reversedY = MAX_VAL - clampedY
// 3. 数值计算优化:减少浮点运算次数
this.hsb = this.hsb || { h: 0, s: 0, b: 0 }
const hsbS = clampedX * SCALE_FACTOR
const hsbB = reversedY * SCALE_FACTOR
this.hsb.s = hsbS < 0 ? 0 : hsbS > 100 ? 100 : hsbS
this.hsb.b = hsbB < 0 ? 0 : hsbB > 100 ? 100 : hsbB
// 4. 返回转换结果
return this.#changeColor()
}
BarCalculation(x, DiscolorationWidth, callback) {
// 1. 参数类型转换与边界处理
x = Math.max(0, Math.min(Number(x) || 0, DiscolorationWidth));
DiscolorationWidth = Math.max(1, Number(DiscolorationWidth) || 225); // 防止除零错误
// 2. 色相计算(保留1位小数)
const hue = parseFloat(((x / DiscolorationWidth) * 360).toFixed(1));
// 3. 更新HSB模型
this.hsb = this.hsb || { h: 0, s: 0, b: 0 };
this.hsb.h = hue % 360; // 色相循环处理 $ H \in [0,360) $
// 4. 异步回调处理
if (typeof callback === 'function') {
// 使用微任务异步执行回调
Promise.resolve().then(() => {
callback(this.#hsbToRgb({
h: this.hsb.h,
s: 100,
b: 100
}), this.#changeColor());
});
}
// rgba.a = X / elem_width;//调透明度的
}
calculateDistance(hsb,colorPanel, Discoloration) {
// 1. 参数校验与预处理
const DiscolorationWidth = Math.max(1, Number(Discoloration.clientWidth) || 255); // 防止除零错误
const { h = 0, s = 0, b = 0 } = hsb || {};
// 2. 计算色条滑块位置 $$ \text{barSliderLeft} = \frac{h \times \text{Discoloration}}{360} $$
const barSliderLeft = Math.max(0, Math.min(
(h / 360) * DiscolorationWidth,
DiscolorationWidth
));
// 3. 获取面板尺寸(避免重复DOM查询)
const panelWidth = colorPanel.offsetWidth;
const panelHeight = colorPanel.offsetHeight;
// 4. 计算面板滑块位置
const panelSliderLeft = Math.max(0, Math.min(
s * 255 / 100,
panelWidth
));
const panelSliderTop = Math.max(0, Math.min(
(100 - b) * 255 / 100,
panelHeight
));
// 5. 颜色转换(使用缓存机制)
const rgb = this.#changeColor().Rgb;
// 数值格式化(减少重复计算)
const format = val => parseFloat(val.toFixed(2));
return {
panelSliderTop: panelSliderTop,
panelSliderLeft: panelSliderLeft,
barSliderLeft: Math.round(barSliderLeft),
hsbValue: {
h: format(h),
s: format(s),
b: format(b)
},
rgb
};
}
callbackConstructor(color,colorPanel, Discoloration, callback) {
if (typeof callback === 'function') {
// 使用微任务异步执行回调
Promise.resolve().then(() => {
callback(
this.#changeColor(),
this.calculateDistance(this.#hexToHsb(color), colorPanel, Discoloration)
);
});
}
}
}
这是我的全篇代码
最新发布