frontend
This commit is contained in:
@@ -1,5 +1,38 @@
|
||||
const HEX_COLOR_PATTERN = /^#?([0-9a-fA-F]{6})$/;
|
||||
|
||||
const clamp01 = (value) => Math.min(1, Math.max(0, value));
|
||||
|
||||
const gammaEncode = (channel) =>
|
||||
channel <= 0.0031308 ? 12.92 * channel : 1.055 * Math.pow(channel, 1 / 2.4) - 0.055;
|
||||
|
||||
const oklchToHex = (l, c, h) => {
|
||||
const hr = (h * Math.PI) / 180;
|
||||
const a = Math.cos(hr) * c;
|
||||
const b = Math.sin(hr) * c;
|
||||
|
||||
const l1 = l + 0.3963377774 * a + 0.2158037573 * b;
|
||||
const m1 = l - 0.1055613458 * a - 0.0638541728 * b;
|
||||
const s1 = l - 0.0894841775 * a - 1.291485548 * b;
|
||||
|
||||
const l3 = l1 ** 3;
|
||||
const m3 = m1 ** 3;
|
||||
const s3 = s1 ** 3;
|
||||
|
||||
const r = 4.0767416621 * l3 - 3.3077115913 * m3 + 0.2309699292 * s3;
|
||||
const g = -1.2684380046 * l3 + 2.6097574011 * m3 - 0.3413193965 * s3;
|
||||
const bLin = -0.0041960863 * l3 - 0.7034186147 * m3 + 1.707614701 * s3;
|
||||
|
||||
if ([r, g, bLin].some((channel) => channel < 0 || channel > 1)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const sr = Math.round(clamp01(gammaEncode(r)) * 255);
|
||||
const sg = Math.round(clamp01(gammaEncode(g)) * 255);
|
||||
const sb = Math.round(clamp01(gammaEncode(bLin)) * 255);
|
||||
|
||||
return `#${((sr << 16) | (sg << 8) | sb).toString(16).padStart(6, '0')}`;
|
||||
};
|
||||
|
||||
export const hexToRgb = (input) => {
|
||||
if (!input) return null;
|
||||
const match = HEX_COLOR_PATTERN.exec(input.trim());
|
||||
@@ -13,28 +46,49 @@ export const hexToRgb = (input) => {
|
||||
};
|
||||
};
|
||||
|
||||
const relativeLuminance = ({ r, g, b }) => {
|
||||
const transform = (channel) => {
|
||||
export const relativeLuminance = ({ r, g, b }) => {
|
||||
const toLinear = (channel) => {
|
||||
const normalized = channel / 255;
|
||||
return normalized <= 0.03928
|
||||
? normalized / 12.92
|
||||
: ((normalized + 0.055) / 1.055) ** 2.4;
|
||||
};
|
||||
|
||||
const [red, green, blue] = [transform(r), transform(g), transform(b)];
|
||||
const [red, green, blue] = [toLinear(r), toLinear(g), toLinear(b)];
|
||||
return 0.2126 * red + 0.7152 * green + 0.0722 * blue;
|
||||
};
|
||||
|
||||
export const getReadableTextColor = (hex, { light = '#1f1f1f', dark = '#ffffff' } = {}) => {
|
||||
const rgb = hexToRgb(hex);
|
||||
if (!rgb) return dark;
|
||||
const luminance = relativeLuminance(rgb);
|
||||
return luminance > 0.6 ? light : dark;
|
||||
};
|
||||
|
||||
export const getTagColorStyle = (hex) => {
|
||||
const rgb = hexToRgb(hex);
|
||||
if (!rgb) return null;
|
||||
const luminance = relativeLuminance(rgb);
|
||||
const textColor = luminance > 0.6 ? '#1f1f1f' : '#ffffff';
|
||||
return {
|
||||
backgroundColor: rgb.hex,
|
||||
borderColor: rgb.hex,
|
||||
color: textColor,
|
||||
color: getReadableTextColor(rgb.hex),
|
||||
};
|
||||
};
|
||||
|
||||
export const generateRandomTagColor = () => {
|
||||
const lightness = 0.72 + (Math.random() - 0.5) * 0.08;
|
||||
let chroma = 0.8;
|
||||
const hue = Math.random() * 360;
|
||||
|
||||
for (let attempt = 0; attempt < 5; attempt += 1) {
|
||||
const hex = oklchToHex(lightness, chroma, hue);
|
||||
if (hex) {
|
||||
return hex;
|
||||
}
|
||||
chroma *= 0.82;
|
||||
}
|
||||
|
||||
return '#8c8982';
|
||||
};
|
||||
|
||||
export { HEX_COLOR_PATTERN };
|
||||
|
||||
Reference in New Issue
Block a user