UNPKG

tiny-essentials

Version:

Collection of small, essential scripts designed to be used across various projects. These simple utilities are crafted for speed, ease of use, and versatility.

579 lines (518 loc) 14.9 kB
'use strict'; /** * Represents a color in RGBA format. * Each element must be a number between 0 and 255. * The fourth value represents the alpha (transparency) channel. * * @typedef {number[]} RgbaColor * @property {number} 0 - Red component (0–255) * @property {number} 1 - Green component (0–255) * @property {number} 2 - Blue component (0–255) * @property {number} 3 - Alpha component (0–255) */ /** * Represents a color in RGB format. * Each element must be a number between 0 and 255. * * @typedef {number[]} RgbColor * @property {number} 0 - Red component (0–255) * @property {number} 1 - Green component (0–255) * @property {number} 2 - Blue component (0–255) */ /** * Represents a color in HSLA format. * The fourth value represents the alpha (transparency) channel. * * @typedef {number[]} HslaColor * @property {number} 0 - Hue (0–360) * @property {number} 1 - Saturation (0–100) * @property {number} 2 - Lightness (0–100) * @property {number} 3 - Alpha component (0–255) */ /** * Represents a color in HSL format. * * @typedef {number[]} HslColor * @property {number} 0 - Hue (0–360) * @property {number} 1 - Saturation (0–100) * @property {number} 2 - Lightness (0–100) */ /** * Represents a hex color. * * @typedef {string} HexColor */ /** * A union type representing various accepted color formats. * Can be a hex color string, a numeric value, or an array-based RGB/RGBA representation. * * @typedef {HexColor | number | RgbColor | RgbaColor} ColorTypes */ /** * @typedef {Object} RgbaResult * @property {number} r - Red component (0–255) * @property {number} g - Green component (0–255) * @property {number} b - Blue component (0–255) * @property {number} a - Alpha component (0–255) */ /** * @typedef {Object} RgbResult * @property {number} r - Red component (0–255) * @property {number} g - Green component (0–255) * @property {number} b - Blue component (0–255) */ /** * @typedef {Object} HexResult * @property {string} hex - Hex color */ /** * @typedef {Object} HslResult * @property {number} h - Hue (0–360) * @property {number} s - Saturation (0–100) * @property {number} l - Lightness (0–100) */ /** * A class that allows converting colors between all common formats. */ class TinyColorConverter { /** * Generates a smooth gradient of colors based on sine wave patterns. * * @see {@link https://www.npmjs.com/package/rainbow-colors-array} Code Reference * @param {number} [len=24] - The number of colors to generate. * @param {'rgb'|'hex'|'hsl'} [type='rgb'] - The format of the colors returned: `'rgb'`, `'hex'`, or `'hsl'`. * @param {boolean} [pastel=false] - If true, generates pastel tones by adjusting the intensity and offset. * @returns {Array<RgbResult|HexResult|HslResult>} An array of color values in the selected format: */ static _rca(len, type, pastel) { let eq1 = 127; let eq2 = 128; if (len === undefined) { len = 24; } if (type === undefined) { type = 'rgb'; } if (pastel === true) { eq1 = 55; eq2 = 200; } const frequency = (Math.PI * 2) / len; const cvparr = []; for (let i = 0; i < len; ++i) { const red = Math.sin(frequency * i + 2) * eq1 + eq2; const green = Math.sin(frequency * i + 0) * eq1 + eq2; const blue = Math.sin(frequency * i + 4) * eq1 + eq2; switch (type) { case 'hex': cvparr.push({ hex: this.rgbToHex(Math.round(red), Math.round(green), Math.round(blue)) }); break; case 'rgb': cvparr.push({ r: red, g: green, b: blue }); break; case 'hsl': const [h, s, l] = this.rgbaToHsl(Math.round(red), Math.round(green), Math.round(blue)); cvparr.push({ h, s, l }); break; } } return cvparr; } /** * Generates a smooth gradient of colors based on sine wave patterns. * * @param {number} [len=24] - The number of colors to generate. * @param {boolean} [pastel=false] - If true, generates pastel tones by adjusting the intensity and offset. * @returns {RgbResult[]} An array of rgb color values. */ static rcaRgb(len, pastel) { return /** @type {RgbResult[]} */ (TinyColorConverter._rca(len, 'rgb', pastel)); } /** * Generates a smooth gradient of colors based on sine wave patterns. * * @param {number} [len=24] - The number of colors to generate. * @param {boolean} [pastel=false] - If true, generates pastel tones by adjusting the intensity and offset. * @returns {HslResult[]} An array of hsl color values. */ static rcaHsl(len, pastel) { return /** @type {HslResult[]} */ (TinyColorConverter._rca(len, 'hsl', pastel)); } /** * Generates a smooth gradient of colors based on sine wave patterns. * * @param {number} [len=24] - The number of colors to generate. * @param {boolean} [pastel=false] - If true, generates pastel tones by adjusting the intensity and offset. * @returns {HexResult[]} An array of hex color values. */ static rcaHex(len, pastel) { return /** @type {HexResult[]} */ (TinyColorConverter._rca(len, 'hex', pastel)); } /** * Generates a random color in hexadecimal format. * * @returns {HexColor} A hex color string (e.g. `#a3e5f2`). */ static randomColor() { const hex = Math.floor(Math.random() * 0x1000000).toString(16); return `#${hex.padStart(6, '0')}`; } /** * Parses input into RGBA array. * @param {ColorTypes} input * @param {boolean} isHsl * @returns {RgbaColor} */ static parseInput(input, isHsl) { if (typeof input === 'string') { input = input.trim().toLowerCase(); if (input.startsWith('#')) return this.hexToRgba(input); if (input.startsWith('rgb')) return this.rgbStringToRgbaArray(input); if (input.startsWith('hsl')) return this.hslStringToRgbaArray(input); } if (typeof input === 'number') return this.intToRgba(input); if ( Array.isArray(input) && (input.length === 3 || input.length === 4) && input.every((item) => typeof item === 'number') ) { if (isHsl) { const [h, s, l, a2] = input; if (h <= 360 && s <= 100 && l <= 100) return this.hslToRgba(h, s, l, a2); } return [...input, 1].slice(0, 4); } throw new Error('Unsupported color format.'); } // HSL Color /** * Converts hsl to integer. * @param {number} h - Hue (0–360) * @param {number} s - Saturation (0–100) * @param {number} l - Lightness (0–100) * @returns {number} */ static hslToInt(h, s, l) { const [r, g, b] = TinyColorConverter.hslToRgba(h, s, l); return TinyColorConverter.rgbToInt(r, g, b); } /** * Converts hsl to hex. * @param {number} h - Hue (0–360) * @param {number} s - Saturation (0–100) * @param {number} l - Lightness (0–100) * @returns {HexColor} */ static hslToHex(h, s, l) { const [r, g, b] = TinyColorConverter.hslToRgba(h, s, l); return TinyColorConverter.rgbToHex(r, g, b); } /** * Converts hsl(a) string to RGBA array. * @param {string} hsl * @returns {RgbaColor} */ static hslStringToRgbaArray(hsl) { const match = hsl.match(/[\d.]+/g)?.map(Number); if (!match || match.length < 3) return [0, 0, 0, 1]; const [h, s, l, a = 1] = match; return this.hslToRgba(h, s, l, a); } /** * Converts HSL or HSLA to RGBA. * @param {number} h - Hue (0–360) * @param {number} s - Saturation (0–100) * @param {number} l - Lightness (0–100) * @param {number} [a=1] - Alpha (0–1) * @returns {RgbaColor} */ static hslToRgba(h, s, l, a = 1) { s /= 100; l /= 100; /** @type {(n: number) => number} */ const k = (n) => (n + h / 30) % 12; const a_ = s * Math.min(l, 1 - l); /** @type {(n: number) => number} */ const f = (n) => l - a_ * Math.max(-1, Math.min(k(n) - 3, Math.min(9 - k(n), 1))); return [Math.round(f(0) * 255), Math.round(f(8) * 255), Math.round(f(4) * 255), a]; } /** * Converts HSL or HSLA to RGB. * @param {number} h - Hue (0–360) * @param {number} s - Saturation (0–100) * @param {number} l - Lightness (0–100) * @param {number} [a=1] - Alpha (0–1) * @returns {RgbColor} */ static hslToRgb(h, s, l, a = 1) { return TinyColorConverter.hslToRgba(h, s, l, a).slice(0, 3); } // Hex Color /** * Converts hex to integer. * @param {HexColor} hex * @returns {number} */ static hexToInt(hex) { return parseInt(hex.replace(/^#/, ''), 16); } /** * Converts hex string to HSL array. * @param {HexColor} hex * @returns {HslColor} */ static hexToHsl(hex) { const [r, g, b, a] = TinyColorConverter.hexToRgba(hex); return TinyColorConverter.rgbaToHsl(r, g, b, a); } /** * Converts hex string to HSL array. * @param {HexColor} hex * @returns {HslaColor} */ static hexToHsla(hex) { const [r, g, b, a] = TinyColorConverter.hexToRgba(hex); return TinyColorConverter.rgbaToHsla(r, g, b, a); } /** * Converts hex string to RGBA array. * @param {HexColor} hex * @returns {RgbaColor} */ static hexToRgba(hex) { hex = hex.replace(/^#/, ''); if (hex.length === 3) hex = hex .split('') .map((c) => c + c) .join(''); const intVal = parseInt(hex, 16); const r = (intVal >> 16) & 255; const g = (intVal >> 8) & 255; const b = intVal & 255; return [r, g, b, 1]; } /** * Converts HEX to RGB. * @param {HexColor} hex * @returns {RgbColor} */ static hexToRgb(hex) { return this.hexToRgba(hex).slice(0, 3); } // RGBA Color /** * Converts RGB to HEX. * @param {number} r * @param {number} g * @param {number} b * @returns {HexColor} */ static rgbToHex(r, g, b) { return '#' + [r, g, b].map((v) => v.toString(16).padStart(2, '0')).join(''); } /** * Converts RGB to integer. * @param {number} r * @param {number} g * @param {number} b * @returns {number} */ static rgbToInt(r, g, b) { return (r << 16) | (g << 8) | b; } /** * Converts RGBA to HSLA. * @param {number} r * @param {number} g * @param {number} b * @param {number} [a=1] * @returns {HslaColor} */ static rgbaToHsla(r, g, b, a = 1) { r /= 255; g /= 255; b /= 255; const max = Math.max(r, g, b); const min = Math.min(r, g, b); let h = 0, s = 0, l = (max + min) / 2; const d = max - min; if (d !== 0) { s = d / (1 - Math.abs(2 * l - 1)); switch (max) { case r: h = (g - b) / d + (g < b ? 6 : 0); break; case g: h = (b - r) / d + 2; break; case b: h = (r - g) / d + 4; break; } h *= 60; } return [Math.round(h), Math.round(s * 100), Math.round(l * 100), a]; } /** * Converts RGBA to HSL. * @param {number} r * @param {number} g * @param {number} b * @param {number} [a=1] * @returns {HslColor} */ static rgbaToHsl(r, g, b, a) { return this.rgbaToHsla(r, g, b, a).slice(0, 3); } /** * Converts rgb(a) string to RGBA array. * @param {string} rgb * @returns {RgbaColor} */ static rgbStringToRgbaArray(rgb) { const match = rgb.match(/[\d.]+/g)?.map(Number); if (!match) return []; return [...match, 1].slice(0, 4); } // Integer Color /** * Converts integer color to HSL. * @param {number} int * @returns {HslColor} */ static intToHsl(int) { const [r, g, b, a] = TinyColorConverter.intToRgba(int); return TinyColorConverter.rgbaToHsl(r, g, b, a); } /** * Converts integer color to HSL. * @param {number} int * @returns {HslaColor} */ static intToHsla(int) { const [r, g, b, a] = TinyColorConverter.intToRgba(int); return TinyColorConverter.rgbaToHsla(r, g, b, a); } /** * Converts integer color to hex. * @param {number} int * @returns {HexColor} */ static intToHex(int) { return '#' + int.toString(16).padStart(6, '0'); } /** * Converts an integer (0xRRGGBB) to RGBA. * @param {number} value * @returns {RgbaColor} */ static intToRgba(value) { const r = (value >> 16) & 255; const g = (value >> 8) & 255; const b = value & 255; return [r, g, b, 1]; } // Class Script /** @type {ColorTypes} */ #original = '#000000'; /** @type {RgbaColor} */ #rgba = [0, 0, 0, 0]; #checkIsHsl; /** * @param {ColorTypes|null} [input=null] - Any valid color (hex, rgb string, rgba string, hsl(a), css name, array or int). * @param {boolean} [checkIsHsl=false] */ constructor(input = null, checkIsHsl = false) { this.#checkIsHsl = checkIsHsl; if (typeof input !== 'undefined' && input !== null) this.setColor(input); } /** * @param {ColorTypes} input - Any valid color (hex, rgb string, rgba string, hsl(a), css name, array or int). */ setColor(input) { this.#original = input; const isHsl = this.#checkIsHsl && Array.isArray(input) && input[0] <= 360 && input[1] <= 100 && input[2] <= 100; this.#rgba = TinyColorConverter.parseInput(input, isHsl); } /** * Returns HSLA array. * @returns {HslaColor} */ toHslaArray() { const [r, g, b, a] = this.#rgba; return TinyColorConverter.rgbaToHsla(r, g, b, a); } /** * Returns RGB string. * @returns {string} */ toHslString() { const [r, g, b] = this.#rgba; const [h, s, l] = TinyColorConverter.rgbaToHsl(r, g, b); return `hsl(${h}, ${s}%, ${l}%)`; } /** * Returns RGBA string. * @returns {string} */ toHslaString() { const [r, g, b, a] = this.#rgba; const [h, s, l, a2] = TinyColorConverter.rgbaToHsla(r, g, b, a); return `hsla(${h}, ${s}%, ${l}%, ${a2 ?? 1})`; } /** * Returns RGBA array. * @returns {RgbaColor} */ toRgbaArray() { return [...this.#rgba]; } /** * Returns RGB string. * @returns {string} */ toRgbString() { const [r, g, b] = this.#rgba; return `rgb(${r}, ${g}, ${b})`; } /** * Returns RGBA string. * @returns {string} */ toRgbaString() { const [r, g, b, a] = this.#rgba; return `rgba(${r}, ${g}, ${b}, ${a ?? 1})`; } /** * Returns hex color. * @returns {HexColor} */ toHex() { const [r, g, b] = this.#rgba; return TinyColorConverter.rgbToHex(r, g, b); } /** * Returns color as integer. * @returns {number} */ toInt() { const [r, g, b] = this.#rgba; return TinyColorConverter.rgbToInt(r, g, b); } /** * Returns the original input. * @returns {ColorTypes} */ getOriginal() { return this.#original; } } module.exports = TinyColorConverter;