UNPKG

color-sorter

Version:
159 lines (158 loc) 4.17 kB
import { A98RGB, A98RGB_Linear, ColorSpace, HSL, HSV, HWB, LCH, Lab, Lab_D65, OKLCH, OKLab, OKLrab, P3, P3_Linear, ProPhoto, ProPhoto_Linear, REC_2020, REC_2020_Linear, XYZ_ABS_D65, XYZ_D50, XYZ_D65, sRGB, sRGB_Linear, to, tryColor } from "colorjs.io/fn"; //#region index.ts ColorSpace.register(sRGB); ColorSpace.register(XYZ_D65); ColorSpace.register(XYZ_D50); ColorSpace.register(XYZ_ABS_D65); ColorSpace.register(Lab_D65); ColorSpace.register(Lab); ColorSpace.register(LCH); ColorSpace.register(sRGB_Linear); ColorSpace.register(HSL); ColorSpace.register(HWB); ColorSpace.register(HSV); ColorSpace.register(P3_Linear); ColorSpace.register(P3); ColorSpace.register(A98RGB_Linear); ColorSpace.register(A98RGB); ColorSpace.register(ProPhoto_Linear); ColorSpace.register(ProPhoto); ColorSpace.register(REC_2020_Linear); ColorSpace.register(REC_2020); ColorSpace.register(OKLab); ColorSpace.register(OKLCH); ColorSpace.register(OKLrab); function numerify(value) { if (typeof value === "number" && Number.isFinite(value)) return value; return 0; } /** * Convert a CSS (string) color into a normalized HSL color that can be used for comparison * @example convert('red') */ function convert(authored) { let parsed = tryColor(authored); if (parsed === null) return { hue: 0, saturation: 0, lightness: 0, alpha: 1, authored }; let converted = parsed.space.id === "hsl" ? parsed : to(parsed, HSL); let hsl = converted.coords; return { hue: numerify(hsl[0]), saturation: numerify(hsl[1]), lightness: numerify(hsl[2]), alpha: numerify(converted.alpha), authored }; } const RED = 0; const ORANGE = 1; const BROWN = 2; const YELLOW = 3; const GREEN = 4; const CYAN = 5; const BLUE = 6; const VIOLET = 7; const MAGENTA = 8; const PINK = 9; const GRAY = 10; const COLOR_GROUP_NAMES = [ "red", "orange", "brown", "yellow", "green", "cyan", "blue", "violet", "magenta", "pink", "gray" ]; /** * Get the color's group name, like "red", "green" or "gray" * * NB: heavily relies on magic numbers. All done by eye so will probably need tweaking from time to time. */ function _color_group(color) { let { hue, saturation, lightness } = color; if (saturation < 10) return GRAY; if (hue < 15 || hue >= 345) return RED; if (hue < 47) { if (lightness > 37 && saturation > .5) return ORANGE; if (saturation < 25 && lightness < 15) return GRAY; return BROWN; } if (hue < 65) { if (saturation < 25 && lightness < 15) return GRAY; if (saturation < 70 && lightness < 80) return BROWN; if (lightness < 30) return GREEN; return YELLOW; } if (hue < 164) return GREEN; if (hue < 194) { if (saturation < 20) return GRAY; return CYAN; } if (hue < 241) { if (saturation < 19) return GRAY; if (saturation > 55 && lightness > 59 && hue > 234) return VIOLET; return BLUE; } if (hue < 271) return VIOLET; if (hue < 327) return MAGENTA; return PINK; } /** * Get the group name of a color * @example * ```ts * const color = convert('rgb(255 0 0)') * const group = color_group(color) // => 'red' * ``` */ function color_group(color) { return COLOR_GROUP_NAMES[_color_group(color)]; } const hue_gaps = { [BLUE]: 48, [GREEN]: 36, [RED]: 0, [GRAY]: 0 }; const default_hue_gap = 24; const collator = new Intl.Collator("en-US", { caseFirst: "upper", sensitivity: "base" }); function sort_group_fn(a, b, group) { const hue_gap = hue_gaps[group] ?? default_hue_gap; if (Math.abs(a.hue - b.hue) < hue_gap) { const diff = b.lightness - a.lightness; if (diff !== 0) return diff; } else if (a.lightness !== b.lightness) return b.lightness - a.lightness; if (a.alpha !== b.alpha) return b.alpha - a.alpha; return collator.compare(a.authored, b.authored); } /** * Compare two colors to determine which one is sorted first. */ function compare(a, b) { let group_a = _color_group(a); let group_b = _color_group(b); if (group_a === group_b) return sort_group_fn(a, b, group_a); return group_a - group_b; } /** * Function that sorts colors * @example ['red', 'yellow'].sort(sort_fn) */ function sort_fn(a, b) { return compare(convert(a), convert(b)); } //#endregion export { COLOR_GROUP_NAMES, color_group, compare, convert, sort_fn };