Skip to content

Instantly share code, notes, and snippets.

@yokoishioka
Created March 12, 2022 02:10
Show Gist options
  • Save yokoishioka/8c4d084e0811bd4e685fabc57599685e to your computer and use it in GitHub Desktop.
Save yokoishioka/8c4d084e0811bd4e685fabc57599685e to your computer and use it in GitHub Desktop.
Color Converter Service
import { Injectable } from '@angular/core';
import { ColorHsl, ColorRgb, ColorRgbCalculations } from './color';
@Injectable({
providedIn: 'root'
})
export class ColorService {
hexToRgb(hex: string): ColorRgb {
hex.trim();
hex = hex.replace('#', '');
if (hex.length === 3) {
return {
red: this._hexToDecimal(hex[0]),
green: this._hexToDecimal(hex[1]),
blue: this._hexToDecimal(hex[2])
}
}
return {
red: this._hexToDecimal(hex[0], hex[1]),
green: this._hexToDecimal(hex[2], hex[3]),
blue: this._hexToDecimal(hex[4], hex[5])
};
}
hexToHsl(hex: string): ColorHsl {
const rgb: ColorRgb = this.hexToRgb(hex);
return this.rgbToHsl(rgb.red, rgb.green, rgb.blue);
}
hslToRgb(hue: number, saturation: number, lightness: number): ColorRgb {
const sFraction: number = saturation / 100;
const lFraction: number = lightness / 100;
const chroma: number = (1 - Math.abs(2 * lFraction - 1)) * sFraction;
const second: number = chroma * (1 - Math.abs((hue / 60) % 2 - 1));
const amount: number = lFraction - (chroma / 2);
let red: number = 0;
let green: number = 0;
let blue: number = 0;
if (0 <= hue && hue < 60) {
red = chroma;
green = second;
blue = 0;
} else if (60 <= hue && hue < 120) {
red = second;
green = chroma;
blue = 0;
} else if (120 <= hue && hue < 180) {
red = 0;
green = chroma;
blue = second;
} else if (180 <= hue && hue < 240) {
red = 0;
green = second;
blue = chroma;
} else if (240 <= hue && hue < 300) {
red = second;
green = 0;
blue = chroma;
} else if (300 <= hue && hue < 360) {
red = chroma;
green = 0;
blue = second;
}
return {
red: Math.round((red + amount) * 255),
green: Math.round((green + amount) * 255),
blue: Math.round((blue + amount) * 255)
};
}
hslToHex(hue: number, saturation: number, lightness: number): string {
const rgb: ColorRgb = this.hslToRgb(hue, saturation, lightness);
return this.rgbToHex(rgb.red, rgb.green, rgb.blue);
}
hslValuesToString(hue: number, saturation: number, lightness: number, alpha?: number): string {
if (!alpha) return `hsl(${hue}, ${saturation}%, ${lightness}%)`;
return `hsl(${hue}, ${saturation}%, ${lightness}%, ${alpha})`;
}
hslStringToValues(hslString: string): ColorHsl | undefined {
const values: number[] | undefined = this.stringToValues(hslString);
if (!values) return;
return {
hue: values[0],
saturation: values[1],
lightness: values[2],
alpha: values[3] || undefined
}
}
rgbToHex(red: number, green: number, blue: number): string {
return `#${this._decimalToHex(red)}${this._decimalToHex(green)}${this._decimalToHex(blue)}`;
}
rgbToHsl(red: number, green: number, blue: number): ColorHsl {
const rgb: ColorRgbCalculations = this._buildRgbCalculations(red, green, blue);
const lightness: number = this._lightnessFromRgb(rgb);
return {
hue: this._hueFromRgb(rgb),
saturation: this._saturationFromRgb(rgb, lightness) * 100,
lightness: Math.round(lightness * 100),
};
}
rgbValuesToString(red: number, green: number, blue: number, alpha?: number): string {
if (!alpha) return `rgb(${red}, ${green}, ${blue})`;
return `rgb(${red}, ${green}, ${blue}, ${alpha})`;
}
rgbStringToValues(rgbString: string): ColorRgb | undefined {
const values: number[] | undefined = this.stringToValues(rgbString);
if (!values) return;
return {
red: values[0],
green: values[1],
blue: values[2],
alpha: values[3] || undefined
}
}
stringToValues(colorString: string): number[] | undefined {
const pattern: RegExp = /(\d+)/g;
return colorString.match(pattern)?.map(value => +value);
}
private _hueFromRgb(rgb: ColorRgbCalculations): number {
if (rgb.delta === 0) return 0;
let hue: number;
switch (rgb.max) {
case rgb.red:
hue = ((rgb.green - rgb.blue) / rgb.delta) % 6;
break;
case rgb.green:
hue = ((rgb.blue - rgb.red) / rgb.delta) + 2;
break;
default:
hue = ((rgb.red - rgb.green) / rgb.delta) + 4;
break;
}
hue = Math.round(hue * 60);
return hue >= 0 ? hue : hue + 360
}
private _lightnessFromRgb(rgb: ColorRgbCalculations): number {
return ((rgb.max + rgb.min) / 2);
}
private _saturationFromRgb(rgb: ColorRgbCalculations, lightness: number): number {
return rgb.delta === 0 ? rgb.delta : rgb.delta / (1 - Math.abs(2 * lightness - 1));
}
private _buildRgbCalculations(red: number, green: number, blue: number): ColorRgbCalculations {
const rgbFractions: number[] = [red / 255, green / 255, blue / 255];
const min: number = Math.min(...rgbFractions);
const max: number = Math.max(...rgbFractions);
const delta: number = max - min;
return {
red: rgbFractions[0],
green: rgbFractions[1],
blue: rgbFractions[2],
min: min,
max: max,
delta: delta
};
}
private _decimalToHex(decimal: number): string {
const hex: string = decimal.toString(16);
return hex.length === 1 ? `0${hex}` : hex;
}
private _hexToDecimal(hex1: string, hex2?: string): number {
return !hex2 ? +(`0x${hex1}${hex1}`) : +(`0x${hex1}${hex2}`);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment