Optimize color extraction, allow passing color arrays to avoid parsing

This commit is contained in:
Mikael Sand
2019-01-29 20:05:40 +02:00
parent f1372842a7
commit ab9633f81a
+123 -136
View File
@@ -149,124 +149,6 @@ const colorNames = {
yellowgreen: [154, 205, 50],
};
function clamp(num, min, max) {
return Math.min(Math.max(min, num), max);
}
const abbr = /^#([a-f0-9]{3,4})$/i;
const hex = /^#([a-f0-9]{6})([a-f0-9]{2})?$/i;
const rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d.]+)\s*)?\)$/;
const per = /^rgba?\(\s*([+-]?[\d.]+)%\s*,\s*([+-]?[\d.]+)%\s*,\s*([+-]?[\d.]+)%\s*(?:,\s*([+-]?[\d.]+)\s*)?\)$/;
const keyword = /(\D+)/;
function rgbFromString(string) {
let rgb = [0, 0, 0, 1];
let match;
let i;
let hexAlpha;
if ((match = string.match(hex))) {
hexAlpha = match[2];
match = match[1];
for (i = 0; i < 3; i++) {
// https://jsperf.com/slice-vs-substr-vs-substring-methods-long-string/19
const i2 = i * 2;
rgb[i] = parseInt(match.slice(i2, i2 + 2), 16);
}
if (hexAlpha) {
rgb[3] = Math.round((parseInt(hexAlpha, 16) / 255) * 100) / 100;
}
} else if ((match = string.match(abbr))) {
match = match[1];
hexAlpha = match[3];
for (i = 0; i < 3; i++) {
rgb[i] = parseInt(match[i] + match[i], 16);
}
if (hexAlpha) {
rgb[3] =
Math.round((parseInt(hexAlpha + hexAlpha, 16) / 255) * 100) / 100;
}
} else if ((match = string.match(rgba))) {
for (i = 0; i < 3; i++) {
rgb[i] = parseInt(match[i + 1], 0);
}
if (match[4]) {
rgb[3] = parseFloat(match[4]);
}
} else if ((match = string.match(per))) {
for (i = 0; i < 3; i++) {
rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55);
}
if (match[4]) {
rgb[3] = parseFloat(match[4]);
}
} else if ((match = string.match(keyword))) {
if (match[1] === 'transparent') {
return [0, 0, 0, 0];
}
rgb = colorNames[match[1]];
if (!rgb) {
return null;
}
rgb[3] = 1;
return rgb;
} else {
return null;
}
for (i = 0; i < 3; i++) {
rgb[i] = clamp(rgb[i], 0, 255);
}
rgb[3] = clamp(rgb[3], 0, 1);
return rgb;
}
const hslRegEx = /^hsla?\(\s*([+-]?(?:\d*\.)?\d+)(?:deg)?\s*,\s*([+-]?[\d.]+)%\s*,\s*([+-]?[\d.]+)%\s*(?:,\s*([+-]?[\d.]+)\s*)?\)$/;
function hslFromString(string) {
const match = string.match(hslRegEx);
if (match) {
const alpha = parseFloat(match[4]);
const h = (parseFloat(match[1]) + 360) % 360;
const s = clamp(parseFloat(match[2]), 0, 100);
const l = clamp(parseFloat(match[3]), 0, 100);
const a = clamp(isNaN(alpha) ? 1 : alpha, 0, 1);
return [h, s, l, a];
}
return null;
}
const hwbRegEx = /^hwb\(\s*([+-]?\d*[.]?\d+)(?:deg)?\s*,\s*([+-]?[\d.]+)%\s*,\s*([+-]?[\d.]+)%\s*(?:,\s*([+-]?[\d.]+)\s*)?\)$/;
function hwbFromString(string) {
const match = string.match(hwbRegEx);
if (match) {
const alpha = parseFloat(match[4]);
const h = ((parseFloat(match[1]) % 360) + 360) % 360;
const w = clamp(parseFloat(match[2]), 0, 100);
const b = clamp(parseFloat(match[3]), 0, 100);
const a = clamp(isNaN(alpha) ? 1 : alpha, 0, 1);
return [h, w, b, a];
}
return null;
}
function hslToRgb(hsl) {
const h = hsl[0] / 360;
const s = hsl[1] / 100;
@@ -383,32 +265,137 @@ function hwbToRgb(hwb) {
return [r * 255, g * 255, b * 255];
}
export default function extractColor(string) {
if (!string || typeof string !== 'string') {
function clamp(num, min, max) {
return Math.min(Math.max(min, num), max);
}
const abbr = /^#([a-f0-9]{3,4})$/i;
const hex = /^#([a-f0-9]{6})([a-f0-9]{2})?$/i;
const rgba = /^rgba?\(\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*,\s*([+-]?\d+)\s*(?:,\s*([+-]?[\d.]+)\s*)?\)$/;
const per = /^rgba?\(\s*([+-]?[\d.]+)%\s*,\s*([+-]?[\d.]+)%\s*,\s*([+-]?[\d.]+)%\s*(?:,\s*([+-]?[\d.]+)\s*)?\)$/;
const keyword = /(\D+)/;
function rgbFromString(string) {
let rgb = [0, 0, 0, 1];
let match;
let i;
let hexAlpha;
if ((match = string.match(hex))) {
hexAlpha = match[2];
match = match[1];
for (i = 0; i < 3; i++) {
// https://jsperf.com/slice-vs-substr-vs-substring-methods-long-string/19
const i2 = i * 2;
rgb[i] = parseInt(match.slice(i2, i2 + 2), 16);
}
if (hexAlpha) {
rgb[3] = Math.round((parseInt(hexAlpha, 16) / 255) * 100) / 100;
}
} else if ((match = string.match(abbr))) {
match = match[1];
hexAlpha = match[3];
for (i = 0; i < 3; i++) {
rgb[i] = parseInt(match[i] + match[i], 16);
}
if (hexAlpha) {
rgb[3] =
Math.round((parseInt(hexAlpha + hexAlpha, 16) / 255) * 100) / 100;
}
} else if ((match = string.match(rgba))) {
for (i = 0; i < 3; i++) {
rgb[i] = parseInt(match[i + 1], 0);
}
if (match[4]) {
rgb[3] = parseFloat(match[4]);
}
} else if ((match = string.match(per))) {
for (i = 0; i < 3; i++) {
rgb[i] = Math.round(parseFloat(match[i + 1]) * 2.55);
}
if (match[4]) {
rgb[3] = parseFloat(match[4]);
}
} else if ((match = string.match(keyword))) {
if (match[1] === 'transparent') {
return [0, 0, 0, 0];
}
rgb = colorNames[match[1]];
if (!rgb) {
return null;
}
rgb[3] = 1;
return rgb;
} else {
return null;
}
for (i = 0; i < 3; i++) {
rgb[i] = clamp(rgb[i], 0, 255);
}
rgb[3] = clamp(rgb[3], 0, 1);
return rgb;
}
const hslRegEx = /^hsla?\(\s*([+-]?(?:\d*\.)?\d+)(?:deg)?\s*,\s*([+-]?[\d.]+)%\s*,\s*([+-]?[\d.]+)%\s*(?:,\s*([+-]?[\d.]+)\s*)?\)$/;
function rgbFromHslString(string) {
const match = string.match(hslRegEx);
if (match) {
const alpha = parseFloat(match[4]);
const h = (parseFloat(match[1]) + 360) % 360;
const s = clamp(parseFloat(match[2]), 0, 100);
const l = clamp(parseFloat(match[3]), 0, 100);
const a = clamp(isNaN(alpha) ? 1 : alpha, 0, 1);
return hslToRgb([h, s, l, a]);
}
return null;
}
const hwbRegEx = /^hwb\(\s*([+-]?\d*[.]?\d+)(?:deg)?\s*,\s*([+-]?[\d.]+)%\s*,\s*([+-]?[\d.]+)%\s*(?:,\s*([+-]?[\d.]+)\s*)?\)$/;
function rgbFromHwbString(string) {
const match = string.match(hwbRegEx);
if (match) {
const alpha = parseFloat(match[4]);
const h = ((parseFloat(match[1]) % 360) + 360) % 360;
const w = clamp(parseFloat(match[2]), 0, 100);
const b = clamp(parseFloat(match[3]), 0, 100);
const a = clamp(isNaN(alpha) ? 1 : alpha, 0, 1);
return hwbToRgb([h, w, b, a]);
}
return null;
}
export default function extractColor(string) {
if (typeof string !== 'string') {
return string;
}
const prefix = string.substring(0, 3).toLowerCase();
let value;
let converter;
switch (prefix) {
case 'hsl':
value = hslFromString(string);
converter = hslToRgb;
break;
return rgbFromHslString(string);
case 'hwb':
value = hwbFromString(string);
converter = hwbToRgb;
break;
return rgbFromHwbString(string);
default:
value = rgbFromString(string);
break;
return rgbFromString(string);
}
if (!value) {
return null;
}
return converter ? converter(value).concat(value[3]) : value;
}