Skip to content

Instantly share code, notes, and snippets.

@xaphod
Last active August 11, 2020 19:00
Show Gist options
  • Save xaphod/ae965011d4864abc4052ebd08c3b795c to your computer and use it in GitHub Desktop.
Save xaphod/ae965011d4864abc4052ebd08c3b795c to your computer and use it in GitHub Desktop.
Darken a color in HSL space
/* eslint-disable no-underscore-dangle */
/* eslint-disable no-multi-assign */
/*
* License: contains code from various sources, including https://github.com/gka/chroma.js/, which has Apache2 license
* Author's contributions: public domain / no attribution required.
* You're on your own to figure out if you can use this code in your project, author disclaims all liability.
*
*/
const parseColor = (color) => {
const m = color.match(/^#([0-9a-f]{6})([0-9a-f]{2}){0,1}$/i);
if (!m || m.length === 0) {
console.error(`parseColor: could not parse ${color}`);
return [];
}
const [, colorPart, alphaPart] = m;
const arr = [
parseInt(colorPart.substr(0, 2), 16),
parseInt(colorPart.substr(2, 2), 16),
parseInt(colorPart.substr(4, 2), 16),
];
if (alphaPart) {
arr.push(parseInt(alphaPart, 16));
} else {
arr.push(255);
}
return arr;
};
const componentToHex = (c) => {
const hex = c.toString(16);
return hex.length === 1 ? `0${hex}` : hex;
};
// INPUT: 3 or 4 values between 0-255
// OUTPUT:
// h = 0-255
// s = 0-1
// l = 0-1
const rgbToHsl = (rIn, gIn, bIn, a) => {
const r = rIn / 255;
const g = gIn / 255;
const b = bIn / 255;
const min = Math.min(r, g, b);
const max = Math.max(r, g, b);
const l = (max + min) / 2;
let s;
let h;
if (max === min) {
s = 0;
h = Number.NaN;
} else {
s = l < 0.5 ? (max - min) / (max + min) : (max - min) / (2 - max - min);
}
if (r === max) h = (g - b) / (max - min);
else if (g === max) h = 2 + (b - r) / (max - min);
else if (b === max) h = 4 + (r - g) / (max - min);
h *= 60;
if (h < 0) h += 360;
return [h, s, l, a];
};
// INPUT:
// h = 0-255
// s = 0-1
// l = 0-1
// a = optional
// OUTPUT: 3 or 4 values between 0-255, alpha omitted if not given
const hslToRgb = (h, s, l, a) => {
let r;
let g;
let b;
if (s === 0) {
r = g = b = l * 255;
} else {
const t3 = [0, 0, 0];
const c = [0, 0, 0];
const t2 = l < 0.5 ? l * (1 + s) : l + s - l * s;
const t1 = 2 * l - t2;
const h_ = h / 360;
t3[0] = h_ + 1 / 3;
t3[1] = h_;
t3[2] = h_ - 1 / 3;
for (let i = 0; i < 3; i += 1) {
if (t3[i] < 0) t3[i] += 1;
if (t3[i] > 1) t3[i] -= 1;
if (6 * t3[i] < 1) c[i] = t1 + (t2 - t1) * 6 * t3[i];
else if (2 * t3[i] < 1) c[i] = t2;
else if (3 * t3[i] < 2) c[i] = t1 + (t2 - t1) * ((2 / 3) - t3[i]) * 6;
else c[i] = t1;
}
[r, g, b] = [Math.round(c[0] * 255), Math.round(c[1] * 255), Math.round(c[2] * 255)];
}
return [r, g, b, a];
};
// INPUT: a color in hex RGB form like #EB64A1, and a luminanceShift in decimal form, per Sketch HSL format.
// a luminanceShift of -4 is noticeably darker
export const adjustLuminanceOfColor = (color, luminanceShift) => {
const components = parseColor(color);
if (!components || components.length === 0) {
return color;
}
const hsl = rgbToHsl(components[0], components[1], components[2]);
hsl[2] += luminanceShift / 100;
const rgb = hslToRgb(hsl[0], hsl[1], hsl[2]);
const retval = `#${componentToHex(rgb[0])}${componentToHex(rgb[1])}${componentToHex(rgb[2])}`;
return retval;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment