Skip to content

Instantly share code, notes, and snippets.

@kalley
Last active December 20, 2015 10:39
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 kalley/6117357 to your computer and use it in GitHub Desktop.
Save kalley/6117357 to your computer and use it in GitHub Desktop.
Small class to create colors, output different formats (hex, rgb(a), hsl(a)), or interpolate between colors, for animation or just to mix them.
/*
* How to use
*
* var red = new Color('#f00');
* var green = new Color(0, 255, 0);
* var blue = new Color('rgb(0,0,255)');
* var yellow = new Color('hsla(60,100%,50%,.5)');
* var purple = new Color();
* purple.extend(Color.parse('800080'));
*
* If you want to interpolate between colors:
* var fn = yellow.interpolate(red);
* var halfYellowRed = fn(.5);
*
*/
var Color = (function(parseFloat, round) {
var hexShorthand = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
var hexLongform = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i;
var renderPattern = /\{\{(.*?)\}\}/g;
var isHex = /^#?([0-9a-f]{3}|[0-9a-f]{6})$/i;
var isHSL = /^hsla?\((\d{1,3}?),\s*(\d{1,3}%),\s*(\d{1,3}%)(?:,\s*([01]?\.?\d*))?\)$/;
var isRGB = /^rgba?\((\d{1,3}%?),\s*(\d{1,3}%?),\s*(\d{1,3}%?)(?:,\s*([01]?\.?\d*))?\)$/;
// Utility functions
var expandHex = function(hex) {
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
hex = hex.replace(hexShorthand, function(m, r, g, b) {
return '#' + r + r + g + g + b + b;
});
return hex;
};
var transition = function(t, from, to) {
t = t > 1 ? 1 : t;
var r = round(from.r + ( to.r - from.r ) * t);
var g = round(from.g + ( to.g - from.g ) * t);
var b = round(from.b + ( to.b - from.b ) * t);
var a = from.a + ( to.a - from.a ) * t;
return { r: r, g: g, b: b, a: a };
};
/* only looks for {{var}} stuff */
var render = function(tmpl, data) {
var result;
var test = tmpl;
while ( result = renderPattern.exec(test) ) {
tmpl = tmpl.replace(result[0], result[1] in data ? data[result[1]] : '');
}
return tmpl;
};
var hue2rgb = function(a, b, c) {
if(c < 0) c += 1;
if(c > 1) c -= 1;
if(c < 1/6) return a + ( b - a ) * 6 * c;
if(c < 1/2) return b;
if(c < 2/3) return a + ( b - a ) * ( 2 / 3 - c ) * 6;
return a;
};
var Color = function(r,g,b,a) {
this.r = r || 0;
this.g = g || 0;
this.b = b || 0;
this.a = a && a > 1 ? ( a / 255 ) : 1;
if ( g === undefined ) {
this.extend(typeof r === 'string' ? Color.parse(r) : r);
}
this.extend(Color.rgb2hsl(this.r, this.g, this.b));
};
// Prototype methods
var proto = Color.prototype;
proto.toHex = function() {
return "#" + ((1 << 24) + (this.r << 16) + (this.g << 8) + this.b).toString(16).slice(1);
};
proto.toRGB = function() {
return render(this.a !== 1 ? 'rgba({{r}},{{g}},{{b}},{{a}})' : 'rgb({{r}},{{g}},{{b}})', this);
};
proto.toHSL = function() {
return render(this.a !== 1 ? 'hsla({{h}},{{s}}%,{{l}}%,{{a}})' : 'hsl({{h}},{{s}}%,{{l}}%)', this);
};
proto.interpolate = function(toColor) {
var fromColor = this;
return function(t) {
return new Color(transition(t, fromColor, toColor));
}
};
proto.extend = function(obj) {
for ( var p in obj ) {
this[p] = obj[p];
}
};
proto.toString = proto.toRGB;
// Class methods (no access to "this")
Color.parse = function(string) {
var rgb;
var result = [];
if ( isHex.test(string) ) {
rgb = Color.hex2rgb(string);
} else if ( ( result = isRGB.exec(string) ) ) {
rgb = {
r: parseFloat(result[1]),
g: parseFloat(result[2]),
b: parseFloat(result[3])
};
} else if ( ( result = isHSL.exec(string) ) ) {
rgb = Color.hsl2rgb(result[1], result[2], result[3]);
}
if ( result[4] ) {
rgb.a = result[4];
}
return rgb;
};
Color.hex2rgb = function(hex) {
hex = expandHex(hex);
var result = hexLongform.exec(hex);
return {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
};
};
Color.rgb2hsl = function(r,g,b) {
r /= 255;
g /= 255;
b /= 255;
var max = Math.max(r, g, b);
var min = Math.min(r, g, b);
var l = (max + min) / 2;
var h;
var s;
if(max == min){
h = s = 0; // achromatic
} else {
var d = max - min;
s = l > 0.5 ? d / ( 2 - max - min ) : d / ( max + min );
/* using if else much faster in Chrome than using switch
http://jsperf.com/hsl-switch-vs-if-else * /
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;
}
/* */
if ( max === r ) {
h = ( g - b ) / d + ( g < b ? 6 : 0 );
} else if ( max === g ) {
h = ( b - r ) / d + 2;
} else if ( max === b ) {
h = ( r - g ) / d + 4;
}
h /= 6;
}
return { h: h * 360, s: s * 100, l: l * 100 };
};
Color.hsl2rgb = function(h, s, l) {
h /= 360;
s = parseFloat(s) / 100;
l = parseFloat(l) / 100;
var q = l < 0.5 ? l * ( 1 + s ) : ( l + s - l * s );
var p = 2 * l - q;
return {
r: hue2rgb(p, q, h + 1 / 3 ) * 255,
g: hue2rgb(p, q, h) * 255,
b: hue2rgb(p, q, h - 1 / 3 ) * 255
};
};
return Color;
})(parseFloat, Math.round);
var Color=function(h,k){var n=/^#?([a-f\d])([a-f\d])([a-f\d])$/i,p=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i,q=/\{\{(.*?)\}\}/g,r=/^#?([0-9a-f]{3}|[0-9a-f]{6})$/i,s=/^hsla?\((\d{1,3}?),\s*(\d{1,3}%),\s*(\d{1,3}%)(?:,\s*([01]?\.?\d*))?\)$/,t=/^rgba?\((\d{1,3}%?),\s*(\d{1,3}%?),\s*(\d{1,3}%?)(?:,\s*([01]?\.?\d*))?\)$/,u=function(b){return b=b.replace(n,function(b,a,d,g){return"#"+a+a+d+d+g+g})},m=function(b,c){for(var a,d=b;a=q.exec(d);)b=b.replace(a[0],a[1]in c?c[a[1]]:"");return b},l=function(b,
c,a){0>a&&(a+=1);1<a&&(a-=1);return a<1/6?b+6*(c-b)*a:0.5>a?c:a<2/3?b+6*(c-b)*(2/3-a):b},f=function(b,c,a,d){this.r=b||0;this.g=c||0;this.b=a||0;this.a=d&&1<d?d/255:1;void 0===c&&this.extend("string"===typeof b?f.parse(b):b);this.extend(f.rgb2hsl(this.r,this.g,this.b))},e=f.prototype;e.toHex=function(){return"#"+(16777216+(this.r<<16)+(this.g<<8)+this.b).toString(16).slice(1)};e.toRGB=function(){return m(1!==this.a?"rgba({{r}},{{g}},{{b}},{{a}})":"rgb({{r}},{{g}},{{b}})",this)};e.toHSL=function(){return m(1!==
this.a?"hsla({{h}},{{s}}%,{{l}}%,{{a}})":"hsl({{h}},{{s}}%,{{l}}%)",this)};e.interpolate=function(b){var c=this;return function(a){var d=f;a=1<a?1:a;var g=k(c.r+(b.r-c.r)*a),e=k(c.g+(b.g-c.g)*a),v=k(c.b+(b.b-c.b)*a);return new d({r:g,g:e,b:v,a:c.a+(b.a-c.a)*a})}};e.extend=function(b){for(var c in b)this[c]=b[c]};e.toString=e.toRGB;f.parse=function(b){var c,a=[];if(r.test(b))c=f.hex2rgb(b);else if(a=t.exec(b))c={r:h(a[1]),g:h(a[2]),b:h(a[3])};else if(a=s.exec(b))c=f.hsl2rgb(a[1],a[2],a[3]);a[4]&&(c.a=
a[4]);return c};f.hex2rgb=function(b){b=u(b);b=p.exec(b);return{r:parseInt(b[1],16),g:parseInt(b[2],16),b:parseInt(b[3],16)}};f.rgb2hsl=function(b,c,a){b/=255;c/=255;a/=255;var d=Math.max(b,c,a),g=Math.min(b,c,a),f=(d+g)/2,e;if(d==g)e=g=0;else{var h=d-g,g=0.5<f?h/(2-d-g):h/(d+g);d===b?e=(c-a)/h+(c<a?6:0):d===c?e=(a-b)/h+2:d===a&&(e=(b-c)/h+4);e/=6}return{h:360*e,s:100*g,l:100*f}};f.hsl2rgb=function(b,c,a){b/=360;c=h(c)/100;a=h(a)/100;c=0.5>a?a*(1+c):a+c-a*c;a=2*a-c;return{r:255*l(a,c,b+1/3),g:255*
l(a,c,b),b:255*l(a,c,b-1/3)}};return f}(parseFloat,Math.round);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment