UNPKG

contra-color

Version:

For a given color, find a contrasting color (not optimal).

149 lines (145 loc) 5.39 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var RE_HEX = /^(?:[0-9a-f]{3}){1,2}$/i; var COLOR_WIDTH = 256; /** Linearize RGB color channel value * @param {number} colorChannel an integer in range [0-255] represent a color channel value * @returns number */ function sRGBtoLin(colorChannel) { colorChannel = colorChannel / 255; if (colorChannel <= 0.04045) { return colorChannel / 12.92; } else { return Math.pow((colorChannel + 0.055) / 1.055, 2.4); } } function luminance(rgb) { var r = sRGBtoLin(rgb[0]); var g = sRGBtoLin(rgb[1]); var b = sRGBtoLin(rgb[2]); return r * 0.2126 + g * 0.7152 + b * 0.0722; } /** thanks to https://github.com/onury/invert-color converts hexadecimal string representing an RBG color to array of numbers * @param {string} hex * @returns RGB */ function hex2RGBArray(hex) { if (hex.slice(0, 1) === "#") { hex = hex.slice(1); } if (!RE_HEX.test(hex)) { throw new Error("Invalid HEX color: \"" + hex + "\""); } // normalize / convert 3-chars hex to 6-chars. if (hex.length === 3) { hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; } return [ parseInt(hex.slice(0, 2), 16), parseInt(hex.slice(2, 4), 16), parseInt(hex.slice(4, 6), 16), // b ]; } /** calculate contrast of 2 given RBG colors * @param {RGB} rgb1 * @param {RGB} rgb2 * @param {} isLinearLuminance=true */ function contrast(rgb1, rgb2, isLinearLuminance) { if (isLinearLuminance === void 0) { isLinearLuminance = true; } var l1 = luminance(rgb1); var l2 = luminance(rgb2); if (!isLinearLuminance) { l1 = Math.pow(l1, 0.425); l2 = Math.pow(l2, 0.425); } if (l1 > l2) { return (l1 + 0.05) / (l2 + 0.05); } return (l2 + 0.05) / (l1 + 0.05); } function padz(str, len) { return (new Array(len).join("0") + str).slice(-len); } function cloneArr(arr) { var r = []; for (var i = 0; i < arr.length; i++) { r.push(arr[i]); } return r; } function getRandomColor() { var r = Math.floor(Math.random() * 255); var g = Math.floor(Math.random() * 255); var b = Math.floor(Math.random() * 255); return [r, g, b]; } function rgb2hex(rgb) { return "#" + rgb.map(function (c) { return padz(c.toString(16), 2); }).join(""); } /** get a contrasting color for a given color. If `isLinearLuminance` is true, it will use the linear luminance formula. Otherwise, it will use power curve. * Set `contrastDiff` as 0 to find color with maximum contrast. If you pass a number in range (0,20], it will find a color with a the smallest contrast that is greater then `contrastDiff` * @param {string} c * @param {} isLinearLuminance=true * @param {} contrastDiff=0 * @returns IContraColor */ function getContrastingColor(c, isLinearLuminance, contrastDiff) { if (isLinearLuminance === void 0) { isLinearLuminance = true; } if (contrastDiff === void 0) { contrastDiff = 0; } var rgb = hex2RGBArray(c); var maxContrast = 0; var greedyResults = [[0, 0, 0], [255, 255, 255], getRandomColor()]; if (contrastDiff > 0) { greedyResults.unshift(hex2RGBArray(c)); } // process from the most important to least important var channelIdxes = [1, 0, 2]; for (var a = 0; a < greedyResults.length; a++) { maxContrast = 0; for (var i = 0; i < channelIdxes.length; i++) { var currChannelIdx = channelIdxes[i]; for (var j = 0; j < COLOR_WIDTH; j++) { var tmp = cloneArr(greedyResults[a]); tmp[currChannelIdx] = j; var currContrast = contrast(rgb, tmp, isLinearLuminance); if (currContrast > maxContrast) { maxContrast = currContrast; greedyResults[a][currChannelIdx] = j; } if (contrastDiff > 0 && currContrast >= contrastDiff && currContrast <= contrastDiff + 0.5) { // early return to find limited contrast difference return { color: rgb2hex(greedyResults[a]), contrast: currContrast }; } } } } var maxOfGreedyResults = [0, 0, 0]; maxContrast = 0; for (var a = 0; a < greedyResults.length; a++) { var currContrast = contrast(rgb, greedyResults[a], isLinearLuminance); if (currContrast > maxContrast) { maxContrast = currContrast; maxOfGreedyResults = greedyResults[a]; } } return { color: rgb2hex(maxOfGreedyResults), contrast: maxContrast }; } /** Find contrast of two colors. If you pass `isLinearLuminance`, it will calculate luminance with power curve. * @param {string} c1 * @param {string} c2 * @param {} isLinearLuminance=true * @returns number */ function getContrast(c1, c2, isLinearLuminance) { if (isLinearLuminance === void 0) { isLinearLuminance = true; } var rgb1 = hex2RGBArray(c1); var rgb2 = hex2RGBArray(c2); return contrast(rgb1, rgb2, isLinearLuminance); } exports.getContrast = getContrast; exports.getContrastingColor = getContrastingColor;