Skip to content

Instantly share code, notes, and snippets.

@nparsons08 nparsons08/colorUtils.js Secret forked from LukeSmetham/colorUtils.js
Created Sep 24, 2019

Embed
What would you like to do?
import tinycolor from "tinycolor2";
//
// Adapted from Material UI v0.x
// https://github.com/mui-org/material-ui/tree/v0.x
//
/**
* Returns a number whose value is limited to the given range.
*
* @param {number} value The value to be clamped
* @param {number} min The lower boundary of the output range
* @param {number} max The upper boundary of the output range
* @returns {number} A number in the range [min, max]
*/
function clamp(value, min, max) {
if (value < min) {
return min;
}
if (value > max) {
return max;
}
return value;
}
/**
* Converts a color object with type and values to a string.
*
* @param {object} color - Decomposed color
* @param {string} color.type - One of, 'rgb', 'rgba', 'hsl', 'hsla'
* @param {array} color.values - [n,n,n] or [n,n,n,n]
* @returns {string} A CSS color string
*/
export function convertColorToString(color) {
const { type, values } = color;
if (type.indexOf("rgb") > -1) {
// Only convert the first 3 values to int (i.e. not alpha)
for (let i = 0; i < 3; i++) {
values[i] = parseInt(values[i], 10);
}
}
let colorString;
if (type.indexOf("hsl") > -1) {
colorString = `${color.type}(${values[0]}, ${values[1]}%, ${values[2]}%`;
} else {
colorString = `${color.type}(${values[0]}, ${values[1]}, ${values[2]}`;
}
if (values.length === 4) {
colorString += `, ${color.values[3]})`;
} else {
colorString += ")";
}
return colorString;
}
export function convertHexToRGBAObj(color) {
if (color.length === 4) {
let extendedColor = "#";
for (let i = 1; i < color.length; i++) {
extendedColor += color.charAt(i) + color.charAt(i);
}
color = extendedColor;
}
return {
r: parseInt(color.substr(1, 2), 16),
g: parseInt(color.substr(3, 2), 16),
b: parseInt(color.substr(5, 2), 16),
a: 1
};
}
/**
* Converts a color from CSS hex format to CSS rgb format.
*
* @param {string} color - Hex color, i.e. #nnn or #nnnnnn
* @returns {string} A CSS rgb color string
*/
export function convertHexToRGB(color) {
if (color.length === 4) {
let extendedColor = "#";
for (let i = 1; i < color.length; i++) {
extendedColor += color.charAt(i) + color.charAt(i);
}
color = extendedColor;
}
const values = {
r: parseInt(color.substr(1, 2), 16),
g: parseInt(color.substr(3, 2), 16),
b: parseInt(color.substr(5, 2), 16)
};
return `rgb(${values.r}, ${values.g}, ${values.b})`;
}
/**
* Returns an object with the type and values of a color.
*
* Note: Does not support rgb % values and color names.
*
* @param {string} color - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla()
* @returns {{type: string, values: number[]}} A MUI color object
*/
export function decomposeColor(color) {
if (color.charAt(0) === "#") {
return decomposeColor(convertHexToRGB(color));
}
const marker = color.indexOf("(");
const type = color.substring(0, marker);
let values = color.substring(marker + 1, color.length - 1).split(",");
values = values.map(value => parseFloat(value));
return { type, values };
}
/**
* Calculates the contrast ratio between two colors.
*
* Formula: http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef
*
* @param {string} foreground - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla()
* @param {string} background - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla()
* @returns {number} A contrast ratio value in the range 0 - 21 with 2 digit precision.
*/
export function getContrastRatio(foreground, background) {
const lumA = getLuminance(foreground);
const lumB = getLuminance(background);
const contrastRatio =
(Math.max(lumA, lumB) + 0.05) / (Math.min(lumA, lumB) + 0.05);
return Number(contrastRatio.toFixed(2)); // Truncate at two digits
}
/**
* The relative brightness of any point in a color space,
* normalized to 0 for darkest black and 1 for lightest white.
*
* Formula: https://www.w3.org/WAI/GL/wiki/Relative_luminance
*
* @param {string} color - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla()
* @returns {number} The relative brightness of the color in the range 0 - 1
*/
export function getLuminance(color) {
color = decomposeColor(color);
if (color.type.indexOf("rgb") > -1) {
const rgb = color.values.map(val => {
val /= 255; // normalized
return val <= 0.03928
? val / 12.92
: Math.pow((val + 0.055) / 1.055, 2.4);
});
return Number(
(0.2126 * rgb[0] + 0.7152 * rgb[1] + 0.0722 * rgb[2]).toFixed(3)
); // Truncate at 3 digits
} else if (color.type.indexOf("hsl") > -1) {
return color.values[2] / 100;
}
}
/**
* Darken or lighten a colour, depending on its luminance.
* Light colors are darkened, dark colors are lightened.
*
* @param {string} color - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla()
* @param {number} coefficient=0.15 - multiplier in the range 0 - 1
* @returns {string} A CSS color string. Hex input values are returned as rgb
*/
export function emphasize(color, coefficient = 0.15) {
return getLuminance(color) > 0.5
? darken(color, coefficient)
: lighten(color, coefficient);
}
/**
* Set the absolute transparency of a color.
* Any existing alpha values are overwritten.
*
* @param {string} color - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla()
* @param {number} value - value to set the alpha channel to in the range 0 -1
* @returns {string} A CSS color string. Hex input values are returned as rgb
*/
export function fade(color, value) {
color = decomposeColor(color);
value = clamp(value, 0, 1);
if (color.type === "rgb" || color.type === "hsl") {
color.type += "a";
}
color.values[3] = value;
return convertColorToString(color);
}
/**
* Darkens a color.
*
* @param {string} color - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla()
* @param {number} coefficient - multiplier in the range 0 - 1
* @returns {string} A CSS color string. Hex input values are returned as rgb
*/
export function darken(color, coefficient) {
color = decomposeColor(color);
coefficient = clamp(coefficient, 0, 1);
if (color.type.indexOf("hsl") > -1) {
color.values[2] *= 1 - coefficient;
} else if (color.type.indexOf("rgb") > -1) {
for (let i = 0; i < 3; i++) {
color.values[i] *= 1 - coefficient;
}
}
return convertColorToString(color);
}
/**
* Lightens a color.
*
* @param {string} color - CSS color, i.e. one of: #nnn, #nnnnnn, rgb(), rgba(), hsl(), hsla()
* @param {number} coefficient - multiplier in the range 0 - 1
* @returns {string} A CSS color string. Hex input values are returned as rgb
*/
export function lighten(color, coefficient) {
color = decomposeColor(color);
coefficient = clamp(coefficient, 0, 1);
if (color.type.indexOf("hsl") > -1) {
color.values[2] += (100 - color.values[2]) * coefficient;
} else if (color.type.indexOf("rgb") > -1) {
for (let i = 0; i < 3; i++) {
color.values[i] += (255 - color.values[i]) * coefficient;
}
}
return convertColorToString(color);
}
export function isValidHex(hex) {
const lh = String(hex).charAt(0) === "#" ? 1 : 0;
return (
hex.length !== 4 + lh && hex.length < 7 + lh && tinycolor(hex).isValid()
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.