diff --git a/lib/extract/extractColor.js b/lib/extract/extractColor.js index 48fd7f78..436dbbf5 100644 --- a/lib/extract/extractColor.js +++ b/lib/extract/extractColor.js @@ -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; }