UNPKG

ddnet

Version:

A typescript npm package for interacting with data from ddnet.org

147 lines 4.96 kB
import { DDNetError } from '../../util.js'; /** * Helper class to convert or pass around colors. * * @typeParam T used internally. */ export class Color { /** * Stored color in HSLA format. */ color = { h: 0, s: 0, l: 0, a: 0 }; /** * Converts an RGBA color to an HSLA color. */ static RGBA_to_HSLA( /** * The RGBA color to convert. */ rgba) { let { r, g, b, a } = rgba; r /= 255; g /= 255; b /= 255; const max = Math.max(r, g, b); const min = Math.min(r, g, b); const delta = max - min; let h = 0; if (delta !== 0) { if (max === r) h = 60 * (((g - b) / delta) % 6); else if (max === g) h = 60 * ((b - r) / delta + 2); else h = 60 * ((r - g) / delta + 4); } if (h < 0) h += 360; let l = (max + min) / 2; let s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1)); s *= 100; l *= 100; return { h, s, l, a }; } static from(input) { //@ts-expect-error return new Color(input); } constructor(input) { switch (typeof input) { case 'string': { let hex = input.slice(1); if (hex.length === 3 || hex.length === 4) { hex = hex .split('') .map(c => c + c) .join(''); } if (hex.length === 6) hex += 'ff'; // assume full opacity if no alpha const r = parseInt(hex.slice(0, 2), 16); const g = parseInt(hex.slice(2, 4), 16); const b = parseInt(hex.slice(4, 6), 16); const a = parseInt(hex.slice(6, 8), 16); this.color = Color.RGBA_to_HSLA({ r, g, b, a }); return; } case 'number': { const byte0 = input & 0xff; // L const byte1 = (input >> 8) & 0xff; // S const byte2 = (input >> 16) & 0xff; // H const byte3 = (input >> 24) & 0xff; // possible alpha const a = input > 0xffffff || byte3 !== 0 ? byte3 : 255; let h = byte2, s = byte1, l = byte0; if (h === 255) h = 0; this.color = { h: (h * 360) / 255, s: (s * 100) / 255, l: (l * 100) / 255, a: a / 255 }; return; } default: break; } if ('r' in input) { this.color = Color.RGBA_to_HSLA(input); return; } if ('h' in input) { this.color = input; return; } } to(format) { switch (format) { case 'hex': { const { r, g, b, a } = this.to('rgba'); const toHex = (v) => v.toString(16).padStart(2, '0'); return `#${toHex(r)}${toHex(g)}${toHex(b)}${toHex(a ?? 255)}`; } case 'tw': { const { h, s, l, a } = this.color; const byteH = Math.round((h / 360) * 255); const byteS = Math.round((s / 100) * 255); const byteL = Math.round((l / 100) * 255); const byteA = a !== undefined && a < 1 ? Math.round(a * 255) : 0; return (byteA << 24) | (byteH << 16) | (byteS << 8) | byteL; } case 'rgba': { const { h, s, l, a } = this.color; const c = (n) => Math.round(n); const lRatio = l / 100; const sRatio = s / 100; const cVal = (1 - Math.abs(2 * lRatio - 1)) * sRatio; const x = cVal * (1 - Math.abs(((h / 60) % 2) - 1)); const m = lRatio - cVal / 2; let r1 = 0, g1 = 0, b1 = 0; if (h < 60) [r1, g1, b1] = [cVal, x, 0]; else if (h < 120) [r1, g1, b1] = [x, cVal, 0]; else if (h < 180) [r1, g1, b1] = [0, cVal, x]; else if (h < 240) [r1, g1, b1] = [0, x, cVal]; else if (h < 300) [r1, g1, b1] = [x, 0, cVal]; else [r1, g1, b1] = [cVal, 0, x]; return { r: c((r1 + m) * 255), g: c((g1 + m) * 255), b: c((b1 + m) * 255), a }; } case 'hsla': { return this.color; } default: throw new DDNetError('Invalid format', format); } } } //# sourceMappingURL=Color.js.map