color-engine
Version:
Creates color gradients from a few colors and converts RBG colors to HEX and vice versa.
268 lines (237 loc) • 5.91 kB
text/typescript
import * as utils from "./utils"
export type ColorResolvable = Rgb | Hex | Color | string
export type Rgb = [number, number, number]
/**
* resolvable hex color
*/
export type Hex = string | number | HexCode
/**
* hex color code (without prefix)
*/
export type HexCode = string
/**
* resolvable color list
*/
export type GradientResolvable = ColorResolvable[]
/**
* color list
*/
export type Gradient = Color[]
export const hexRegex = /^(?:#|0x)?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i
export const rgbRegex = /^\s*([1-9][0-9.]*)\s*,\s*([1-9][0-9.]*)\s*,\s*([1-9][0-9.]*)\s*$/
export class Color {
/**
* edit this flag to change default type of Hex values
* (string or number)
* default: number
*/
static hexMode: "number" | "string" = "string"
private r = 0
private g = 0
private b = 0
private a = 255
/**
* Not a color ?
*/
readonly isNaC: boolean
get red(): number {
return this.r
}
get green(): number {
return this.g
}
get blue(): number {
return this.b
}
get alpha(): number {
return this.a
}
set red(r) {
this.r = r
}
set green(g) {
this.g = g
}
set blue(b) {
this.b = b
}
set alpha(a) {
this.a = a
}
constructor(resolvable: ColorResolvable) {
const rgb = Color.resolve(resolvable)
this.isNaC = !rgb
if (rgb) {
this.r = rgb[0]
this.g = rgb[1]
this.b = rgb[2]
}
}
/**
* get the RGB color as Array
*/
get rgb(): Rgb {
return [this.r, this.g, this.b]
}
/**
* get the hex color as {Color.hexMode}
*/
get hex(): string | number {
return Color.rgbToHex(this.rgb)
}
/**
* get the hex color as String
*/
get hexString(): string {
return Color.rgbToHexString(this.rgb)
}
/**
* get the hex color as Number
*/
get hexNumber(): number {
return Color.rgbToHexNumber(this.rgb)
}
/**
* get hex code (without prefix) as String
*/
get hexCode(): string {
return Color.rgbToHexCode(this.rgb)
}
/**
* @param resolvable - the fusion color
* @param proportion - the ratio of fusion with the second color (0...1)
*/
fusion(resolvable: ColorResolvable, proportion: number): Color {
const color = new Color(resolvable)
return this.map((c, i) => {
return utils.map(proportion, 0, 1, c, color.rgb[i])
})
}
/**
* return a new Color with mapped values [r, g, b]
*/
map(callback: (value: number, index: number) => number): Color {
return new Color(this.rgb.map(callback) as Rgb)
}
toString(type: "rgb" | "hex" = "hex"): string {
switch (type) {
case "hex":
return Color.rgbToHexString(this.rgb)
case "rgb":
return this.rgb.toString()
}
}
/**
* alias for hexNumber
*/
toNumber(): number {
return Color.rgbToHexNumber(this.rgb)
}
toJSON(): Rgb {
return this.rgb
}
/**
* return false if the given resolvable is not a color
* @param resolvable - color to resolve
*/
static resolve(resolvable: ColorResolvable): Rgb | false {
if (!resolvable) return false
if (typeof resolvable === "number" || typeof resolvable === "string") {
if (this.isRgb(resolvable)) {
return Color.stringToRgb(resolvable as string) as Rgb
} else if (this.isHex(resolvable)) {
return Color.hexToRgb(resolvable) as Rgb
}
} else if (Array.isArray(resolvable)) {
if (
resolvable.length !== 3 ||
resolvable.some((c) => {
return isNaN(c) || c < 0 || c > 255
})
)
return false
return resolvable as Rgb
} else if (!resolvable.isNaC) {
return resolvable.rgb
}
return false
}
static rgbToHexCode(rgb: Rgb): HexCode {
return ((1 << 24) + (rgb[0] << 16) + (rgb[1] << 8) + rgb[2])
.toString(16)
.slice(1)
}
static hexCodeToHex(hexCode: HexCode): Hex {
switch (this.hexMode) {
case "string":
return "#" + hexCode
case "number":
return Number("0x" + hexCode)
}
}
static rgbToHex(rgb: Rgb): Hex {
return this.hexCodeToHex(this.rgbToHexCode(rgb))
}
static rgbToHexString(rgb: Rgb): string {
return "#" + this.rgbToHexCode(rgb)
}
static rgbToHexNumber(rgb: Rgb): number {
return Number("0x" + this.rgbToHexCode(rgb))
}
static hexToRgb(hex: Hex): Rgb | null {
const result = hexRegex.exec(String(hex))
return result
? [
parseInt(result[1], 16),
parseInt(result[2], 16),
parseInt(result[3], 16),
]
: null
}
static stringToRgb(resolvable: string): Rgb | null {
const result = rgbRegex.exec(resolvable)
return result
? [Number(result[1]), Number(result[2]), Number(result[3])]
: null
}
static isHex(resolvable: Hex): boolean {
return hexRegex.test(String(resolvable))
}
static isRgb(resolvable: any): boolean {
return rgbRegex.test(String(resolvable))
}
/**
* return a gradiant from another mapped gradient
* @param resolvable - input gradient
* @param length - output gradient length
*/
static gradient(resolvable: GradientResolvable, length: number): Gradient {
const colors: Gradient = resolvable.map((r) => new Color(r))
let array = []
let section = length / (colors.length - 1)
for (let i = 0; i < length; i++) {
if (colors.length > 1) {
let c = Math.floor(i / section)
array.push(
colors[c].fusion(
colors[c + 1],
utils.map(i - c * section, 0, section, 0, 1)
)
)
} else if (colors.length > 0) {
array.push(colors[0])
}
}
return array
}
/**
* return new random Color
*/
static random(): Color {
return new Color([
Math.floor(Math.random() * 255),
Math.floor(Math.random() * 255),
Math.floor(Math.random() * 255),
])
}
}