fabric
Version:
Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.
294 lines (293 loc) • 8.34 kB
JavaScript
import { _defineProperty } from "../../_virtual/_@oxc-project_runtime@0.122.0/helpers/defineProperty.mjs";
import { radiansToDegrees } from "../util/misc/radiansDegreesConversion.mjs";
import { normalizeWs } from "../util/internals/normalizeWhiteSpace.mjs";
import { ColorNameMap } from "./color_map.mjs";
import { reHSLa, reHex, reRGBa } from "./constants.mjs";
import { fromAlphaToFloat, greyAverage, hexify, hue2rgb, rgb2Hsl } from "./util.mjs";
//#region src/color/Color.ts
/**
* @class Color common color operations
* @see {@link http://fabric5.fabricjs.com/fabric-intro-part-2#colors colors}
*/
var Color = class Color {
/**
*
* @param {string} [color] optional in hex or rgb(a) or hsl format or from known color list
*/
constructor(color) {
_defineProperty(this, "isUnrecognised", false);
if (!color) this.setSource([
0,
0,
0,
1
]);
else if (color instanceof Color) this.setSource([...color._source]);
else if (Array.isArray(color)) {
const [r, g, b, a = 1] = color;
this.setSource([
r,
g,
b,
a
]);
} else this.setSource(this._tryParsingColor(color));
}
/**
* @private
* @param {string} [color] Color value to parse
* @returns {TRGBAColorSource}
*/
_tryParsingColor(color) {
color = color.toLowerCase();
if (color in ColorNameMap) color = ColorNameMap[color];
return color === "transparent" ? [
255,
255,
255,
0
] : Color.sourceFromHex(color) || Color.sourceFromRgb(color) || Color.sourceFromHsl(color) || (this.isUnrecognised = true) && [
0,
0,
0,
1
];
}
/**
* Returns source of this color (where source is an array representation; ex: [200, 200, 100, 1])
* @return {TRGBAColorSource}
*/
getSource() {
return this._source;
}
/**
* Sets source of this color (where source is an array representation; ex: [200, 200, 100, 1])
* @param {TRGBAColorSource} source
*/
setSource(source) {
this._source = source;
}
/**
* Returns color representation in RGB format
* @return {String} ex: rgb(0-255,0-255,0-255)
*/
toRgb() {
const [r, g, b] = this.getSource();
return `rgb(${r},${g},${b})`;
}
/**
* Returns color representation in RGBA format
* @return {String} ex: rgba(0-255,0-255,0-255,0-1)
*/
toRgba() {
return `rgba(${this.getSource().join(",")})`;
}
/**
* Returns color representation in HSL format
* @return {String} ex: hsl(0-360,0%-100%,0%-100%)
*/
toHsl() {
const [h, s, l] = rgb2Hsl(...this.getSource());
return `hsl(${h},${s}%,${l}%)`;
}
/**
* Returns color representation in HSLA format
* @return {String} ex: hsla(0-360,0%-100%,0%-100%,0-1)
*/
toHsla() {
const [h, s, l, a] = rgb2Hsl(...this.getSource());
return `hsla(${h},${s}%,${l}%,${a})`;
}
/**
* Returns color representation in HEX format
* @return {String} ex: FF5555
*/
toHex() {
return this.toHexa().slice(0, 6);
}
/**
* Returns color representation in HEXA format
* @return {String} ex: FF5555CC
*/
toHexa() {
const [r, g, b, a] = this.getSource();
return `${hexify(r)}${hexify(g)}${hexify(b)}${hexify(Math.round(a * 255))}`;
}
/**
* Gets value of alpha channel for this color
* @return {Number} 0-1
*/
getAlpha() {
return this.getSource()[3];
}
/**
* Sets value of alpha channel for this color
* @param {Number} alpha Alpha value 0-1
* @return {Color} thisArg
*/
setAlpha(alpha) {
this._source[3] = alpha;
return this;
}
/**
* Transforms color to its grayscale representation
* @return {Color} thisArg
*/
toGrayscale() {
this.setSource(greyAverage(this.getSource()));
return this;
}
/**
* Transforms color to its black and white representation
* @param {Number} threshold
* @return {Color} thisArg
*/
toBlackWhite(threshold) {
const [average, , , a] = greyAverage(this.getSource()), bOrW = average < (threshold || 127) ? 0 : 255;
this.setSource([
bOrW,
bOrW,
bOrW,
a
]);
return this;
}
/**
* Overlays color with another color
* @param {String|Color} otherColor
* @return {Color} thisArg
*/
overlayWith(otherColor) {
if (!(otherColor instanceof Color)) otherColor = new Color(otherColor);
const source = this.getSource(), otherAlpha = .5, otherSource = otherColor.getSource(), [R, G, B] = source.map((value, index) => Math.round(value * (1 - otherAlpha) + otherSource[index] * otherAlpha));
this.setSource([
R,
G,
B,
source[3]
]);
return this;
}
/**
* Returns new color object, when given a color in RGB format
* @param {String} color Color value ex: rgb(0-255,0-255,0-255)
* @return {Color}
*/
static fromRgb(color) {
return Color.fromRgba(color);
}
/**
* Returns new color object, when given a color in RGBA format
* @param {String} color
* @return {Color}
*/
static fromRgba(color) {
return new Color(Color.sourceFromRgb(color));
}
/**
* Returns array representation (ex: [100, 100, 200, 1]) of a color that's in RGB or RGBA format
* @param {String} color Color value ex: rgb(0-255,0-255,0-255), rgb(0%-100%,0%-100%,0%-100%)
* @return {TRGBAColorSource | undefined} source
*/
static sourceFromRgb(color) {
const match = normalizeWs(color).match(reRGBa());
if (match) {
const [r, g, b] = match.slice(1, 4).map((value) => {
const parsedValue = parseFloat(value);
return value.endsWith("%") ? Math.round(parsedValue * 2.55) : parsedValue;
});
return [
r,
g,
b,
fromAlphaToFloat(match[4])
];
}
}
/**
* Returns new color object, when given a color in HSL format
* @param {String} color Color value ex: hsl(0-260,0%-100%,0%-100%)
* @return {Color}
*/
static fromHsl(color) {
return Color.fromHsla(color);
}
/**
* Returns new color object, when given a color in HSLA format
* @param {String} color
* @return {Color}
*/
static fromHsla(color) {
return new Color(Color.sourceFromHsl(color));
}
/**
* Returns array representation (ex: [100, 100, 200, 1]) of a color that's in HSL or HSLA format.
* Adapted from <a href="https://rawgithub.com/mjijackson/mjijackson.github.com/master/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript.html">https://github.com/mjijackson</a>
* @param {String} color Color value ex: hsl(0-360,0%-100%,0%-100%) or hsla(0-360,0%-100%,0%-100%, 0-1)
* @return {TRGBAColorSource | undefined} source
* @see http://http://www.w3.org/TR/css3-color/#hsl-color
*/
static sourceFromHsl(color) {
const match = normalizeWs(color).match(reHSLa());
if (!match) return;
const h = (Color.parseAngletoDegrees(match[1]) % 360 + 360) % 360 / 360, s = parseFloat(match[2]) / 100, l = parseFloat(match[3]) / 100;
let r, g, b;
if (s === 0) r = g = b = l;
else {
const q = l <= .5 ? l * (s + 1) : l + s - l * s, p = l * 2 - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
return [
Math.round(r * 255),
Math.round(g * 255),
Math.round(b * 255),
fromAlphaToFloat(match[4])
];
}
/**
* Returns new color object, when given a color in HEX format
* @param {String} color Color value ex: FF5555
* @return {Color}
*/
static fromHex(color) {
return new Color(Color.sourceFromHex(color));
}
/**
* Returns array representation (ex: [100, 100, 200, 1]) of a color that's in HEX format
* @param {String} color ex: FF5555 or FF5544CC (RGBa)
* @return {TRGBAColorSource | undefined} source
*/
static sourceFromHex(color) {
if (color.match(reHex())) {
const value = color.slice(color.indexOf("#") + 1), isShortNotation = value.length <= 4;
let expandedValue;
if (isShortNotation) expandedValue = value.split("").map((hex) => hex + hex);
else expandedValue = value.match(/.{2}/g);
const [r, g, b, a = 255] = expandedValue.map((hexCouple) => parseInt(hexCouple, 16));
return [
r,
g,
b,
a / 255
];
}
}
/**
* Converts a string that could be any angle notation (50deg, 0.5turn, 2rad)
* into degrees without the 'deg' suffix
* @param {String} value ex: 0deg, 0.5turn, 2rad
* @return {Number} number in degrees or NaN if inputs are invalid
*/
static parseAngletoDegrees(value) {
const lowercase = value.toLowerCase();
const numeric = parseFloat(lowercase);
if (lowercase.includes("rad")) return radiansToDegrees(numeric);
if (lowercase.includes("turn")) return numeric * 360;
return numeric;
}
};
//#endregion
export { Color };
//# sourceMappingURL=Color.mjs.map