UNPKG

color-delta-e

Version:

![npm](https://img.shields.io/npm/v/color-delta-e?color=crimson&label=latest&logo=npm&style=flat-square) ![npm bundle size](https://img.shields.io/bundlephobia/min/color-delta-e?logo=npm&label=min&style=flat-square) ![npm bundle size](https://img.shields.

281 lines (273 loc) 9 kB
import { isArray, isString } from 'yewtils'; var __pow = Math.pow; function convertHexToTuple(h) { let r = 0; let g = 0; let b = 0; if (h.length === 4) { r = `0x${h[1]}${h[1]}`; g = `0x${h[2]}${h[2]}`; b = `0x${h[3]}${h[3]}`; } else if (h.length === 7) { r = `0x${h[1]}${h[2]}`; g = `0x${h[3]}${h[4]}`; b = `0x${h[5]}${h[6]}`; } return [parseInt(r), parseInt(g), parseInt(b)]; } function convertRGBToLAB(rgb) { let r = rgb[0] / 255; let g = rgb[1] / 255; let b = rgb[2] / 255; let x; let y; let z; r = r > 0.04045 ? __pow((r + 0.055) / 1.055, 2.4) : r / 12.92; g = g > 0.04045 ? __pow((g + 0.055) / 1.055, 2.4) : g / 12.92; b = b > 0.04045 ? __pow((b + 0.055) / 1.055, 2.4) : b / 12.92; x = (r * 0.4124 + g * 0.3576 + b * 0.1805) / 0.95047; y = (r * 0.2126 + g * 0.7152 + b * 0.0722) / 1; z = (r * 0.0193 + g * 0.1192 + b * 0.9505) / 1.08883; x = x > 8856e-6 ? __pow(x, 1 / 3) : 7.787 * x + 16 / 116; y = y > 8856e-6 ? __pow(y, 1 / 3) : 7.787 * y + 16 / 116; z = z > 8856e-6 ? __pow(z, 1 / 3) : 7.787 * z + 16 / 116; return [116 * y - 16, 500 * (x - y), 200 * (y - z)]; } function toRBGString([r, g, b]) { return `rgb(${r},${g},${b})`; } function toHSLString([h, s, l]) { return `hsl(${h},${s},${l})`; } const convertStringToTuple = (color) => { const r = color.match(/\d+/g); if (!r) return [0, 0, 0]; return r; }; const convertHSLtoTuple = (color) => { let [h, s, l] = convertStringToTuple(color); s /= 100; l /= 100; if (color.includes("rad")) h = Math.round(h * (180 / Math.PI)); else if (color.includes("turn")) h = Math.round(h * 360); if (h >= 360) h %= 360; const c = (1 - Math.abs(2 * l - 1)) * s; const x = c * (1 - Math.abs(h / 60 % 2 - 1)); const m = l - c / 2; let r = 0; let g = 0; let b = 0; if (h >= 0 && h < 60) { r = c; g = x; b = 0; } else if (h >= 60 && h < 120) { r = x; g = c; b = 0; } else if (h >= 120 && h < 180) { r = 0; g = c; b = x; } else if (h >= 180 && h < 240) { r = 0; g = x; b = c; } else if (h >= 240 && h < 300) { r = x; g = 0; b = c; } else if (h >= 300 && h < 360) { r = c; g = 0; b = x; } r = Math.round((r + m) * 255); g = Math.round((g + m) * 255); b = Math.round((b + m) * 255); return [r, g, b]; }; const stringConverstionMap = { rgb: convertStringToTuple, hex: convertHexToTuple, hsl: convertHSLtoTuple, lab: convertStringToTuple }; const tupleConverstionMap = { rgb: (color) => convertRGBToLAB(color), hsl: (color) => convertRGBToLAB(stringConverstionMap.hsl(toHSLString(color))), lab: (color) => color }; function getType(color) { if (color.slice(0, 3).includes("rgb")) return "rgb"; if (color.slice(0, 3).includes("hsl")) return "hsl"; if (color.slice(0, 3).includes("#")) return "hex"; if (color.slice(0, 3).includes("lab")) return "lab"; return "rgb"; } function getStringColorConvertion(color) { const type = getType(color); const rgbTuple = stringConverstionMap[type](color); return getTupleColorConvertion(rgbTuple, "rgb"); } function getTupleColorConvertion(color, type) { const res = tupleConverstionMap[type](color); return res; } function isColorTuple(possibleColorTuple) { return isArray(possibleColorTuple) && possibleColorTuple.length === 3; } const deltaCache = /* @__PURE__ */ new Map(); function clearCache() { deltaCache.clear(); } function deltaE(color1, color2, type, nocache) { if (isString(color1)) color1 = getStringColorConvertion(color1); else if (isColorTuple(color1)) color1 = getTupleColorConvertion(color1, type || "rgb"); else throw new Error(`${color1} type could not be infered if is string, otherwise type has not been provided as an option if passing a tuple`); if (isString(color2)) color2 = getStringColorConvertion(color2); else if (isColorTuple(color2)) color2 = getTupleColorConvertion(color2, type || "rgb"); else throw new Error(`${color2} type could not be infered if is string, otherwise type has not been provided as an option `); if (!nocache) { const value = deltaCache.get(JSON.stringify([color1, color2])); if (value) return value; } if (!isColorTuple(color1) || !isColorTuple(color2)) throw new Error(`colors: ${color1} and ${color2} could type could not be infered or converted`); const labA = color1; const labB = color2; const deltaL = labA[0] - labB[0]; const deltaA = labA[1] - labB[1]; const deltaB = labA[2] - labB[2]; const c1 = Math.sqrt(labA[1] * labA[1] + labA[2] * labA[2]); const c2 = Math.sqrt(labB[1] * labB[1] + labB[2] * labB[2]); const deltaC = c1 - c2; let deltaH = deltaA * deltaA + deltaB * deltaB - deltaC * deltaC; deltaH = deltaH < 0 ? 0 : Math.sqrt(deltaH); const sc = 1 + 0.045 * c1; const sh = 1 + 0.015 * c1; const deltaLKlsl = deltaL / 1; const deltaCkcsc = deltaC / sc; const deltaHkhsh = deltaH / sh; const i = deltaLKlsl * deltaLKlsl + deltaCkcsc * deltaCkcsc + deltaHkhsh * deltaHkhsh; const result = i < 0 ? 0 : Math.sqrt(i); if (!nocache) deltaCache.set(JSON.stringify([color1, color2]), result); return result; } function isPerceivable(first, second, options) { const threshold = (options == null ? void 0 : options.threshold) || 5; const type = (options == null ? void 0 : options.type) || "rgb"; return Math.round(deltaE(first, second, type, options == null ? void 0 : options.nocache)) > threshold; } const colorReturnMapsString = { rgb: ["rgb(0, 0, 0)", "rgb(255, 255, 255)"], hsl: ["hsl(0, 0%, 0%)", "hsl(0, 100%, 100%)"], hex: ["#000", "#fff"], lab: ["#000", "#fff"] }; const colorReturnMapsTuples = { rgb: [[0, 0, 0], [255, 255, 255]], hsl: [[0, 0, 0], [0, 100, 100]], lab: [[0, 0, 0], [0, 0, 0]] }; const maps = { string: colorReturnMapsString, array: colorReturnMapsTuples }; function contrastText(color, type) { if (type === void 0 && isColorTuple(color)) throw new Error("No color space type passed contrastText when tuple is passed"); type = isColorTuple(color) ? type : getType(color); if (!type) throw new Error("Could not infer color space type inside contrastText"); const m = maps[isString(color) ? "string" : "array"]; return m[type][+isPerceivable(color, colorReturnMapsString[type][1], { type, threshold: 50 })]; } function sampleImage(imgEl) { var _a; if (!((_a = window == null ? void 0 : window.document) == null ? void 0 : _a.createElement)) { console.error("[color-delta-e]: sampleImage can only run in browser as it needs access to HTMLCanvas API, please check your runtime environment"); return [0, 0, 0]; } const blockSize = 5; const defaultRGB = { r: 0, g: 0, b: 0 }; const canvas = document.createElement("canvas"); const context = canvas.getContext && canvas.getContext("2d"); let data; let i = -4; const rgb = { r: 0, g: 0, b: 0 }; let count = 0; if (!context) return [defaultRGB.r, defaultRGB.g, defaultRGB.b]; const height = canvas.height = imgEl.naturalHeight || imgEl.offsetHeight || imgEl.height; const width = canvas.width = imgEl.naturalWidth || imgEl.offsetWidth || imgEl.width; context.drawImage(imgEl, 0, 0); try { data = context.getImageData(0, 0, width, height); } catch (e) { console.error(e); return [defaultRGB.r, defaultRGB.g, defaultRGB.b]; } const length = data.data.length; while ((i += blockSize * 4) < length) { ++count; rgb.r += data.data[i]; rgb.g += data.data[i + 1]; rgb.b += data.data[i + 2]; } rgb.r = ~~(rgb.r / count); rgb.g = ~~(rgb.g / count); rgb.b = ~~(rgb.b / count); return [rgb.r, rgb.g, rgb.b]; } var __defProp = Object.defineProperty; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; const defaultOptions = { type: "rgb", threshold: 5 }; function selector(options, ...fallbacks) { let { compare, threshold, type } = __spreadValues(__spreadValues({}, defaultOptions), options); function filter(...filters) { if (filters.length > 1) { if (!isPerceivable(compare, filters[0], { threshold, type })) return selector(options, ...filters.slice(1)); else return filters[0]; } else { return filters[0]; } } return filter(...fallbacks); } export { clearCache, contrastText, deltaE, isPerceivable, sampleImage, selector, toHSLString, toRBGString };