Last active
December 20, 2015 10:39
-
-
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.
This file contains 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
/* | |
* 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); |
This file contains 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
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