coral-spectrum/coral-component-colorinput/src/scripts/Color.js
/**
* Copyright 2019 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
import {transform} from '../../../coral-utils';
// try to stay in same color space as long as possible (because of conversions being not 100% accurate ...)
/** @ignore */
const colorSpace = {
RGB: 'rgb',
HEX: 'hex',
CMYK: 'cmyk',
HSB: 'hsb',
HSL: 'hsl'
};
/**
Transforms part of a color (r,g,b) into a hex value.
@static
@param {Number} x
value between 0-255
@return {String} Hex representation
@ignore
*/
function _hex(x) {
return `0${x.toString(16)}`.slice(-2);
}
/** @ignore */
function _slice(str, startStr) {
let sliced = [];
str = transform.string(str).toLowerCase();
startStr = transform.string(startStr).toLowerCase();
if (str.indexOf(startStr) !== -1) {
sliced = str.substring(str.indexOf(startStr) + startStr.length, str.lastIndexOf(')')).split(/,\s*/);
}
return sliced;
}
/**
Parse an rgb value into an object.
e.g.: 'rgb(0,0,0)' => {r:0, g:0, b:0}
@static
@param {String} rgbStr
The string representing the rgb value
@return {Object} {r, g, b} Returns null if string could not be parsed
@ignore
*/
function _parseRGB(rgbStr) {
const sliced = _slice(rgbStr, 'rgb(');
if (sliced.length !== 3) {
return null;
}
const r = parseInt(sliced[0], 10);
const g = parseInt(sliced[1], 10);
const b = parseInt(sliced[2], 10);
if (isNaN(r) || isNaN(g) || isNaN(b)) {
return null;
}
if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255) {
return null;
}
return {r, g, b};
}
/**
Serialize an rgb object into a string.
e.g.: {r:0, g:0, b:0} => 'rgb(0,0,0)'
@static
@param {Object} rgb
@return {String} rgbStr The string representing the rgb value
@ignore
*/
function _serializeRGB(rgb) {
if (rgb) {
return `rgb(${rgb.r},${rgb.g},${rgb.b})`;
}
return '';
}
/**
Parse an rgba value into an object.
e.g.: 'rgba(0,0,0,0.5)' => {r:0, g:0, b:0, a:0.5}
@static
@param {String} rgbaStr
The string representing the rgba value.
@return {Object} {r, g, b, a} Returns null if string could not be parsed
@ignore
*/
function _parseRGBA(rgbaStr) {
const sliced = _slice(rgbaStr, 'rgba(');
if (sliced.length !== 4) {
return null;
}
const r = parseInt(sliced[0], 10);
const g = parseInt(sliced[1], 10);
const b = parseInt(sliced[2], 10);
const a = parseFloat(sliced[3]);
if (isNaN(r) || isNaN(g) || isNaN(b) || isNaN(a)) {
return null;
}
if (r < 0 || r > 255 || g < 0 || g > 255 || b < 0 || b > 255 || a < 0 || a > 1) {
return null;
}
return {r, g, b, a};
}
/**
Serialize an rgba object into a string.
e.g.: {r:0, g:0, b:0, a:0.5} => 'rgb(0,0,0,0.5)'
@static
@param {Object} rgba
@return {String} rgbaStr The string representing the rgba value
@ignore
*/
function _serializeRGBA(rgba) {
if (rgba) {
return `rgba(${rgba.r},${rgba.g},${rgba.b},${rgba.a})`;
}
return '';
}
/**
Parse an cmyk value into an object.
e.g.: 'cmyk(0, 100, 50, 0)' => {c:0, m:100, y:50, k:0}
@static
@param {String} cmykStr
The string representing the cmyk value.
@return {Object} {c, m, y, k} Returns null if string could not be parsed
@ignore
*/
function _parseCMYK(cmykStr) {
const sliced = _slice(cmykStr, 'cmyk(');
if (sliced.length !== 4) {
return null;
}
const c = parseFloat(sliced[0]);
const m = parseFloat(sliced[1]);
const y = parseFloat(sliced[2]);
const k = parseFloat(sliced[3]);
if (isNaN(c) || isNaN(m) || isNaN(y) || isNaN(k)) {
return null;
}
if (c < 0 || c > 100 || m < 0 || m > 100 || y < 0 || y > 100 || k < 0 || k > 100) {
return null;
}
return {c, m, y, k};
}
/**
Serialize an cmyk object into a string.
e.g.: {c:0, m:100, y:50, k:0} => 'cmyk(0, 100, 50, 0)'
@static
@param {Object} cmyk
@return {String} cmykStr The string representing the cmyk value
@ignore
*/
function _serializeCMYK(cmyk) {
if (cmyk) {
// make sure there are not more than 2 digits after dot
const c = parseFloat(cmyk.c.toFixed(2));
const m = parseFloat(cmyk.m.toFixed(2));
const y = parseFloat(cmyk.y.toFixed(2));
const k = parseFloat(cmyk.k.toFixed(2));
return `cmyk(${c},${m},${y},${k})`;
}
return '';
}
/**
Parse an hex value into a number. Corrects a hex value, if it is represented by 3 or 6 characters with or without
'#'.
@static
@param {String} hexStr The string representing the hex value
@return {Number} Returns a number representing the parsed hex value
@ignore
*/
function _parseHex(hexStr) {
hexStr = transform.string(hexStr).replace('#', '');
if (hexStr.length === 3) {
hexStr = hexStr.charAt(0) + hexStr.charAt(0) +
hexStr.charAt(1) + hexStr.charAt(1) +
hexStr.charAt(2) + hexStr.charAt(2);
}
// test if this could be a hex value
const isOk = (/^[0-9A-F]{6}$/i).test(hexStr);
if (!isOk) {
return null;
}
return parseInt(hexStr, 16);
}
/**
Transforms a hex color into RGB representation.
@static
@param {Number} hex
The color hex representation.
@return {Object} {r, g, b}
@ignore
*/
function _hexToRgb(hex) {
// explicitly test null (0 is valid)
if (hex !== null) {
return {
r: hex >> 16,
// eslint-disable-next-line no-extra-parens
g: (hex & 0x00FF00) >> 8,
// eslint-disable-next-line no-extra-parens
b: (hex & 0x0000FF)
};
}
return null;
}
/**
Serialize a hex number into a string.
@static
@param {Number} hex
@return {String}
@ignore
*/
function _serializeHex(hex) {
// explicitly test null (0 is valid)
if (hex !== null) {
const rgb = _hexToRgb(hex);
return `#${_hex(rgb.r) + _hex(rgb.g) + _hex(rgb.b)}`;
}
return '';
}
/**
Transforms a RGB color into HEX representation.
@static
@param {Object} rgb
@return {Number} hex The color hex representation
@ignore
*/
function _rgbToHex(rgb) {
if (rgb) {
return _parseHex(_hex(rgb.r) + _hex(rgb.g) + _hex(rgb.b));
}
return null;
}
/**
Transforms a cmyk color into RGB representation. Converting CMYK to RGB will incur slight loss because both color
spaces are not absolute and there will be some round-off error in the conversion process.
@static
@param {Object} cmyk
@return {Object} {r, g, b}
@ignore
*/
function _cmykToRgb(cmyk) {
if (!cmyk) {
return null;
}
const result = {
r: 0,
g: 0,
b: 0
};
const c = parseFloat(cmyk.c) / 100;
const m = parseFloat(cmyk.m) / 100;
const y = parseFloat(cmyk.y) / 100;
const k = parseFloat(cmyk.k) / 100;
result.r = 1 - Math.min(1, c * (1 - k) + k);
result.g = 1 - Math.min(1, m * (1 - k) + k);
result.b = 1 - Math.min(1, y * (1 - k) + k);
result.r = Math.round(result.r * 255);
result.g = Math.round(result.g * 255);
result.b = Math.round(result.b * 255);
return result;
}
/**
Transforms a rgb color into cmyk representation. Converting CMYK to RGB will incur slight loss because both color
spaces are not absolute and there will be some round-off error in the conversion process.
@static
@param {Object} rgb
@return {Object} {c, m, y, k}
@ignore
*/
function _rgbToCmyk(rgb) {
if (!rgb) {
return null;
}
const result = {
c: 0,
m: 0,
y: 0,
k: 0
};
if (rgb.r === 0 && rgb.g === 0 && rgb.b === 0) {
result.k = 100;
return result;
}
const r = rgb.r / 255;
const g = rgb.g / 255;
const b = rgb.b / 255;
result.k = Math.min(1 - r, 1 - g, 1 - b);
result.c = (1 - r - result.k) / (1 - result.k);
result.m = (1 - g - result.k) / (1 - result.k);
result.y = (1 - b - result.k) / (1 - result.k);
result.c = Math.round(result.c * 100);
result.m = Math.round(result.m * 100);
result.y = Math.round(result.y * 100);
result.k = Math.round(result.k * 100);
return result;
}
/**
Parse an hsb value into an object.
e.g.: 'hsb(360,100,0)' => {h:360, s:100, b:0}
@static
@param {String} hsbStr
The string representing the hsb value.
@return {Object} {h, s, b} Returns null if string could not be parsed
@ignore
*/
function _parseHSB(hsbStr) {
const sliced = _slice(hsbStr, 'hsb(');
if (sliced.length !== 3) {
return null;
}
// make sure there are not more than 2 digits after dot
const h = parseFloat(sliced[0]);
const s = parseFloat(sliced[1]);
const b = parseFloat(sliced[2]);
if (isNaN(h) || isNaN(s) || isNaN(b)) {
return null;
}
if (h < 0 || h > 360 || s < 0 || s > 100 || b < 0 || b > 100) {
return null;
}
return {h, s, b};
}
/**
Serialize an hsb object into a string.
e.g.: {h:0, s:0, b:0} => 'hsb(0,0,0)'
@static
@param {Object} hsb
@return {String} hsb The string representing the hsb value
@ignore
*/
function _serializeHSB(hsb) {
if (hsb) {
// make sure there are not more than 2 digits after dot
const h = parseFloat(hsb.h.toFixed(2));
const s = parseFloat(hsb.s.toFixed(2));
const b = parseFloat(hsb.b.toFixed(2));
return `hsb(${h},${s},${b})`;
}
return '';
}
/**
Transforms a HSB (same as HSV) color into RGB representation.
h (hue has value between 0-360 degree)
s (saturation has a value between 0-100 percent)
b (brightness has a value between 0-100 percent)
@static
@param {Object} hsb
@return {Object} {r, g, b}
@ignore
*/
function _hsbToRgb(hsb) {
if (!hsb) {
return null;
}
const h = hsb.h / 360;
const s = hsb.s / 100;
const v = hsb.b / 100;
let r, g, b;
const i = Math.floor(h * 6);
const f = h * 6 - i;
const p = v * (1 - s);
const q = v * (1 - f * s);
const t = v * (1 - (1 - f) * s);
switch (i % 6) {
case 0:
r = v;
g = t;
b = p;
break;
case 1:
r = q;
g = v;
b = p;
break;
case 2:
r = p;
g = v;
b = t;
break;
case 3:
r = p;
g = q;
b = v;
break;
case 4:
r = t;
g = p;
b = v;
break;
case 5:
r = v;
g = p;
b = q;
break;
}
return {
r: Math.round(r * 255),
g: Math.round(g * 255),
b: Math.round(b * 255)
};
}
/**
Transforms a RGB color into HSB (same as HSV) representation.
@static
@param {Object} rgb
@return {Object} {h, s, b}
@ignore
*/
function _rgbToHsb(rgb) {
if (!rgb) {
return null;
}
const r = rgb.r;
const g = rgb.g;
const b = rgb.b;
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
const d = max - min;
let h;
const s = max === 0 ? 0 : d / max;
const v = max / 255;
switch (max) {
case min:
h = 0;
break;
case r:
h = g - b + d * (g < b ? 6 : 0);
h /= 6 * d;
break;
case g:
h = b - r + d * 2;
h /= 6 * d;
break;
case b:
h = r - g + d * 4;
h /= 6 * d;
break;
}
return {
h: h * 360,
s: s * 100,
b: v * 100
};
}
/**
Parse an hsl value into an object.
e.g.: 'hsl(360,100,0)' => {h:360, s:100, b:0}
@static
@param {String} hslStr
The string representing the hsl value.
@return {Object} {h, s, l} Returns null if string could not be parsed
@ignore
*/
function _parseHSL(hslStr) {
const sliced = _slice(hslStr, 'hsl(');
if (sliced.length !== 3) {
return null;
}
// make sure there are not more than 2 digits after dot
const h = parseFloat(sliced[0]);
const s = parseFloat(sliced[1]);
const l = parseFloat(sliced[2]);
if (isNaN(h) || isNaN(s) || isNaN(l)) {
return null;
}
if (h < 0 || h > 360 || s < 0 || s > 100 || l < 0 || l > 100) {
return null;
}
return {h, s, l};
}
/**
Serialize an hsl object into a string.
e.g.: {h:0, s:0, l:0} => 'hsl(0,0%,0%)'
@static
@param {Object} hsl
@return {String} hsb The string representing the hsb value
@ignore
*/
function _serializeHSL(hsl) {
if (hsl) {
// make sure there are not more than 2 digits after dot
const h = parseFloat(hsl.h.toFixed(2));
const s = parseFloat(hsl.s.toFixed(2));
const l = parseFloat(hsl.l.toFixed(2));
return `hsl(${h},${s}%,${l}%)`;
}
return '';
}
/**
Transforms a HSL color into RGB representation.
h (hue has value between 0-360 degree)
s (saturation has a value between 0-100 percent)
l (lightness has a value between 0-100 percent)
@static
@param {Object} hsl
@return {Object} {r, g, b}
@ignore
*/
function _hslToRgb(hsl) {
if (!hsl) {
return null;
}
const h = hsl.h / 360;
const s = hsl.s / 100;
const l = hsl.l / 100;
let r;
let g;
let b;
if (s === 0) {
// achromatic
r = g = b = l;
} else {
const hue2rgb = (p, q, t) => {
if (t < 0) {
t += 1;
}
if (t > 1) {
t -= 1;
}
if (t < 1 / 6) {
return p + (q - p) * 6 * t;
}
if (t < 1 / 2) {
return q;
}
if (t < 2 / 3) {
return p + (q - p) * (2 / 3 - t) * 6;
}
return p;
};
const qValue = l < 0.5 ? l * (1 + s) : l + s - l * s;
const pValue = 2 * l - qValue;
r = hue2rgb(pValue, qValue, h + 1 / 3);
g = hue2rgb(pValue, qValue, h);
b = hue2rgb(pValue, qValue, h - 1 / 3);
}
return {
r: Math.round(r * 255),
g: Math.round(g * 255),
b: Math.round(b * 255)
};
}
/**
Transforms an RGB color into HSL representation.
@static
@param {Object} rgb
@return {Object} {h, s, l}
@ignore
*/
function _rgbToHsl(rgb) {
if (!rgb) {
return null;
}
const r = rgb.r / 255;
const g = rgb.g / 255;
const b = rgb.b / 255;
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
let h;
let s;
const l = (max + min) / 2;
if (max === min) {
// achromatic
h = s = 0;
} else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
return {
h: h * 360,
s: s * 100,
l: l * 100
};
}
/**
Parse an hsla value into an object.
e.g.: 'hsla(360,100%,0%,0.5)' => {h:360, s:100, l:0, 0.5}
@static
@param {String} hslaStr
The string representing the hsl value.
@return {Object} {h, s, l, a} Returns null if string could not be parsed
@ignore
*/
function _parseHSLA(hslaStr) {
const sliced = _slice(hslaStr, 'hsla(');
if (sliced.length !== 4) {
return null;
}
// make sure there are not more than 2 digits after dot
const h = parseFloat(sliced[0]);
const s = parseFloat(sliced[1]);
const l = parseFloat(sliced[2]);
const a = parseFloat(sliced[3]);
if (isNaN(h) || isNaN(s) || isNaN(l)) {
return null;
}
if (h < 0 || h > 360 || s < 0 || s > 100 || l < 0 || l > 100 || a < 0 || a > 1) {
return null;
}
return {h, s, l, a};
}
/**
Serialize an hsla object into a string.
e.g.: {h:0, s:0, l:0, a:0.5} => 'hsl(0,0%,0%,0.5)'
@static
@param {Object} hsla
@return {String} hsb The string representing the hsb value
@ignore
*/
function _serializeHSLA(hsla) {
if (hsla) {
// make sure there are not more than 2 digits after dot
const h = parseFloat(hsla.h.toFixed(2));
const s = parseFloat(hsla.s.toFixed(2));
const l = parseFloat(hsla.l.toFixed(2));
const a = parseFloat(hsla.a.toFixed(2));
return `hsla(${h},${s}%,${l}%,${a})`;
}
return '';
}
/**
Color is used to get a color in different color spaces, calculate tints and shades etc.
*/
class Color {
/** @ignore */
constructor() {
// Set defaults
this._colorSpace = colorSpace.HEX;
this._value = '';
this._alpha = 1;
}
/**
The value of the color. This value can be set in different formats (HEX, RGB, RGBA, HSB, HSL, HSLA and CMYK).
Corrects a hex value, if it is represented by 3 or 6 characters with or without '#'.
e.g:
HEX: #FFFFFF
RGB: rgb(16,16,16)
RGBA: rgba(215,40,40,0.9)
HSB: hsb(360,100,100)
HSL: hsl(360,100%,100%)
HSLA: hsla(360,100%,100%,0.9)
CMYK: cmyk(0,100,50,0)
@type {String}
@default ""
*/
get value() {
return this._value;
}
set value(value) {
// Two color formats with alpha values
const rgba = _parseRGBA(value);
const hsla = _parseHSLA(value);
const rgb = _parseRGB(value);
const cmyk = _parseCMYK(value);
const hsb = _parseHSB(value);
const hsl = _parseHSL(value);
const hex = _parseHex(value);
if (rgba !== null) {
this._colorSpace = colorSpace.RGB;
this.alpha = rgba.a;
value = _serializeRGB({
r: rgba.r,
g: rgba.g,
b: rgba.b
});
} else if (hsla !== null) {
this._colorSpace = colorSpace.HSL;
this.alpha = hsla.a;
value = _serializeHSL({
h: hsla.h,
s: hsla.s,
l: hsla.l
});
} else if (rgb !== null) {
this._colorSpace = colorSpace.RGB;
} else if (cmyk !== null) {
this._colorSpace = colorSpace.CMYK;
} else if (hsb !== null) {
this._colorSpace = colorSpace.HSB;
} else if (hsl !== null) {
this._colorSpace = colorSpace.HSL;
} else if (hex !== null) {
this._colorSpace = colorSpace.HEX;
} else {
// restore defaults
this._colorSpace = colorSpace.HEX;
value = '';
}
this._value = value;
}
/**
The alpha value of the color (value between 0-1).
@type {Number}
@default 1
*/
get alpha() {
return this._alpha;
}
set alpha(value) {
if (isNaN(value) || value < 0 || value > 1) {
return;
}
this._alpha = transform.number(value);
}
/**
The rgb values of the color (value between 0-255).
e.g.: {r:0, g:0, b:0}
@type {Object}
@default null
*/
get rgb() {
let rgb = null;
if (this._colorSpace === colorSpace.RGB) {
rgb = _parseRGB(this.value);
} else if (this._colorSpace === colorSpace.HEX) {
const hex = _parseHex(this.value);
rgb = _hexToRgb(hex);
} else if (this._colorSpace === colorSpace.CMYK) {
const cmyk = _parseCMYK(this.value);
rgb = _cmykToRgb(cmyk);
} else if (this._colorSpace === colorSpace.HSB) {
const hsb = _parseHSB(this.value);
rgb = _hsbToRgb(hsb);
} else if (this._colorSpace === colorSpace.HSL) {
const hsl = _parseHSL(this.value);
rgb = _hslToRgb(hsl);
}
return rgb;
}
set rgb(value) {
this.value = _serializeRGB(value);
}
/**
The serialized rgb values of the color (r,g,b values between 0-255).
e.g: 'rgb(0,0,0)'
@type {String}
@default ""
*/
get rgbValue() {
return _serializeRGB(this.rgb);
}
set rgbValue(value) {
this.value = value;
}
/**
The rgba values of the color (r,g,b values between 0-255 and a between 0-1).
e.g: {r:0, g:0, b:0, a:1}
@type {Object}
@default null
*/
get rgba() {
const rgb = this.rgb;
if (rgb) {
return {
r: rgb.r,
g: rgb.g,
b: rgb.b,
a: this.alpha
};
}
return null;
}
set rgba(value) {
this.value = _serializeRGBA(value);
}
/**
The serialized rgba values of the color (r,g,b values between 0-255 and alpha between 0-1).
e.g: 'rgba(0,0,0,1)'
@type {String}
@default ""
*/
get rgbaValue() {
return _serializeRGBA(this.rgba);
}
set rgbaValue(value) {
this.value = value;
}
/**
The hex value of the color.
@type {Number}
@default null
*/
get hex() {
// as hex color space is essentially just the same as rgb and there is no loss in conversion, we can do it this way
return _rgbToHex(this.rgb);
}
set hex(value) {
this.value = _serializeHex(value);
}
/**
The serialized hex value of the color.
e.g: '#94CD4B'
@type {String}
@default ""
*/
get hexValue() {
return _serializeHex(this.hex);
}
set hexValue(value) {
this.value = value;
}
/**
The cmyk values of the color (all values between 0-100).
e.g: {c:0, m:100, y:0, k:100}
@type {Object}
@default null
*/
get cmyk() {
let cmyk = null;
let rgb = null;
if (this._colorSpace === colorSpace.RGB) {
rgb = _parseRGB(this.value);
cmyk = _rgbToCmyk(rgb);
} else if (this._colorSpace === colorSpace.HEX) {
const hex = _parseHex(this.value);
rgb = _hexToRgb(hex);
cmyk = _rgbToCmyk(rgb);
} else if (this._colorSpace === colorSpace.CMYK) {
cmyk = _parseCMYK(this.value);
} else if (this._colorSpace === colorSpace.HSB) {
const hsb = _parseHSB(this.value);
rgb = _hsbToRgb(hsb);
cmyk = _rgbToCmyk(rgb);
} else if (this._colorSpace === colorSpace.HSL) {
const hsl = _parseHSL(this.value);
rgb = _hslToRgb(hsl);
cmyk = _rgbToCmyk(rgb);
}
return cmyk;
}
set cmyk(value) {
this.value = _serializeCMYK(value);
}
/**
The serialized cmyk values of the color (all values between 0-100).
e.g: 'cmyk(100,100,100,100)'
@type {String}
@default ""
*/
get cmykValue() {
return _serializeCMYK(this.cmyk);
}
set cmykValue(value) {
this.value = value;
}
/**
The hsb values of the color.
h (hue has value between 0-360 degree)
s (saturation has a value between 0-100 percent)
b (brightness has a value between 0-100 percent)
@type {Object}
@default null
*/
get hsb() {
let hsb = null;
let rgb = null;
if (this._colorSpace === colorSpace.RGB) {
rgb = _parseRGB(this.value);
hsb = _rgbToHsb(rgb);
} else if (this._colorSpace === colorSpace.HEX) {
const hex = _parseHex(this.value);
rgb = _hexToRgb(hex);
hsb = _rgbToHsb(rgb);
} else if (this._colorSpace === colorSpace.CMYK) {
const cmyk = _parseCMYK(this.value);
rgb = _cmykToRgb(cmyk);
hsb = _rgbToHsb(rgb);
} else if (this._colorSpace === colorSpace.HSB) {
hsb = _parseHSB(this.value);
} else if (this._colorSpace === colorSpace.HSL) {
const hsl = _parseHSL(this.value);
rgb = _hslToRgb(hsl);
hsb = _rgbToHsb(rgb);
}
return hsb;
}
set hsb(value) {
this.value = _serializeHSB(value);
}
/**
The serialized hsb values of the color (s and b values between 0-100, h between 0-360).
e.g: 'hsb(360,100,100)'
@type {String}
@default ""
*/
get hsbValue() {
return _serializeHSB(this.hsb);
}
set hsbValue(value) {
this.value = value;
}
/*
The hsl values of the color.
h (hue has value between 0-360 degree)
s (saturation has a value between 0-100 percent)
l (lightness has a value between 0-100 percent)
@type {Object}
@default null
*/
get hsl() {
let hsl = null;
let rgb = null;
if (this._colorSpace === colorSpace.RGB) {
rgb = _parseRGB(this.value);
hsl = _rgbToHsl(rgb);
} else if (this._colorSpace === colorSpace.HEX) {
const hex = _parseHex(this.value);
rgb = _hexToRgb(hex);
hsl = _rgbToHsl(rgb);
} else if (this._colorSpace === colorSpace.CMYK) {
const cmyk = _parseCMYK(this.value);
rgb = _cmykToRgb(cmyk);
hsl = _rgbToHsl(rgb);
} else if (this._colorSpace === colorSpace.HSB) {
const hsb = _parseHSB(this.value);
rgb = _hsbToRgb(hsb);
hsl = _rgbToHsl(rgb);
} else if (this._colorSpace === colorSpace.HSL) {
hsl = _parseHSL(this.value);
}
return hsl;
}
set hsl(value) {
this.value = _serializeHSL(value);
}
/**
The serialized hsl values of the color (s and l values between 0-100 in percent, h between 0-360).
e.g: 'hsl(360,100%,100%)'
@type {String}
@default ""
*/
get hslValue() {
return _serializeHSL(this.hsl);
}
set hslValue(value) {
this.value = value;
}
/**
The hsla values of the color.
h (hue has value between 0-360 degree)
s (saturation has a value between 0-100 percent)
l (lightness has a value between 0-100 percent)
a (alpha has a value between 0-1)
@type {Object}
@default null
*/
get hsla() {
const hsl = this.hsl;
if (hsl) {
return {
h: hsl.h,
s: hsl.s,
l: hsl.l,
a: this.alpha
};
}
return null;
}
set hsla(value) {
this.value = _serializeHSLA(value);
}
/**
The serialized hsla values of the color.
h (hue has value between 0-360 degree)
s (saturation has a value between 0-100 percent)
l (lightness has a value between 0-100 percent)
a (alpha has a value between 0-1)
e.g: 'hsla(360,50%,50%,0.9)'
@type {String}
@default ""
*/
get hslaValue() {
return _serializeHSLA(this.hsla);
}
set hslaValue(value) {
this.value = value;
}
/**
Clone this color.
@type {Color}
*/
clone() {
const clone = new this.constructor();
clone.value = this.value;
clone.alpha = this.alpha;
return clone;
}
/**
Test if this Color is similar to another color.
@type {Boolean}
@param {Color} compareColor
The color to compare this color too.
@param {Boolean} [allowSlightColorDifference=true]
While converting between color spaces slight loses might happen => we should normally consider colors equal,
even if they are minimally different.
*/
isSimilarTo(compareColor, allowSlightColorDifference) {
if (this.rgb === null && (!compareColor || compareColor.rgb === null)) {
// Consider an rgb of null equal to a null object (or another value of null)
return true;
}
if (!compareColor || compareColor.rgb === null || this.rgb === null) {
return false;
}
let allowedRgbDifference = 1;
let allowedAlphaDifference = 0.01;
if (allowSlightColorDifference === false) {
allowedRgbDifference = 0;
allowedAlphaDifference = 0;
}
const rgb = this.rgb;
const rgb2 = compareColor.rgb;
const rDiff = Math.abs(rgb2.r - rgb.r);
const gDiff = Math.abs(rgb2.g - rgb.g);
const bDiff = Math.abs(rgb2.b - rgb.b);
const aDiff = Math.abs(this.alpha - compareColor.alpha);
if (rDiff <= allowedRgbDifference && gDiff <= allowedRgbDifference && bDiff <= allowedRgbDifference && aDiff <= allowedAlphaDifference) {
return true;
}
return false;
}
/**
Calculates an array of lighter colors.
@type {Array<Coral.Color>}
@param {Number} amount
Amount of tint colors to generate.
*/
calculateTintColors(amount) {
const tintColors = [];
let tintColor = null;
const rgb = this.rgb;
if (rgb) {
const r = rgb.r;
const g = rgb.g;
const b = rgb.b;
let tintFactor = 1;
for (let i = 1 ; i <= amount ; i++) {
tintFactor = i / (amount + 1);
tintColor = this.clone();
// alpha value kept from original
tintColor.rgb = {
r: r + (255 - r) * tintFactor,
g: g + (255 - g) * tintFactor,
b: b + (255 - b) * tintFactor
};
tintColors.push(tintColor);
}
}
return tintColors;
}
/**
Calculates an array of darker colors.
@type {Array<Coral.Color>}
@param {Number} amount
Amount of shade colors to generate.
*/
calculateShadeColors(amount) {
const shadeColors = [];
let shadeColor = null;
const rgb = this.rgb;
if (rgb) {
const r = rgb.r;
const g = rgb.g;
const b = rgb.b;
let shadeFactor = 1;
for (let i = 1 ; i <= amount ; i++) {
shadeFactor = i / (amount + 1);
shadeColor = this.clone();
// alpha value kept from original
shadeColor.rgb = {
r: r * (1 - shadeFactor),
g: g * (1 - shadeFactor),
b: b * (1 - shadeFactor)
};
shadeColors.push(shadeColor);
}
}
return shadeColors;
}
}
export default Color;