Last active
January 15, 2026 17:07
-
-
Save samermurad/54ca688b977632350c75417aa80485b0 to your computer and use it in GitHub Desktop.
Very basic Typescript Color class, allows init with HexString, HexNumber, RGB and RGBA numbers array
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class Color implements Iterable<any>{ | |
| private _hex: string; | |
| private _rgba: RGBA; | |
| get hex(): string { | |
| return this._hex; | |
| } | |
| get rgba(): RGBA { | |
| return this._rgba | |
| } | |
| constructor(input: ColorInput) { | |
| if (input === undefined) throw new Error('ColorInput must be a supported type: (HexString/HexNumber/RGB/RGBA/)'); | |
| if (typeof input === 'string') { | |
| this._hex = Color.normalizeHexString(input); | |
| this._rgba = Color.hexStringToComponents(this._hex) | |
| } else if (typeof input === 'number') { | |
| this._rgba = Color.hexNumberToComponents(input); | |
| this._hex = Color.componentsToHexString(this._rgba); | |
| } else if (Array.isArray(input)) { | |
| if (!(input.length === 4 || input.length === 3)) throw new Error('must use RGB | RGBA as array input'); | |
| this._hex = Color.componentsToHexString(input); | |
| this._rgba = Color.hexStringToComponents(this._hex); | |
| } else if (typeof input === 'object') { | |
| // defaults in invalid pink color | |
| const { r = 1, g = 0, b = 0.7647058823529411, a = 1 } = input; | |
| this._hex = Color.componentsToHexString([r,g,b,a]); | |
| this._rgba = Color.hexStringToComponents(this._hex) | |
| } else { | |
| this._hex = Color.normalizeHexString('#ff00c3a'); | |
| this._rgba = Color.hexStringToComponents(this._hex); | |
| } | |
| } | |
| static normalizeHexString(hex: string): HexString { | |
| const hexNoPound = hex.replace('#', ''); | |
| if (hexNoPound.length < 2 || hexNoPound.length > 8) throw new Error('Invalid hex value: ' + hex); | |
| let fixHex = ''; | |
| switch (hexNoPound.length) { | |
| case 2: | |
| fixHex = `${hexNoPound}${hexNoPound}${hexNoPound}`; | |
| break; | |
| case 3: // 0xffff => 0xff_ff_ff | |
| case 4: // 0xffff => 0xff_ff_ff_ff | |
| fixHex = `${hexNoPound.split('').map(f => `${f}${f}`).join('')}`; | |
| break; | |
| case 5: {// 0xf_f_f_f_f | |
| const [r1, r2, g1, g2, b] = hexNoPound as any; | |
| fixHex = `${r1}${r2}${g1}${g2}${b}${b}`; | |
| break; | |
| } | |
| case 6: | |
| case 8: | |
| fixHex = hexNoPound // string is correct; | |
| /* no op*/ | |
| break; | |
| case 7: {// 0xf_f_f_f_f_f_f | |
| const [r1, r2, g1, g2, b1, b2, a] = hexNoPound as any; | |
| fixHex = `${r1}${r2}${g1}${g2}${b1}${b2}${a}${a}`; | |
| break; | |
| } | |
| } | |
| if (isNaN(parseInt(fixHex, 16))) throw new Error(fixHex + ' is not a hex number.'); | |
| return `#${fixHex}` as HexString; | |
| } | |
| static hexStringToComponents(hexStr: string) : RGBA { | |
| const normalizedHex = Color.normalizeHexString(hexStr).replace('#', ''); | |
| const hex = parseInt(normalizedHex, 16); | |
| if (isNaN(hex)) throw new Error(hex + ' is not a hex number.'); | |
| let rgba = [1, 1, 1, 1]; // r, g, b, a | |
| const size = (normalizedHex.length / 2); | |
| for (let i = size - 1; i >= 0; i--) | |
| rgba[(size - 1 - i)] = ((hex >> (i * 8)) & 0xff) / 255.0; // 24 / 16 / 8 / 0; | |
| const [r, g, b, a] = rgba; | |
| return [r, g, b, a]; | |
| } | |
| static hexNumberToComponents(hex: number): RGBA { | |
| return Color.hexStringToComponents(hex.toString(16)); | |
| } | |
| static componentsToHexString(components: RGB | RGBA): HexString { | |
| let str = ''; | |
| for (let i = 0; i < components.length; i++) { | |
| let component = components[i]; | |
| // normalize component value | |
| if (component <= 0) component = 0; | |
| else if (component <= 1) component = component * 255.0 | |
| else if (component > 1) component = Math.min(component, 255); | |
| component = Math.round(component); | |
| // add it to string as hex | |
| str += component.toString(16).padStart(2, '0'); | |
| } | |
| return Color.normalizeHexString(str); | |
| } | |
| valueOf() { | |
| return this._hex; | |
| } | |
| toString() { | |
| return this._hex; | |
| } | |
| [Symbol.iterator](): Iterator<number> { | |
| let currentIndex = 0; | |
| const rgba = this.rgba; | |
| const size = rgba.length; | |
| return { | |
| next(): IteratorResult<number> { | |
| if (currentIndex < size) { | |
| const nextData = currentIndex++ | |
| return { | |
| value: rgba[nextData], | |
| done: false, | |
| } | |
| } else { | |
| return { done: true, value: undefined }; | |
| } | |
| } | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| try { | |
| console.log(new Color('#f')); | |
| } catch (error) { | |
| console.log(error.message); | |
| } | |
| try { | |
| console.log(new Color('#rr')); | |
| } catch (error) { | |
| console.log(error.message); | |
| } | |
| console.log(new Color('#ff')); | |
| console.log(new Color('#ffa')); | |
| console.log(new Color('#f01a')); | |
| console.log(new Color('#fd01b')); | |
| console.log(new Color('#68ff46')); | |
| console.log(new Color('#405c91a')); | |
| console.log(new Color('#405c91aa')); | |
| try { | |
| console.log(new Color('#405c91aae')); | |
| } catch (error) { | |
| console.log(error.message); | |
| } | |
| console.log(new Color('#ff00c3')); | |
| console.log(new Color('#ff00c3a')); | |
| console.log(new Color('#ff00c3a')); | |
| console.log(new Color('#ff00c3a')); | |
| console.log(new Color([1, 0, 0.7647058823529411, 0.6666666666666666])); | |
| console.log(new Color([1, 0, 0.7647058823529411])); | |
| console.log(new Color([255, 0, 255 * 0.7647058823529411, 255 * 0.6666666666666666])); | |
| console.log(new Color(0xff00c3a)); | |
| console.log(new Color(0xffaacca)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment