mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-15 11:01:21 +08:00
feat(css): Add HSL/HSLA support (#7730)
This commit is contained in:

committed by
Manol Donev

parent
f438ad7d69
commit
3cabdde05f
@ -1,6 +1,7 @@
|
||||
import * as definition from ".";
|
||||
import * as definition from ".";
|
||||
import * as types from "../utils/types";
|
||||
import * as knownColors from "./known-colors";
|
||||
import { convertHSLToRGBColor } from "tns-core-modules/css/parser";
|
||||
|
||||
const SHARP = "#";
|
||||
const HEX_REGEX = /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)|(^#[0-9A-F]{8}$)/i;
|
||||
@ -18,6 +19,8 @@ export class Color implements definition.Color {
|
||||
if (types.isString(arg)) {
|
||||
if (isRgbOrRgba(arg)) {
|
||||
this._argb = argbFromRgbOrRgba(arg);
|
||||
} else if (isHslOrHsla(arg)) {
|
||||
this._argb = argbFromHslOrHsla(arg);
|
||||
} else if (knownColors.isKnownName(arg)) {
|
||||
// The parameter is a known color name
|
||||
const hex = knownColors.getKnownColor(arg);
|
||||
@ -127,7 +130,7 @@ export class Color implements definition.Color {
|
||||
return true;
|
||||
}
|
||||
|
||||
return HEX_REGEX.test(value) || isRgbOrRgba(value);
|
||||
return HEX_REGEX.test(value) || isRgbOrRgba(value) || isHslOrHsla(value);
|
||||
}
|
||||
|
||||
private _componentToHex(component: number): string {
|
||||
@ -162,33 +165,58 @@ function isRgbOrRgba(value: string): boolean {
|
||||
return (toLower.indexOf("rgb(") === 0 || toLower.indexOf("rgba(") === 0) && toLower.indexOf(")") === (toLower.length - 1);
|
||||
}
|
||||
|
||||
function argbFromRgbOrRgba(value: string): number {
|
||||
function isHslOrHsla(value: string): boolean {
|
||||
const toLower = value.toLowerCase();
|
||||
const parts = toLower.replace("rgba(", "").replace("rgb(", "").replace(")", "").trim().split(",");
|
||||
|
||||
let r = 255;
|
||||
let g = 255;
|
||||
let b = 255;
|
||||
return (toLower.indexOf("hsl(") === 0 || toLower.indexOf("hsla(") === 0) && toLower.indexOf(")") === (toLower.length - 1);
|
||||
}
|
||||
|
||||
function parseColorWithAlpha(value: string): any {
|
||||
const toLower = value.toLowerCase();
|
||||
const parts = toLower.replace(/(rgb|hsl)a?\(/, "")
|
||||
.replace(")", "")
|
||||
.trim().split(",");
|
||||
|
||||
let f = 255;
|
||||
let s = 255;
|
||||
let t = 255;
|
||||
let a = 255;
|
||||
|
||||
if (parts[0]) {
|
||||
r = parseInt(parts[0].trim());
|
||||
f = parseInt(parts[0].trim());
|
||||
}
|
||||
|
||||
if (parts[1]) {
|
||||
g = parseInt(parts[1].trim());
|
||||
s = parseInt(parts[1].trim());
|
||||
}
|
||||
|
||||
if (parts[2]) {
|
||||
b = parseInt(parts[2].trim());
|
||||
t = parseInt(parts[2].trim());
|
||||
}
|
||||
|
||||
if (parts[3]) {
|
||||
a = Math.round(parseFloat(parts[3].trim()) * 255);
|
||||
}
|
||||
|
||||
return { f, s, t, a };
|
||||
}
|
||||
|
||||
function argbFromRgbOrRgba(value: string): number {
|
||||
const { f: r, s: g, t: b, a } = parseColorWithAlpha(value);
|
||||
|
||||
return (a & 0xFF) * 0x01000000
|
||||
+ (r & 0xFF) * 0x00010000
|
||||
+ (g & 0xFF) * 0x00000100
|
||||
+ (b & 0xFF) * 0x00000001;
|
||||
+ (b & 0xFF);
|
||||
}
|
||||
|
||||
function argbFromHslOrHsla(value: string): number {
|
||||
const { f: h, s: s, t: l, a } = parseColorWithAlpha(value);
|
||||
|
||||
const { r, g, b } = convertHSLToRGBColor(h, s, l);
|
||||
|
||||
return (a & 0xFF) * 0x01000000
|
||||
+ (r & 0xFF) * 0x00010000
|
||||
+ (g & 0xFF) * 0x00000100
|
||||
+ (b & 0xFF);
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ export function parseHexColor(text: string, start: number = 0): Parsed<ARGB> {
|
||||
|
||||
function rgbaToArgbNumber(r: number, g: number, b: number, a: number = 1): number | undefined {
|
||||
if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255 && a >= 0 && a <= 1) {
|
||||
return (Math.round(a * 0xFF) * 0x01000000) + (r * 0x010000) + (g * 0x000100) + (b * 0x000001);
|
||||
return (Math.round(a * 0xFF) * 0x01000000) + (r * 0x010000) + (g * 0x000100) + b;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@ -116,6 +116,67 @@ export function parseRGBAColor(text: string, start: number = 0): Parsed<ARGB> {
|
||||
return { start, end, value };
|
||||
}
|
||||
|
||||
export function convertHSLToRGBColor(hue: number, saturation: number, lightness: number): { r: number; g: number; b: number; } {
|
||||
// Per formula it will be easier if hue is divided to 60° and saturation to 100 beforehand
|
||||
// https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_RGB
|
||||
hue /= 60;
|
||||
lightness /= 100;
|
||||
|
||||
let chroma = (1 - Math.abs(2 * lightness - 1)) * saturation / 100,
|
||||
X = chroma * (1 - Math.abs(hue % 2 - 1)),
|
||||
// Add lightness match to all RGB components beforehand
|
||||
{ m: r, m: g, m: b } = { m: lightness - chroma / 2 };
|
||||
|
||||
if (0 <= hue && hue < 1) { r += chroma; g += X; }
|
||||
else if (hue < 2) { r += X; g += chroma; }
|
||||
else if (hue < 3) { g += chroma; b += X; }
|
||||
else if (hue < 4) { g += X; b += chroma; }
|
||||
else if (hue < 5) { r += X; b += chroma; }
|
||||
else if (hue < 6) { r += chroma; b += X; }
|
||||
|
||||
return {
|
||||
r: Math.round(r * 0xFF),
|
||||
g: Math.round(g * 0xFF),
|
||||
b: Math.round(b * 0xFF)
|
||||
};
|
||||
}
|
||||
|
||||
function hslaToArgbNumber(h: number, s: number, l: number, a: number = 1): number | undefined {
|
||||
let { r, g, b } = convertHSLToRGBColor(h, s, l);
|
||||
|
||||
if (r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255 && a >= 0 && a <= 1) {
|
||||
return (Math.round(a * 0xFF) * 0x01000000) + (r * 0x010000) + (g * 0x000100) + b;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
const hslColorRegEx = /\s*(hsl\(\s*([\d.]*)\s*,\s*([\d.]*)%\s*,\s*([\d.]*)%\s*\))/gy;
|
||||
export function parseHSLColor(text: string, start: number = 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: number = 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 enum colors {
|
||||
transparent = 0x00000000,
|
||||
aliceblue = 0xFFF0F8FF,
|
||||
@ -280,7 +341,12 @@ export function parseColorKeyword(value, start: number, keyword = parseKeyword(v
|
||||
}
|
||||
|
||||
export function parseColor(value: string, start: number = 0, keyword = parseKeyword(value, start)): Parsed<ARGB> {
|
||||
return parseHexColor(value, start) || parseColorKeyword(value, start, keyword) || parseRGBColor(value, start) || parseRGBAColor(value, start);
|
||||
return parseHexColor(value, start) ||
|
||||
parseColorKeyword(value, start, keyword) ||
|
||||
parseRGBColor(value, start) ||
|
||||
parseRGBAColor(value, start) ||
|
||||
parseHSLColor(value, start) ||
|
||||
parseHSLAColor(value, start);
|
||||
}
|
||||
|
||||
const keywordRegEx = /\s*([a-z][\w\-]*)\s*/giy;
|
||||
|
@ -56,6 +56,8 @@ describe("css", () => {
|
||||
test(parseColor, " #85456789 ", { start: 0, end: 12, value: 0x85456789 });
|
||||
test(parseColor, " rgb(255, 8, 128) ", { start: 0, end: 18, value: 0xFFFF0880 });
|
||||
test(parseColor, " rgba(255, 8, 128, 0.5) ", { start: 0, end: 24, value: 0x80FF0880 });
|
||||
test(parseColor, " hsl(330.9, 100%, 51.6%) ", { start: 0, end: 25, value: 0xFFFF0880 });
|
||||
test(parseColor, " hsla(330.9, 100%, 51.6%, 0.5) ", { start: 0, end: 31, value: 0x80FF0880 });
|
||||
test(parseColor, "#FF0000 url(lucky.gif)", 8, null);
|
||||
test(parseColor, "url(lucky.gif) #FF0000 repeat", 15, { start: 15, end: 23, value: 0xFFFF0000 });
|
||||
});
|
||||
|
Reference in New Issue
Block a user