Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vipertechofficial/c5353b4df4910aebffed4f0a07fd725e to your computer and use it in GitHub Desktop.
Save vipertechofficial/c5353b4df4910aebffed4f0a07fd725e to your computer and use it in GitHub Desktop.
Fastest RGBA, HSLA, and HEX color conversion, formatting and blending (mix) in JavaScript developed for the pixa.pics project.
_format_color = (color) => { // Supports #fff (short rgb), #fff0 (short rgba), #e2e2e2 (full rgb) and #e2e2e2ff (full rgba)
const hex = color || "#00000000";
const hex_length = hex.length;
if(hex_length === 9) {
return hex;
} else if (hex_length === 7) {
return hex.concat("ff");
} else if (hex_length === 5) {
const a = hex.charAt(1), b = hex.charAt(2), c = hex.charAt(3), d = hex.charAt(4);
return "#".concat(a, a, b, b, c, c, d, d);
} else if (hex_length === 4) {
const a = hex.charAt(1), b = hex.charAt(2), c = hex.charAt(3);
return "#".concat(a, a, b, b, c, c, "ff");
}
};
_get_rgba_from_hex = (hex) => {
return new Uint8ClampedArray(Uint32Array.of(parseInt(hex.slice(1), 16)).buffer).reverse();
};
_get_hex_color_from_rgba_values = (r, g, b, a) => {
return "#".concat("00000000".concat(new Uint32Array(Uint8ClampedArray.of(a, b, g, r).buffer)[0].toString(16)).slice(-8));
};
_rgb_to_hsl = (r, g, b) => {
r /= 255, g /= 255, b /= 255;
const max = Math.max(r, g, b), min = Math.min(r, g, b);
let h, s, l = (max + min) / 2;
if(max === min){
h = s = 0; // achromatic
}else {
const d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
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 /= 6;
}
return Array.of(parseInt(h * 360), parseInt(s * 100), parseInt(l * 100));
}
_hsl_to_rgb = (h, s, l) => {
h /= 360, s /= 100, l /= 100;
let r, g, b;
if (s === 0) {
r = g = b = l;
} else {
const hue_to_rgb = function(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
};
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = hue_to_rgb(p, q, h + 1 / 3);
g = hue_to_rgb(p, q, h);
b = hue_to_rgb(p, q, h - 1 / 3);
}
return Uint8ClampedArray.of(r * 255, g * 255, b * 255);
};
_blend_colors = (color_a, color_b, amount = 1, should_return_transparent = false, alpha_addition = false) => {
color_a = this._format_color(color_a);
// If we blend the first color with the second with 0 "force", return transparent
if(amount === 0 && color_b !== "hover" && should_return_transparent) {
return "#00000000";
}
// Make sure we have a color based on the 4*2 hex char format
if(color_b === "hover") {
const rgba = this._get_rgba_from_hex(color_a);
const hsl = this._rgb_to_hsl(rgba[0], rgba[1], rgba[2], rgba[3]);
const rgb = this._hsl_to_rgb(hsl[0], hsl[1], parseInt(hsl[2] >= 50 ? hsl[2]/2: hsl[2]*2));
color_b = this._get_hex_color_from_rgba_values(rgb[0], rgb[1], rgb[2], 255);
}else {
color_b = this._format_color(color_b);
}
// If the second color is transparent, return transparent
if(should_return_transparent && color_b === "#00000000" && amount === 1) { return "#00000000"; }
// Extract RGBA from both colors
const base = this._get_rgba_from_hex(color_a);
const added = this._get_rgba_from_hex(color_b);
if(added[3] === 255 && amount === 1) { return color_b; }
const ba3 = base[3] / 255;
const ad3 = (added[3] / 255) * amount;
let mix = new Uint8ClampedArray(4);
let mi3 = 0;
if (ba3 > 0 && ad3 > 0) {
if(alpha_addition) {
mi3 = ad3 + ba3;
}else {
mi3 = 1 - ((1 - ad3) * (1 - ba3));
}
const ao = ad3 / mi3;
const bo = ba3 * (1 - ad3) / mi3;
mix[0] = parseInt(added[0] * ao + base[0] * bo); // red
mix[1] = parseInt(added[1] * ao + base[1] * bo); // green
mix[2] = parseInt(added[2] * ao + base[2] * bo); // blue
}else if(ad3 > 0) {
mi3 = added[3] / 255;
mix[0] = added[0];
mix[1] = added[1];
mix[2] = added[2];
}else {
mi3 = base[3] / 255;
mix[0] = base[0];
mix[1] = base[1];
mix[2] = base[2];
}
if(alpha_addition) {
mi3 /= 2;
}
mix[3] = parseInt(mi3 * 255);
return this._get_hex_color_from_rgba_values(mix[0], mix[1], mix[2], mix[3]);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment