font-color-contrast
Version:
JavaScript module to use black or white font according to the given background color
263 lines • 15.8 kB
JavaScript
"use strict";
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _FontColorContrast_hexColorOrRedOrArray, _FontColorContrast_greenOrThreshold, _FontColorContrast_blue, _FontColorContrast_threshold;
Object.defineProperty(exports, "__esModule", { value: true });
exports.FontColorContrast = exports.NumberType = void 0;
const cssNamedColors_1 = require("./cssNamedColors");
var NumberType;
(function (NumberType) {
NumberType[NumberType["COLOR"] = 255] = "COLOR";
NumberType[NumberType["RGB"] = 16777215] = "RGB";
NumberType[NumberType["THRESHOLD"] = 1] = "THRESHOLD";
})(NumberType = exports.NumberType || (exports.NumberType = {}));
class FontColorContrast {
/**
* Sets the #params in the instance
* @param hexColorOrRedOrArray One of the options: hex color number, hex color string, named CSS color, array with red, green and blue or string or the red portion of the color
* @param greenOrThreshold The green portion of the color or the contrast threshold to control the resulting font color
* @param blue The blue portion of the color
* @param threshold Contrast threshold to control the resulting font color
*/
constructor(hexColorOrRedOrArray, greenOrThreshold, blue, threshold) {
this.red = 0;
this.green = 0;
this.blue = 0;
_FontColorContrast_hexColorOrRedOrArray.set(this, void 0);
_FontColorContrast_greenOrThreshold.set(this, void 0);
_FontColorContrast_blue.set(this, void 0);
_FontColorContrast_threshold.set(this, void 0);
/**
* Contrast threshold to control the resulting font color, float values from 0 to 1. Default is 0.5
*/
this.threshold = 0.5;
__classPrivateFieldSet(this, _FontColorContrast_hexColorOrRedOrArray, hexColorOrRedOrArray, "f");
__classPrivateFieldSet(this, _FontColorContrast_greenOrThreshold, greenOrThreshold, "f");
__classPrivateFieldSet(this, _FontColorContrast_blue, blue, "f");
__classPrivateFieldSet(this, _FontColorContrast_threshold, threshold, "f");
}
/**
* Analyses the color (normally used in the background) and retrieves what color (black or white) has a better contrast.
* @returns The best contrast between black and white
*/
getColor() {
if (this.isRgb()) {
this.setColorsFromRgbNumbers();
}
else if (this.isHexString()) {
this.setColorsFromHexString();
}
else if (this.isNumber()) {
this.setColorsFromNumber();
}
else if (this.isArray()) {
this.setColorsFromArray();
}
else {
return '#ffffff';
}
return this.contrastFromHSP();
}
/**
* Checks if the color is set as RGB on each param
* @returns True if color is set as RGB on each param
*/
isRgb() {
return (FontColorContrast.isValidNumber(__classPrivateFieldGet(this, _FontColorContrast_hexColorOrRedOrArray, "f"), NumberType.COLOR) &&
FontColorContrast.isValidNumber(__classPrivateFieldGet(this, _FontColorContrast_greenOrThreshold, "f"), NumberType.COLOR) &&
FontColorContrast.isValidNumber(__classPrivateFieldGet(this, _FontColorContrast_blue, "f"), NumberType.COLOR) &&
FontColorContrast.isValidNumber(__classPrivateFieldGet(this, _FontColorContrast_threshold, "f"), NumberType.THRESHOLD));
}
/**
* Checks if color is set on the first param as a hex string and removes the hash of it
* @returns True if color is a hex string
*/
isHexString() {
const [cleanString, hexNum] = this.getCleanStringAndHexNum();
if (FontColorContrast.isValidNumber(hexNum, NumberType.RGB) &&
FontColorContrast.isValidNumber(__classPrivateFieldGet(this, _FontColorContrast_greenOrThreshold, "f"), NumberType.THRESHOLD) &&
FontColorContrast.isNotSet(__classPrivateFieldGet(this, _FontColorContrast_blue, "f")) &&
FontColorContrast.isNotSet(__classPrivateFieldGet(this, _FontColorContrast_threshold, "f"))) {
__classPrivateFieldSet(this, _FontColorContrast_hexColorOrRedOrArray, cleanString, "f");
return true;
}
return false;
}
/**
* Checks if color is set on the first param as a number
* @returns True if color is a valid RGB nunbernumber
*/
isNumber() {
return (FontColorContrast.isValidNumber(__classPrivateFieldGet(this, _FontColorContrast_hexColorOrRedOrArray, "f"), NumberType.RGB) &&
FontColorContrast.isValidNumber(__classPrivateFieldGet(this, _FontColorContrast_greenOrThreshold, "f"), NumberType.THRESHOLD) &&
FontColorContrast.isNotSet(__classPrivateFieldGet(this, _FontColorContrast_blue, "f")) &&
FontColorContrast.isNotSet(__classPrivateFieldGet(this, _FontColorContrast_threshold, "f")));
}
/**
* Checks if color is set as an RGB array
* @returns True if color is set as an RGB array
*/
isArray() {
return (Array.isArray(__classPrivateFieldGet(this, _FontColorContrast_hexColorOrRedOrArray, "f")) &&
__classPrivateFieldGet(this, _FontColorContrast_hexColorOrRedOrArray, "f").length === 3 &&
FontColorContrast.isValidNumber(__classPrivateFieldGet(this, _FontColorContrast_hexColorOrRedOrArray, "f")[0], NumberType.COLOR) &&
FontColorContrast.isValidNumber(__classPrivateFieldGet(this, _FontColorContrast_hexColorOrRedOrArray, "f")[1], NumberType.COLOR) &&
FontColorContrast.isValidNumber(__classPrivateFieldGet(this, _FontColorContrast_hexColorOrRedOrArray, "f")[2], NumberType.COLOR) &&
FontColorContrast.isValidNumber(__classPrivateFieldGet(this, _FontColorContrast_greenOrThreshold, "f"), NumberType.THRESHOLD) &&
FontColorContrast.isNotSet(__classPrivateFieldGet(this, _FontColorContrast_blue, "f")) &&
FontColorContrast.isNotSet(__classPrivateFieldGet(this, _FontColorContrast_threshold, "f")));
}
/**
* Converts a color array or separated in RGB to the respective RGB values
* @example All these examples produces the same value
* arrayOrRgbToRGB(0, 0xcc, 153)
* arrayOrRgbToRGB(0x0, 0xcc, 153)
* arrayOrRgbToRGB(0, 204, 0x99)
*/
setColorsFromRgbNumbers() {
this.red = __classPrivateFieldGet(this, _FontColorContrast_hexColorOrRedOrArray, "f");
this.green = __classPrivateFieldGet(this, _FontColorContrast_greenOrThreshold, "f");
this.blue = __classPrivateFieldGet(this, _FontColorContrast_blue, "f");
this.setThreshold(__classPrivateFieldGet(this, _FontColorContrast_threshold, "f"));
}
/**
* Converts a color array or separated in RGB to the respective RGB values
* @param this.#hexColorOrRedOrArray The RGB array
* @param threshold The threshold
* @example All these examples produces the same value
* arrayOrRgbToRGB([0, 0xcc, 153])
* arrayOrRgbToRGB([0x0, 0xcc, 153])
* arrayOrRgbToRGB([0, 204, 0x99])
*/
setColorsFromArray() {
this.red = __classPrivateFieldGet(this, _FontColorContrast_hexColorOrRedOrArray, "f")[0];
this.green = __classPrivateFieldGet(this, _FontColorContrast_hexColorOrRedOrArray, "f")[1];
this.blue = __classPrivateFieldGet(this, _FontColorContrast_hexColorOrRedOrArray, "f")[2];
this.setThreshold(__classPrivateFieldGet(this, _FontColorContrast_greenOrThreshold, "f"));
}
/**
* Converts a ColorIntensity string or number, with all possibilities (e.g. '#009', '009', '#000099', '000099', 153, 0x00099) to the respective RGB values
* @param hexColor The color string or number
* @param threshold The threshold
* @example All these examples produces the same value
* hexColorToRGB('#0C9')
* hexColorToRGB('0C9')
* hexColorToRGB('#00CC99')
* hexColorToRGB('00cc99')
* hexColorToRGB(52377)
* hexColorToRGB(0x00Cc99)
*/
setColorsFromHexString() {
switch (__classPrivateFieldGet(this, _FontColorContrast_hexColorOrRedOrArray, "f").length) {
// Color has one char for each color, so they must be repeated
case 3:
this.red = parseInt(__classPrivateFieldGet(this, _FontColorContrast_hexColorOrRedOrArray, "f")[0].repeat(2), 16);
this.green = parseInt(__classPrivateFieldGet(this, _FontColorContrast_hexColorOrRedOrArray, "f")[1].repeat(2), 16);
this.blue = parseInt(__classPrivateFieldGet(this, _FontColorContrast_hexColorOrRedOrArray, "f")[2].repeat(2), 16);
break;
// All chars are filled, so no transformation is needed
default:
this.red = parseInt(__classPrivateFieldGet(this, _FontColorContrast_hexColorOrRedOrArray, "f").substring(0, 2), 16);
this.green = parseInt(__classPrivateFieldGet(this, _FontColorContrast_hexColorOrRedOrArray, "f").substring(2, 4), 16);
this.blue = parseInt(__classPrivateFieldGet(this, _FontColorContrast_hexColorOrRedOrArray, "f").substring(4, 6), 16);
break;
}
this.setThreshold(__classPrivateFieldGet(this, _FontColorContrast_greenOrThreshold, "f"));
}
/**
* Converts the RGB number and sets the respective RGB values.
*/
setColorsFromNumber() {
/*
* The RGB color has 24 bits (8 bits per color).
* This function uses binary operations for better performance, but can be tricky to understand. A 24 bits color could be represented as RRRRRRRR GGGGGGGG BBBBBBBB (the first 8 bits are red, the middle 8 bits are green and the last 8 bits are blue).
* To get each color we perform some RIGHT SHIFT and AND operations.
* Gets the first 8 bits of the color by shifting it 16 bits
* RIGHT SHIFT operation (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Right_shift)
* AND operation (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_AND)
*/
// To get red, we shift the 24 bits number 16 bits to the right, leaving the number only with the leftmost 8 bits (RRRRRRRR)
this.red = __classPrivateFieldGet(this, _FontColorContrast_hexColorOrRedOrArray, "f") >> 16;
// To get green, the middle 8 bits, we shift it by 8 bits (removing all blue bits - RRRRRRRR GGGGGGGG) and use an AND operation with "0b0000000011111111 = 0xff" to get only the rightmost bits (GGGGGGGG)
this.green = (__classPrivateFieldGet(this, _FontColorContrast_hexColorOrRedOrArray, "f") >> 8) & 0xff;
// To get blue we use an AND operation with "0b000000000000000011111111 = 0xff" to get only the rightmost bits (BBBBBBBB)
this.blue = __classPrivateFieldGet(this, _FontColorContrast_hexColorOrRedOrArray, "f") & 0xff;
this.setThreshold(__classPrivateFieldGet(this, _FontColorContrast_greenOrThreshold, "f"));
}
/**
* Sets the threshold to the passed value (if valid - less than or equal 1) or the dafault (0.5)
* @param threshold The passed threshold or undefined if not passed
*/
setThreshold(threshold) {
this.threshold = threshold || this.threshold;
}
/**
* Verifies if a number is a valid color number (numberType = NumberType.COLOR = 0xff) or a valid RGB (numberType = NumberType.RGB = 0xffffff) or a valid threshold (numberType = NumberType.THRESHOLD = 1)
* @param num The number to be checked
* @param numberType The type of number to be chacked that defines maximum value of the number (default = NumberType.COLOR = 0xff)
* @returns True if the number is valid
*/
static isValidNumber(num, numberType) {
if (numberType === NumberType.THRESHOLD && (num === undefined || num === null))
return true;
return (typeof num === 'number' &&
((numberType !== NumberType.THRESHOLD && Number.isInteger(num)) || numberType === NumberType.THRESHOLD) &&
num !== undefined &&
num !== null &&
num >= 0 &&
num <= numberType);
}
/**
* Verifies if a string is a valig string to be used as a color and if true, returns the correspondent hex number
* @returns Array with an empty string and false if the string is invalid or an array with the clean string and the converted string number]
*/
getCleanStringAndHexNum() {
if (typeof __classPrivateFieldGet(this, _FontColorContrast_hexColorOrRedOrArray, "f") !== 'string')
return ['', false];
const cleanRegEx = /(#|\s)/ig;
const namedColor = cssNamedColors_1.cssNamedColors.find(color => color.name === __classPrivateFieldGet(this, _FontColorContrast_hexColorOrRedOrArray, "f"));
if (namedColor) {
__classPrivateFieldSet(this, _FontColorContrast_hexColorOrRedOrArray, namedColor.hex.replace(cleanRegEx, ''), "f");
}
const cleanString = (__classPrivateFieldGet(this, _FontColorContrast_hexColorOrRedOrArray, "f")).replace(cleanRegEx, '');
if (cleanString.length !== 3 && cleanString.length !== 6)
return ['', false];
const hexNum = Number('0x' + cleanString);
return [cleanString, hexNum];
}
/**
* Verifies if a value is not set
* @param value The value that should be undefined or null
* @returns True if the value is not set
*/
static isNotSet(value) {
return (value === undefined || value === null);
}
/**
* Calculates the best color (black or white) to contrast with the passed RGB color using the algorithm from https://alienryderflex.com/hsp.html
* @returns Black or White depending on the best possible contrast
*/
contrastFromHSP() {
const pRed = 0.299;
const pGreen = 0.587;
const pBlue = 0.114;
const contrast = Math.sqrt(pRed * Math.pow((this.red / 255), 2) +
pGreen * Math.pow((this.green / 255), 2) +
pBlue * Math.pow((this.blue / 255), 2));
return contrast > this.threshold
? '#000000'
: '#ffffff';
}
}
exports.FontColorContrast = FontColorContrast;
_FontColorContrast_hexColorOrRedOrArray = new WeakMap(), _FontColorContrast_greenOrThreshold = new WeakMap(), _FontColorContrast_blue = new WeakMap(), _FontColorContrast_threshold = new WeakMap();
//# sourceMappingURL=FontColorContrast.js.map