feat(color): added utilities and improved color parsing performance (#9110)

This commit is contained in:
farfromrefuge
2021-08-11 19:54:57 +02:00
committed by Nathan Walker
parent 65f9598a0d
commit c4db847ded
4 changed files with 152 additions and 154 deletions

View File

@ -1,7 +1,6 @@
import * as definition from '.'; import * as definition from '.';
import * as types from '../utils/types'; import * as types from '../utils/types';
import * as knownColors from './known-colors'; import * as knownColors from './known-colors';
import { convertHSLToRGBColor } from '../css/parser';
const SHARP = '#'; const SHARP = '#';
const HEX_REGEX = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)|(^#[0-9A-F]{8}$)/i; const HEX_REGEX = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)|(^#[0-9A-F]{8}$)/i;
@ -12,26 +11,28 @@ export class Color implements definition.Color {
constructor(color: number); constructor(color: number);
constructor(color: string); constructor(color: string);
constructor(a: number, r: number, g: number, b: number); constructor(a: number, r: number, g: number, b: number, type?: 'rbg' | 'hsl' | 'hsv');
constructor(...args) { constructor(...args) {
if (args.length === 1) { if (args.length === 1) {
const arg = args[0]; const arg = args[0];
if (types.isString(arg)) { if (types.isString(arg)) {
if (isRgbOrRgba(arg)) { const lowered = arg.toLowerCase();
this._argb = argbFromRgbOrRgba(arg); if (isRgbOrRgba(lowered)) {
} else if (isHslOrHsla(arg)) { this._argb = argbFromRgbOrRgba(lowered);
this._argb = argbFromHslOrHsla(arg); } else if (isHslOrHsla(lowered)) {
} else if (knownColors.isKnownName(arg)) { this._argb = argbFromHslOrHsla(lowered);
} else if (isHsvOrHsva(lowered)) {
this._argb = argbFromHsvOrHsva(lowered);
} else if (knownColors.isKnownName(lowered)) {
// The parameter is a known color name // The parameter is a known color name
const argb = knownColors.getKnownColor(arg); const argb = knownColors.getKnownColor(lowered);
this._name = arg; this._name = arg;
this._argb = argb; this._argb = argb;
} else if (arg[0].charAt(0) === SHARP && (arg.length === 4 || arg.length === 7 || arg.length === 9)) { } else if (arg[0].charAt(0) === SHARP && (arg.length === 4 || arg.length === 7 || arg.length === 9)) {
// we dont use the regexp as it is quite slow. Instead we expect it to be a valid hex format // we dont use the regexp as it is quite slow. Instead we expect it to be a valid hex format
// strange that it would not be. And if it is not a thrown error seems best // strange that it would not be. And if it is not a thrown error seems best
// The parameter is a "#RRGGBBAA" formatted string // The parameter is a "#RRGGBBAA" formatted string
const hex = this._normalizeHex(arg); this._argb = this._argbFromString(arg);
this._argb = this._argbFromString(hex);
} else { } else {
throw new Error('Invalid color: ' + arg); throw new Error('Invalid color: ' + arg);
} }
@ -47,8 +48,23 @@ export class Color implements definition.Color {
} else { } else {
throw new Error('Expected 1 or 4 constructor parameters.'); throw new Error('Expected 1 or 4 constructor parameters.');
} }
} else if (args.length === 4) { } else if (args.length >= 4) {
this._argb = (args[0] & 0xff) * 0x01000000 + (args[1] & 0xff) * 0x00010000 + (args[2] & 0xff) * 0x00000100 + (args[3] & 0xff) * 0x00000001; const a = args[0];
switch (args[4]) {
case 'hsl': {
const { r, g, b } = hslToRgb(args[1], args[2], args[3]);
this._argb = (a & 0xff) * 0x01000000 + (r & 0xff) * 0x00010000 + (g & 0xff) * 0x00000100 + (b & 0xff) * 0x00000001;
break;
}
case 'hsv': {
const { r, g, b } = hsvToRgb(args[1], args[2], args[3]);
this._argb = (a & 0xff) * 0x01000000 + (r & 0xff) * 0x00010000 + (g & 0xff) * 0x00000100 + (b & 0xff) * 0x00000001;
break;
}
default:
this._argb = (a & 0xff) * 0x01000000 + (args[1] & 0xff) * 0x00010000 + (args[2] & 0xff) * 0x00000100 + (args[3] & 0xff) * 0x00000001;
break;
}
} else { } else {
throw new Error('Expected 1 or 4 constructor parameters.'); throw new Error('Expected 1 or 4 constructor parameters.');
} }
@ -72,11 +88,11 @@ export class Color implements definition.Color {
} }
get hex(): string { get hex(): string {
if (this.a === 0xff) { let result = SHARP + ('000000' + (this._argb & 0xffffff).toString(16)).toUpperCase().slice(-6);
return ('#' + this._componentToHex(this.r) + this._componentToHex(this.g) + this._componentToHex(this.b)).toUpperCase(); if (this.a !== 0xff) {
} else { return (result += ('00' + this.a.toString(16).toUpperCase()).slice(-2));
return ('#' + this._componentToHex(this.r) + this._componentToHex(this.g) + this._componentToHex(this.b) + this._componentToHex(this.a)).toUpperCase();
} }
return result;
} }
get name(): string { get name(): string {
@ -92,13 +108,13 @@ export class Color implements definition.Color {
} }
public _argbFromString(hex: string): number { public _argbFromString(hex: string): number {
if (hex.charAt(0) === '#') { // always called as SHARP as first char
hex = hex.substr(1); hex = hex.substr(1);
} const length = hex.length;
// first we normalize
if (hex.length === 3) { if (length === 3) {
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
} else if (hex.length === 4) { } else if (length === 4) {
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2] + hex[3] + hex[3]; hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2] + hex[3] + hex[3];
} }
@ -142,35 +158,19 @@ export class Color implements definition.Color {
if (!types.isString(value)) { if (!types.isString(value)) {
return false; return false;
} }
const lowered = value.toLowerCase();
if (knownColors.isKnownName(value)) { if (knownColors.isKnownName(lowered)) {
return true; return true;
} }
return HEX_REGEX.test(value) || isRgbOrRgba(value) || isHslOrHsla(value); return HEX_REGEX.test(value) || isRgbOrRgba(lowered) || isHslOrHsla(lowered);
} }
public static fromHSL(a, h, s, l) { public static fromHSL(a, h, s, l) {
const rgb = hslToRgb(h, s, l); return new Color(a, h, s, l, 'hsl');
return new Color(a, rgb.r, rgb.g, rgb.b);
} }
public static fromHSV(a, h, s, l) {
private _componentToHex(component: number): string { return new Color(a, h, s, l, 'hsv');
let hex = component.toString(16);
if (hex.length === 1) {
hex = '0' + hex;
}
return hex;
}
private _normalizeHex(hexStr: string): string {
// we expect this to already has a # as first char as it is supposed to be tested before
if (hexStr.length === 4) {
// Duplicate each char after the #, so "#123" becomes "#112233"
hexStr = hexStr.charAt(0) + hexStr.charAt(1) + hexStr.charAt(1) + hexStr.charAt(2) + hexStr.charAt(2) + hexStr.charAt(3) + hexStr.charAt(3);
}
return hexStr;
} }
public toString(): string { public toString(): string {
@ -247,7 +247,7 @@ export class Color implements definition.Color {
* *
*/ */
public toHsl() { public toHsl() {
const hsl = rgbToHsl(this.r, this.g, this.b); const hsl = rgbToHsl(this.r / 255, this.g / 255, this.b / 255);
return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this.a }; return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this.a };
} }
@ -256,7 +256,7 @@ export class Color implements definition.Color {
* *
*/ */
public toHslString() { public toHslString() {
const hsl = rgbToHsl(this.r, this.g, this.b); const hsl = rgbToHsl(this.r / 255, this.g / 255, this.b / 255);
const h = Math.round(hsl.h * 360), const h = Math.round(hsl.h * 360),
s = Math.round(hsl.s * 100), s = Math.round(hsl.s * 100),
l = Math.round(hsl.l * 100); l = Math.round(hsl.l * 100);
@ -269,7 +269,7 @@ export class Color implements definition.Color {
* *
*/ */
public toHsv() { public toHsv() {
const hsv = rgbToHsv(this.r, this.g, this.b); const hsv = rgbToHsv(this.r / 255, this.g / 255, this.b / 255);
return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this.a }; return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this.a };
} }
@ -278,7 +278,7 @@ export class Color implements definition.Color {
* *
*/ */
public toHsvString() { public toHsvString() {
const hsv = rgbToHsv(this.r, this.g, this.b); const hsv = rgbToHsv(this.r / 255, this.g / 255, this.b / 255);
const h = Math.round(hsv.h * 360), const h = Math.round(hsv.h * 360),
s = Math.round(hsv.s * 100), s = Math.round(hsv.s * 100),
v = Math.round(hsv.v * 100); v = Math.round(hsv.v * 100);
@ -302,10 +302,10 @@ export class Color implements definition.Color {
*/ */
public desaturate(amount: number) { public desaturate(amount: number) {
amount = amount === 0 ? 0 : amount || 10; amount = amount === 0 ? 0 : amount || 10;
const hsl = this.toHsl(); const hsl = rgbToHsl(this.r / 255, this.g / 255, this.b / 255);
hsl.s -= amount / 100; hsl.s -= amount / 100;
hsl.s = Math.min(1, Math.max(0, hsl.s)); hsl.s = Math.min(1, Math.max(0, hsl.s));
return Color.fromHSL(this.a, hsl.h, hsl.s, hsl.l); return Color.fromHSL(this.a, hsl.h * 360, hsl.s * 100, hsl.l * 100);
} }
/** /**
@ -315,10 +315,10 @@ export class Color implements definition.Color {
*/ */
public saturate(amount: number) { public saturate(amount: number) {
amount = amount === 0 ? 0 : amount || 10; amount = amount === 0 ? 0 : amount || 10;
const hsl = this.toHsl(); const hsl = rgbToHsl(this.r / 255, this.g / 255, this.b / 255);
hsl.s += amount / 100; hsl.s += amount / 100;
hsl.s = Math.min(1, Math.max(0, hsl.s)); hsl.s = Math.min(1, Math.max(0, hsl.s));
return Color.fromHSL(this.a, hsl.h, hsl.s, hsl.l); return Color.fromHSL(this.a, hsl.h * 360, hsl.s * 100, hsl.l * 100);
} }
/** /**
@ -336,10 +336,10 @@ export class Color implements definition.Color {
*/ */
public lighten(amount: number) { public lighten(amount: number) {
amount = amount === 0 ? 0 : amount || 10; amount = amount === 0 ? 0 : amount || 10;
const hsl = this.toHsl(); const hsl = rgbToHsl(this.r / 255, this.g / 255, this.b / 255);
hsl.l += amount / 100; hsl.l += amount / 100;
hsl.l = Math.min(1, Math.max(0, hsl.l)); hsl.l = Math.min(1, Math.max(0, hsl.l));
return Color.fromHSL(this.a, hsl.h, hsl.s, hsl.l); return Color.fromHSL(this.a, hsl.h * 360, hsl.s * 100, hsl.l * 100);
} }
/** /**
@ -362,16 +362,14 @@ export class Color implements definition.Color {
*/ */
public darken(amount: number) { public darken(amount: number) {
amount = amount === 0 ? 0 : amount || 10; amount = amount === 0 ? 0 : amount || 10;
const hsl = this.toHsl(); const hsl = rgbToHsl(this.r / 255, this.g / 255, this.b / 255);
hsl.l -= amount / 100; return Color.fromHSL(this.a, hsl.h * 360, hsl.s * 100, Math.min(100, Math.max(0, hsl.l - amount)));
hsl.l = Math.min(1, Math.max(0, hsl.l));
return Color.fromHSL(this.a, hsl.h, hsl.s, hsl.l);
} }
/** /**
* Spin the hue a given amount, from -360 to 360. Calling with 0, 360, or -360 will do nothing (since it sets the hue back to what it was before). * Spin the hue a given amount, from -360 to 360. Calling with 0, 360, or -360 will do nothing (since it sets the hue back to what it was before).
* *
* @param amount (between 0 and 100) * @param amount (between -360 and 360)
*/ */
public spin(amount: number) { public spin(amount: number) {
const hsl = this.toHsl(); const hsl = this.toHsl();
@ -389,25 +387,37 @@ export class Color implements definition.Color {
hsl.h = (hsl.h + 180) % 360; hsl.h = (hsl.h + 180) % 360;
return Color.fromHSL(this.a, hsl.h, hsl.s, hsl.l); return Color.fromHSL(this.a, hsl.h, hsl.s, hsl.l);
} }
static mix(color1: Color, color2: Color, amount = 50) {
const p = amount / 100;
const rgba = {
r: (color2.r - color1.r) * p + color1.r,
g: (color2.g - color1.g) * p + color1.g,
b: (color2.b - color1.b) * p + color1.b,
a: (color2.a - color1.a) * p + color1.a,
};
return new Color(rgba.a, rgba.r, rgba.g, rgba.b);
}
} }
function isRgbOrRgba(value: string): boolean { function isRgbOrRgba(value: string): boolean {
const toLower = value.toLowerCase(); return (value.startsWith('rgb(') || value.startsWith('rgba(')) && value.endsWith(')');
return (toLower.indexOf('rgb(') === 0 || toLower.indexOf('rgba(') === 0) && toLower.indexOf(')') === toLower.length - 1;
} }
function isHslOrHsla(value: string): boolean { function isHslOrHsla(value: string): boolean {
const toLower = value.toLowerCase(); return (value.startsWith('hsl') || value.startsWith('hsla(')) && value.endsWith(')');
}
return (toLower.indexOf('hsl(') === 0 || toLower.indexOf('hsla(') === 0) && toLower.indexOf(')') === toLower.length - 1; function isHsvOrHsva(value: string): boolean {
return (value.startsWith('hsv') || value.startsWith('hsva(')) && value.endsWith(')');
} }
function parseColorWithAlpha(value: string): any { function parseColorWithAlpha(value: string): any {
const toLower = value.toLowerCase(); const parts = value
const parts = toLower .replace(/(rgb|hsl|hsv)a?\(/, '')
.replace(/(rgb|hsl)a?\(/, '')
.replace(')', '') .replace(')', '')
.replace(/%/g, '')
.trim() .trim()
.split(','); .split(',');
@ -444,14 +454,22 @@ function argbFromRgbOrRgba(value: string): number {
function argbFromHslOrHsla(value: string): number { function argbFromHslOrHsla(value: string): number {
const { f: h, s: s, t: l, a } = parseColorWithAlpha(value); const { f: h, s: s, t: l, a } = parseColorWithAlpha(value);
const { r, g, b } = convertHSLToRGBColor(h, s, l); const { r, g, b } = hslToRgb(h, s, l);
return (a & 0xff) * 0x01000000 + (r & 0xff) * 0x00010000 + (g & 0xff) * 0x00000100 + (b & 0xff);
}
function argbFromHsvOrHsva(value: string): number {
const { f: h, s: s, t: v, a } = parseColorWithAlpha(value);
const { r, g, b } = hsvToRgb(h, s, v);
return (a & 0xff) * 0x01000000 + (r & 0xff) * 0x00010000 + (g & 0xff) * 0x00000100 + (b & 0xff); return (a & 0xff) * 0x01000000 + (r & 0xff) * 0x00010000 + (g & 0xff) * 0x00000100 + (b & 0xff);
} }
// `rgbToHsl` // `rgbToHsl`
// Converts an RGB color value to HSL. // Converts an RGB color value to HSL.
// *Assumes:* r, g, and b are contained in [0, 255] or [0, 1] // *Assumes:* r, g, and b are contained in [0, 1]
// *Returns:* { h, s, l } in [0,1] // *Returns:* { h, s, l } in [0,1]
function rgbToHsl(r, g, b) { function rgbToHsl(r, g, b) {
const max = Math.max(r, g, b), const max = Math.max(r, g, b),
@ -493,9 +511,12 @@ function hue2rgb(p, q, t) {
// `hslToRgb` // `hslToRgb`
// Converts an HSL color value to RGB. // Converts an HSL color value to RGB.
// *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100] // *Assumes:* h is contained in [0, 360] and s and l are contained [0, 100]
// *Returns:* { r, g, b } in the set [0, 255] // *Returns:* { r, g, b } in the set [0, 255]
function hslToRgb(h, s, l) { function hslToRgb(h1, s1, l1) {
const h = (h1 % 360) / 360;
const s = s1 / 100;
const l = l1 / 100;
let r, g, b; let r, g, b;
if (s === 0) { if (s === 0) {
r = g = b = l; // achromatic r = g = b = l; // achromatic
@ -512,7 +533,7 @@ function hslToRgb(h, s, l) {
// `rgbToHsv` // `rgbToHsv`
// Converts an RGB color value to HSV // Converts an RGB color value to HSV
// *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1] // *Assumes:* r, g, and b are contained in the set [0, 1]
// *Returns:* { h, s, v } in [0,1] // *Returns:* { h, s, v } in [0,1]
function rgbToHsv(r, g, b) { function rgbToHsv(r, g, b) {
const max = Math.max(r, g, b), const max = Math.max(r, g, b),
@ -541,3 +562,25 @@ function rgbToHsv(r, g, b) {
} }
return { h: h, s: s, v: v }; return { h: h, s: s, v: v };
} }
// `hsvToRgb`
// Converts an HSV color value to RGB.
// *Assumes:* h is contained in [0, 360] and s and v are contained [0, 100]
// *Returns:* { r, g, b } in the set [0, 255]
function hsvToRgb(h1, s1, v1) {
const h = ((h1 % 360) / 360) * 6;
const s = s1 / 100;
const v = v1 / 100;
var i = Math.floor(h),
f = h - i,
p = v * (1 - s),
q = v * (1 - f * s),
t = v * (1 - (1 - f) * s),
mod = i % 6,
r = [v, q, p, p, t, v][mod],
g = [t, v, v, q, p, p][mod],
b = [p, p, t, v, v, q][mod];
return { r: r * 255, g: g * 255, b: b * 255 };
}

View File

@ -1,11 +1,11 @@
/** /**
* Represents a color object. Stores all color components (alpha (opacity), red, green, blue) in a [0..255] range. * Represents a color object. Stores all color components (alpha (opacity), red, green, blue) in a [0..255] range.
*/ */
export class Color { export class Color {
constructor(knownColor: string); constructor(knownColor: string);
constructor(hex: string); constructor(hex: string);
constructor(argb: number); constructor(argb: number);
constructor(alpha: number, red: number, green: number, blue: number); constructor(alpha: number, red: number, green: number, blue: number, type?: 'rgb' | 'hsl' | 'hsv');
/** /**
* Gets the Alpha component (in the [0, 255] range) of this color. This is a read-only property. * Gets the Alpha component (in the [0, 255] range) of this color. This is a read-only property.
@ -190,4 +190,17 @@ export class Color {
* *
*/ */
public complement(): Color; public complement(): Color;
/**
* returns the color complement
*
*/
public static mix(color1: Color, color2: Color, amount: number): Color;
/**
* returns a new Color from HSL
*
*/
public static fromHSL(a, h, s, l): Color;
public static fromHSV(a, h, s, l): Color;
} }

View File

@ -1,9 +1,9 @@
import { Color } from '../color';
import { getKnownColor } from '../color/known-colors'; import { getKnownColor } from '../color/known-colors';
export type Parsed<V> = { start: number; end: number; value: V }; export type Parsed<V> = { start: number; end: number; value: V };
// Values // Values
export type ARGB = number;
export type URL = string; export type URL = string;
export type Angle = number; export type Angle = number;
export interface Unit<T> { export interface Unit<T> {
@ -15,7 +15,7 @@ export type Percentage = Unit<'%'>;
export type LengthPercentage = Length | Percentage; export type LengthPercentage = Length | Percentage;
export type Keyword = string; export type Keyword = string;
export interface ColorStop { export interface ColorStop {
argb: ARGB; color: Color;
offset?: LengthPercentage; offset?: LengthPercentage;
} }
export interface LinearGradient { export interface LinearGradient {
@ -67,59 +67,31 @@ export function parseURL(text: string, start = 0): Parsed<URL> {
return { start, end, value }; return { start, end, value };
} }
const hexColorRegEx = /\s*#((?:[0-9A-F]{8})|(?:[0-9A-F]{6})|(?:[0-9A-F]{3}))\s*/giy; const hexColorRegEx = /\s*#((?:[0-9A-F]{8})|(?:[0-9A-F]{6})|(?:[0-9A-F]{4})|(?:[0-9A-F]{3}))\s*/giy;
export function parseHexColor(text: string, start = 0): Parsed<ARGB> { export function parseHexColor(text: string, start = 0): Parsed<Color> {
hexColorRegEx.lastIndex = start; hexColorRegEx.lastIndex = start;
const result = hexColorRegEx.exec(text); const result = hexColorRegEx.exec(text);
if (!result) { if (!result) {
return null; return null;
} }
const end = hexColorRegEx.lastIndex; const end = hexColorRegEx.lastIndex;
const hex = result[1]; return { start, end, value: new Color('#'+ result[1]) };
let argb;
if (hex.length === 8) {
argb = parseInt('0x' + hex);
} else if (hex.length === 6) {
argb = parseInt('0xFF' + hex);
} else if (hex.length === 3) {
argb = parseInt('0xFF' + hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]);
}
return { start, end, value: argb };
} }
function rgbaToArgbNumber(r: number, g: number, b: number, a = 1): number | undefined { const cssColorRegEx = /\s*((?:rgb|rgba|hsl|hsla|hsv|hsva)\([^\(\)]\))/gy;
if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255 && a >= 0 && a <= 1) { export function parseCssColor(text: string, start = 0): Parsed<Color> {
return Math.round(a * 0xff) * 0x01000000 + r * 0x010000 + g * 0x000100 + b; cssColorRegEx.lastIndex = start;
} else { const result = cssColorRegEx.exec(text);
return null;
}
}
const rgbColorRegEx = /\s*(rgb\(\s*(\d*)\s*,\s*(\d*)\s*,\s*(\d*)\s*\))/gy;
export function parseRGBColor(text: string, start = 0): Parsed<ARGB> {
rgbColorRegEx.lastIndex = start;
const result = rgbColorRegEx.exec(text);
if (!result) { if (!result) {
return null; return null;
} }
const end = rgbColorRegEx.lastIndex; const end = cssColorRegEx.lastIndex;
const value = result[1] && rgbaToArgbNumber(parseInt(result[2]), parseInt(result[3]), parseInt(result[4])); try {
return { start, end, value: new Color(text) };
return { start, end, value }; } catch {
}
const rgbaColorRegEx = /\s*(rgba\(\s*(\d*)\s*,\s*(\d*)\s*,\s*(\d*)\s*,\s*([01]?\.?\d*)\s*\))/gy;
export function parseRGBAColor(text: string, start = 0): Parsed<ARGB> {
rgbaColorRegEx.lastIndex = start;
const result = rgbaColorRegEx.exec(text);
if (!result) {
return null; return null;
} }
const end = rgbaColorRegEx.lastIndex;
const value = rgbaToArgbNumber(parseInt(result[2]), parseInt(result[3]), parseInt(result[4]), parseFloat(result[5]));
return { start, end, value };
} }
export function convertHSLToRGBColor(hue: number, saturation: number, lightness: number): { r: number; g: number; b: number } { export function convertHSLToRGBColor(hue: number, saturation: number, lightness: number): { r: number; g: number; b: number } {
@ -160,49 +132,19 @@ export function convertHSLToRGBColor(hue: number, saturation: number, lightness:
}; };
} }
function hslaToArgbNumber(h: number, s: number, l: number, a = 1): number | undefined {
const { r, g, b } = convertHSLToRGBColor(h, s, l);
return rgbaToArgbNumber(r, g, b, a);
}
const hslColorRegEx = /\s*(hsl\(\s*([\d.]*)\s*,\s*([\d.]*)%\s*,\s*([\d.]*)%\s*\))/gy; export function parseColorKeyword(value, start: number, keyword = parseKeyword(value, start)): Parsed<Color> {
export function parseHSLColor(text: string, start = 0): Parsed<ARGB> {
hslColorRegEx.lastIndex = start;
const result = hslColorRegEx.exec(text);
if (!result) {
return null;
}
const end = hslColorRegEx.lastIndex;
const value = result[1] && hslaToArgbNumber(parseFloat(result[2]), parseFloat(result[3]), parseFloat(result[4]));
return { start, end, value };
}
const hslaColorRegEx = /\s*(hsla\(\s*([\d.]*)\s*,\s*([\d.]*)%\s*,\s*([\d.]*)%\s*,\s*([01]?\.?\d*)\s*\))/gy;
export function parseHSLAColor(text: string, start = 0): Parsed<ARGB> {
hslaColorRegEx.lastIndex = start;
const result = hslaColorRegEx.exec(text);
if (!result) {
return null;
}
const end = hslaColorRegEx.lastIndex;
const value = hslaToArgbNumber(parseFloat(result[2]), parseFloat(result[3]), parseFloat(result[4]), parseFloat(result[5]));
return { start, end, value };
}
export function parseColorKeyword(value, start: number, keyword = parseKeyword(value, start)): Parsed<ARGB> {
const parseColor = keyword && getKnownColor(keyword.value); const parseColor = keyword && getKnownColor(keyword.value);
if (parseColor != null) { if (parseColor != null) {
const end = keyword.end; const end = keyword.end;
const value = parseColor; const value = parseColor;
return { start, end, value }; return { start, end, value: new Color(parseColor) };
} }
return null; return null;
} }
export function parseColor(value: string, start = 0, keyword = parseKeyword(value, start)): Parsed<ARGB> { export function parseColor(value: string, start = 0, keyword = parseKeyword(value, start)): Parsed<Color> {
return parseHexColor(value, start) || parseColorKeyword(value, start, keyword) || parseRGBColor(value, start) || parseRGBAColor(value, start) || parseHSLColor(value, start) || parseHSLAColor(value, start); return parseHexColor(value, start) || parseColorKeyword(value, start, keyword) || parseCssColor(value, start);
} }
const keywordRegEx = /\s*([a-z][\w\-]*)\s*/giy; const keywordRegEx = /\s*([a-z][\w\-]*)\s*/giy;
@ -558,11 +500,11 @@ export function parseColorStop(text: string, start = 0): Parsed<ColorStop> {
return { return {
start, start,
end, end,
value: { argb: color.value, offset: offset.value }, value: { color: color.value, offset: offset.value },
}; };
} }
return { start, end, value: { argb: color.value } }; return { start, end, value: { color: color.value } };
} }
const linearGradientStartRegEx = /\s*linear-gradient\s*/gy; const linearGradientStartRegEx = /\s*linear-gradient\s*/gy;

View File

@ -22,7 +22,7 @@ export class LinearGradient {
} }
return { return {
color: new Color(color.argb), color: color.color,
offset: offsetUnit, offset: offsetUnit,
}; };
}); });