A literal clone of the mixcolor
function provided in libsass in TypeScript.
Calling the mix
function will do the mixing as well as color hex value sanity checking among other things.
Created
January 13, 2021 14:00
-
-
Save ktnyt/2573047b5b4c7c775f2be22326ebf6a8 to your computer and use it in GitHub Desktop.
Sass color mixing for TypeScript.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const clamp = (n: number, min: number, max: number) => | |
n <= min ? min : n >= max ? max : n | |
const toHex = (v: number) => { | |
const h = v.toString(16) | |
switch (h.length) { | |
case 1: | |
return `0${h}` | |
case 2: | |
return h | |
default: | |
throw new Error(`expected value from 0~255, got: ${v}`) | |
} | |
} | |
export class Color extends String { | |
constructor(s: string) { | |
if (/^#[0-9A-Fa-f]{8}$/.test(s) || /^#[0-9A-Fa-f]{6}$/.test(s)) { | |
super(s) | |
return | |
} | |
if (/^#[0-9A-Fa-f]{4}$/.test(s) || /^#[0-9A-Fa-f]{3}$/.test(s)) { | |
super( | |
`#${s | |
.substr(1) | |
.split('') | |
.map((c) => `${c}${c}`) | |
.join('')}` | |
) | |
return | |
} | |
throw new Error(`expected color hex string, got '${s}'`) | |
} | |
get red() { | |
return parseInt(this.substr(1, 2), 16) | |
} | |
get green() { | |
return parseInt(this.substr(3, 2), 16) | |
} | |
get blue() { | |
return parseInt(this.substr(5, 2), 16) | |
} | |
get alpha() { | |
return this.length === 9 ? parseInt(this.substr(7, 2), 16) : 1.0 | |
} | |
get hasAlpha() { | |
return this.length === 9 | |
} | |
public static fromRGB(r: number, g: number, b: number) { | |
return new Color(`#${toHex(r)}${toHex(g)}${toHex(b)}`) | |
} | |
public static fromRGBA(r: number, g: number, b: number, a: number) { | |
return new Color(`${Color.fromRGB(r, g, b)}${toHex(Math.floor(a * 255))}`) | |
} | |
mix(color: Color, weight = 50) { | |
const p = clamp(weight, 0.0, 100.0) / 100.0 | |
const w = 2.0 * p - 1.0 | |
const a = this.alpha - color.alpha | |
const w1 = ((w * a === -1.0 ? w : (w + a) / (1.0 + w * a)) + 1.0) / 2.0 | |
const w2 = 1.0 - w1 | |
const red = Math.round(this.red * w1 + color.red * w2) | |
const green = Math.round(this.green * w1 + color.green * w2) | |
const blue = Math.round(this.blue * w1 + color.blue * w2) | |
const alpha = this.alpha * p + color.alpha * (1.0 - p) | |
return this.hasAlpha || color.hasAlpha || alpha !== 1.0 | |
? Color.fromRGBA(red, green, blue, alpha) | |
: Color.fromRGB(red, green, blue) | |
} | |
} | |
export const mix = (color1: string, color2: string, weight = 50) => | |
String(new Color(color1).mix(new Color(color2), weight)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thank you so much for this! Very helpful.