FANDOM


Notice: the code presented here is a bit outdated. This library was absorbed into the Javable Widget Project and the latest sources can be found there, released under the project license.

This is a Color library I whipped up for a project where it would be overkill. I got carried away. The base conversion functions are the standard equations to convert between color-spaces. However, credit goes to Harry (g6auc) for actually putting them into readable/understandable JavaScript. :) I just formatted them as objects with tons of really neat and cool properties.

Whats in here:

  • Color (RGB)
  • CMYK
  • HSV

Each Color-space object has the following:

  • Ability to convert to the other color-spaces.
  • Get a String formatted for canvas, ex: "rgba(244, 238, 224, 1)"
  • Getting its complementary color.
  • Adding and subtracting colors.
  • JSDoc formatting (mostly perfect? I may have forgotten a few here obscure things...)

It is worth mentioning Color (RGB)'s extra properties/abilities...

  • Support for alpha/transparency. (This info is lost when converted to another color-space.)
  • Ability to convert Hexadecimal colors to RGB. (Send in one String with a "#" in it, instead of 3 integers.)

To create an object, just instantiate it:

var red  = new Color(255, 0, 0);

Code Edit

Here's the lib... enjoy!

/**
 * A Color object that does not handle transparency. (does not need to).
 * Credit goes to Harry (g6auc) for the HEX -> RGB and vice versa. 
 * Operations can be done on two colors such as adding, subtracting, or comparing colors.
 * @param {int|String} hexR This is either a hexadecimal color string, or a integer representing the red value.
 * @param {int} g This is an integer representing the green value. Undefined if hex String is used as the only parameter.  
 * @param {int} b This is an integer representing the blue value. Undefined if a hex string is used instead.
 */
function Color(hexR, g, b, a) {
	var color = this;

	this.r = 0;
	this.g = 0;
	this.b = 0;
	this.a = 255;

	if(hexR !== undefined && hexR.toString().indexOf("#") === 0) {
		hexR = hexR.replace("#", "");

		if(hexR.length >= 3) {
			if(hexR.length === 3) {
				var sixHex = "";

				sixHex += hexR.charAt(0) + "" + hexR.charAt(0);
				sixHex += hexR.charAt(1) + "" + hexR.charAt(1);
				sixHex += hexR.charAt(2) + "" + hexR.charAt(2);

				hexR = sixHex;				
			}

			this.r = parseInt(hexR.substring(0, 2), 16); // split the HEX color into (r g b)
			this.g = parseInt(hexR.substring(2, 4), 16);
			this.b = parseInt(hexR.substring(4, 6), 16);
		}
	} else if(!isNaN(Number(hexR)) && !isNaN(Number(g)) && !isNaN(Number(b))) {
		if(!isNaN(hexR) && !isNaN(g) && !isNaN(b)) {
			this.r = Math.min(255, Math.max(0, Number(hexR)));
			this.g = Math.min(255, Math.max(0, Number(g)));
			this.b = Math.min(255, Math.max(0, Number(b)));
		}

		if(!isNaN(Number(a))) {
			this.a = Math.min(255, Math.max(0, Number(a))); 
		}
	}

	/**
	 * This formats the Color object so it can be directly used with a canvas context. 
	 * @returns A String formatted for Canvas. 
	 */
	this.getCanvasColor = function() {
		return("rgba( " + (color.r) + ", " + (color.g) + ", " + (color.b) + ", " + (color.a/255) + " )");
	};

	/**
	 * @see CMYK
	 * @returns A CMYK object representing this color. 
	 */
	this.getCMYK = function() {
		var c = 1-(color.r/255);			// first convert to (c m y) values
		var m = 1-(color.g/255);
		var y = 1-(color.b/255);
		var k = Math.min(c, m, y);			// then convert (c m y) to (c m y k)

		if (k === 1) {
			c = 0;
			m = 0;
			y = 0;
		} else {
			c = Math.round(100*(c - k)/(1 - k));
			m = Math.round(100*(m - k)/(1 - k));
			y = Math.round(100*(y - k)/(1 - k));
		}

		k = Math.round(100*k);

		return (new CMYK(c, m, y, k));
	};

	/**
	 * @returns A new Color object representing the complementary of the current color. 
	 */
	this.getComplementary = function() {
		var r1 = 255 - color.r;
		var g1 = 255 - color.g;
		var b1 = 255 - color.b;

		return (new Color(r1, g1, b1));
	};

	/**
	 * @returns A hex String representing this color. 
	 */
	this.getHex = function() {
		var hexA = "0123456789ABCDEF";

		function hex2(n) {
			return (hexA[(n >>> 4) % 16] + hexA[n % 16]);
		}

		return ("#" + hex2(this.r) + hex2(this.g) + hex2(this.b));	
	};

	/**
	 * @see HSV
	 * @returns A HSV object representing this Color. 
	 */
	this.getHSV = function() {

		var r = color.r/255;
		var g = color.g/255;
		var b = color.b/255;

		var max = Math.max(r, g, b);
		var min = Math.min(r, g, b);
		var v = max;
		var delta = max - min;
		var s;
		var h;

		if (max === 0) {
			 s = 0;
			 h = undefined;
		} else {
			s = delta/max;
			if (r === max) {
				h = (g - b)/delta; 			// between yellow & magenta
			} else if (g === max) {
				h = 2 + (b - r)/delta;		// between cyan & yellow
			} else {
				h = 4 + (r - g)/delta;		// between magenta & cyan
			}

			h *= 60;						// degrees
			if( h < 0 ) { h += 360; }
		}
		if (isNaN(h)) { h = 0; }
		h = Math.round(h);				// This is the only value we care about;
		s = Math.round(100*s);
		v = Math.round(100*v);

		return (new HSV(h, s, v));
	};

	/**
	 * Adds the current color object to the parameter object and returns the result as a new Color.
	 * If it's not a Color object, we return a clone of the object we are trying to add to.
	 * @param {Object} other This the second Color object.
	 * @returns A Color object which is the addition of two Colors.
	 */
	this.add = function(other) {
		if(other.toString().indexOf("Color") >= 0) {
			var r = Math.max(255, color.r + other.r);
			var g = Math.max(255, color.g + other.g);
			var b = Math.max(255, color.b + other.b);

			return (new Color(r, g, b));
		} else {
			return (color.clone());
		}
	};

	/**
	 * We try to the given Color object from the current Color object.
	 * If it's not a Color object, we return a clone of the object we are trying to subtract from.
	 * @param {Object} other This the second Color object.
	 * @returns A Color object which is the subtraction of two Colors.
	 */
	this.subtract = function(other) {
		if(other.toString().indexOf("Color") >= 0) {
			var r = Math.min(0, color.r - other.r);
			var g = Math.min(0, color.g - other.g);
			var b = Math.min(0, color.b - other.b);

			return (new Color(r, g, b));
		} else {
			return (color.clone());
		}
	};

	/**
	 * @param {Object} other This another object we to see if it is equal to this object. If it is a color, it will compare colors. If not, it will always return false.
	 * @returns A boolean. True means the two objects are equal (the same color), false means they are not.  
	 */
	this.equals = function(other) {
		if(other.toString().indexOf("Color") >= 0) {
			return (color.compareTo(other) === 0);
		} else {
			return (false);
		}
	};
		

	/**
	 * @param {Object} other This another object we wish to compare this color with. If it is not a Color object -1 will be returned. Else, they are converted to HSV and hue values are compared.
	 * @returns An integer. A negative number means it's greater than, zero means its equal to, and a positive number means it's greater than the other Color object.  
	 */
	this.compareTo = function(other) {
		if(other.toString().indexOf("Color") >= 0) { 
			var hsvThis = color.getHSV();
			var hsvOther = other.getHSV();

			return (hsvThis.h - hsvOther.h);
		} else {
			return -1;
		}
	};

	/**
	 * @returns A String representation of this Color object.
	 */
	this.toString = function() {
		return ("Color( r: " + color.r + ", g: " + color.g + ", b: " + color.b + ", a: " + color.a + " )");
	};

	/**
	 * @returns A copy of this color object, as a new object.
	 */
	this.clone = function() {
		return (new Color(color.r, color.g, color.b));
	};

	/**
	 * @returns An eval()-able String representing this object.
	 */
	this.toSource = function() {
		return ("new Color(" + color.r + ", " + color.g + ", " + color.b + ", " + color.a + ")");
	};
}

/**
 * A Color object designed to store a color in the CMYK colorspace.
 * Credit goes to Harry (g6auc) for the conversion code.
 * @param {int} c This is the cyan component.
 * @param {int} m This is the magenta component.  
 * @param {int} y This is the yellow component.
 * @param {int} k This is the black component.
 */
function CMYK(c, m, y, k) {
	var cmyk = this;

	this.c = 0;
	this.m = 0;
	this.y = 0;
	this.k = 0;

	if(!isNaN(Number(c)) && !isNaN(Number(m)) && !isNaN(Number(y))) {
		this.c = Math.min(100, Math.max(0, Number(c)));
		this.m = Math.min(100, Math.max(0, Number(m)));
		this.y = Math.min(100, Math.max(0, Number(y)));

		if(!isNaN(Number(k))) {
			this.k = Math.min(100, Math.max(0, Number(k)));
		}

		if(k === 1) {
			this.c = 0;
			this.m = 0;
			this.y = 0;
		}
	}

	/**
	 * This formats the CMYK object so it can be directly used with a canvas context.
	 * @see Color#getCanvasColor 
	 * @returns A String formatted for Canvas. 
	 */
	this.getCanvasColor = function() {
		return(cmyk.getRGB().getCanvasColor());
	};

	/**
	 * @see Color#getComplementary
	 * @returns A new CMYK object representing the complementary of the current color. 
	 */
	this.getComplementary = function() {
		return (cmyk.getRGB().getComplementary().getCMYK());
	};

	/**
	 * @see HSV
	 * @returns A HSV object representing this color. 
	 */
	this.getHSV = function() {
		return (cmyk.getRGB().getHSV());
	};

	/**
	 * @see Color#getHex
	 * @returns A hex String representing this color. 
	 */
	this.getHex = function() {
		return (cmyk.getRGB().getHex());	
	};

	/**
	 * @see Color
	 * @returns An RGB object representing this color. 
	 */
	this.getRGB = function() {
		var c = this.c/100;					// convert to [0,1]
		var m = this.m/100;
		var y = this.y/100;
		var k = this.k/100;

		c = c*(1 - k) + k;					// first convert (c m y k) to (c m y) values
		m = m*(1 - k) + k;
		y = y*(1 - k) + k;

		var r = Math.round(255*(1 - c));		// then convert (c m k) to (r g b)
		var g = Math.round(255*(1 - m));
		var b = Math.round(255*(1 - y));

		return (new Color(r, g, b));
	};

	/**
	 * Adds the current CMYK object to the parameter object and returns the result as a new CMYK.
	 * If it's not a CMYK object, we return a clone of the object we are trying to add to.
	 * @param {Object} other This the second CMYK object.
	 * @returns A CMYK object which is the addition of two CMYKs.
	 */
	this.add = function(other) {
		if(other.toString().indexOf("CMYK") >= 0) {
			var c = Math.max(100, cmyk.c + other.c);
			var m = Math.max(100, cmyk.m + other.m);
			var y = Math.max(100, cmyk.y + other.y);
			var k = Math.max(100, cmyk.k + other.k);

			return (new CMYK(c, m, y, k));
		} else {
			return (cmyk.clone());
		}
	};

	/**
	 * We try to the given CMYK object from the current CMYK object.
	 * If it's not a CMYK object, we return a clone of the object we are trying to subtract from.
	 * @param {Object} other This the second CMYK object.
	 * @returns A CMYK object which is the subtraction of two CMYKs.
	 */
	this.subtract = function(other) {
		if(other.toString().indexOf("CMYK") >= 0) {
			var c = Math.min(0, cmyk.c - other.c);
			var m = Math.min(0, cmyk.m - other.m);
			var y = Math.min(0, cmyk.y - other.y);
			var k = Math.min(0, cmyk.k - other.k);

			return (new CMYK(c, m, y, k));
		} else {
			return (cmyk.clone());
		}
	};

	/**
	 * @see #compareTo
	 * @param {Object} other This another object we to see if it is equal to this object. If it is a CMYK, it will compare colors. If not, it will always return false.
	 * @returns A boolean. True means the two objects are equal (the same color), false means they are not.  
	 */
	this.equals = function(other) {
		if(other.toString().indexOf("CMYK") >= 0) {
			return (cmyk.compareTo(other) === 0);
		} else {
			return (false);
		}
	};

	/**
	 * @param {Object} other This another object we wish to compare this color with. If it is not a CMYK object -1 will be returned. Else, the average values will be compared.
	 * @returns An integer. A negative number means it's greater than, zero means its equal to, and a positive number means it's greater than the other CMYK object.  
	 */
	this.compareTo = function(other) {
		if(other.toString().indexOf("CMYK") >= 0) { 
			var cmykAvg = (cmyk.c + cmyk.m + cmyk.y + cmyk.k)/400;
			var otherAvg = (other.c + other.m + other.y + other.k)/400;
			return (cmykAvg - otherAvg);
		} else {
			return -1;
		}
	};

	/**
	 * @returns A String representation of this CMYK object.
	 */
	this.toString = function() {
		return ("CMYK( cyan: " + cmyk.c + ", magenta: " + cmyk.m + ", yellow: " + cmyk.y + ", black: " + cmyk.k + " )");
	};

	/**
	 * @returns A copy of this CMYK object, as a new object.
	 */
	this.clone = function() {
		return (new CMYK(cmyk.c, cmyk.m, cmyk.y, cmyk.k));
	};

	/**
	 * @returns An eval()-able String representing this object.
	 */
	this.toSource = function() {
		return ("new CMYK(" + cmyk.c + "," + cmyk.m + ", " + cmyk.y + ", " + cmyk.k + ")");
	};
}


/**
 * A Color object designed to store a color in the HSV colorspace.
 * Credit goes to Harry (g6auc) for the conversion code.
 * @param {int} h This is the hue value.
 * @param {int} s This is the saturation value.  
 * @param {int} v This is the value (value? ... heh, wow).
 */
function HSV(h, s, v) {
	var hsv = this;

	this.h = 0;
	this.s = 0;
	this.v = 0;

	if(!isNaN(Number(h)) && !isNaN(Number(s)) && !isNaN(Number(v))) {
		this.h = Math.min(360, Math.max(0, h));
		this.s = Math.min(100, Math.max(0, s));
		this.v = Math.min(100, Math.max(0, v));
	}		 

	/**
	 * This formats the HSV object so it can be directly used with a canvas context. 
	 * @see Color#getCanvasColor
	 * @returns A String formatted for Canvas. 
	 */
	this.getCanvasColor = function() {
		return(hsv.getRGB().getCanvasColor());
	};

	/**
	 * @see CMYK
	 * @returns A CMYK object representing this color. 
	 */
	this.getCMYK = function() {
		return (hsv.getRGB().getCMYK());
	};

	/**
	 * @returns A new Color object representing the complementary of the current color. 
	 */
	this.getComplementary = function() {
		var s = hsv.s/100;
		var v = hsv.v/100;
		var h = hsv.h;

		var h1 = Math.round((h + 180) % 360);
		var s1 = Math.round(100*(v*s)/(v*(s - 1) + 1));	// ((s == 0) && (v == 1))	gives 0/0
		var v1 = Math.round(100*(v*(s - 1) + 1));

		if (isNaN(s1)) { s1 = 0; }						// S1 = 0/0

		return (new HSV(h1, s1, v1));
	};

	/**
	 * @see Color#getHex
	 * @returns A hex String representing this color. 
	 */
	this.getHex = function() {
		return (hsv.getRGB().getHex());	
	};

	/**
	 * @see Color
	 * @returns An RGB object representing this color. 
	 */
	this.getRGB = function() {
		var h = this.h/360;
		var s = this.s/100;
		var v = this.v/100;

		if (s === 0) {
			var r = v; 	// grey
			var g = v;
			var b = v;
		} else {
			h = h * 6;					// [0,6)
			var i = Math.floor(h);
			var f = h - i;
			var p = v*(1 - s);
			var q = v*(1 - s*f);
			var t = v*(1 - s*(1 - f));

			switch (i) {
				case 0: r = v; g = t; b = p; break;
				case 1: r = q; g = v; b = p; break;
				case 2: r = p; g = v; b = t; break;
				case 3: r = p; g = q; b = v; break;
				case 4: r = t; g = p; b = v; break;
		    	default: r = v; g = p; b = q; break;
			}
		}

		r = Math.round(255*r);
		g = Math.round(255*g);
		b = Math.round(255*b);

		return (new Color(r, g, b));
	};

	/**
	 * Adds the current color object to the parameter object and returns the result as a new HSV.
	 * If it's not a HSV object, we return a clone of the object we are trying to add to.
	 * @param {Object} other This the second HSV object.
	 * @returns A HSV object which is the addition of two colors.
	 */
	this.add = function(other) {
		if(other.toString().indexOf("HSV") >= 0) {
			var h = Math.max(360, hsv.h + other.h);
			var s = Math.max(100, hsv.s + other.s);
			var v = Math.max(100, hsv.v + other.v);

			return (new HSV(h, s, v));
		} else {
			return (hsv.clone());
		}
	};

	/**
	 * We try to the given HSV object from the current HSV object.
	 * If it's not a HSV object, we return a clone of the object we are trying to subtract from.
	 * @param {Object} other This the second HSV object.
	 * @returns A HSV object which is the subtraction of two HSVs.
	 */
	this.subtract = function(other) {
		if(other.toString().indexOf("HSV") >= 0) {
			var h = Math.min(0, hsv.h - other.h);
			var s = Math.min(0, hsv.s - other.s);
			var v = Math.min(0, hsv.v - other.v);

			return (new HSV(h, s, v));
		} else {
			return (hsv.clone());
		}
	};

	/**
	 * @see #compareTo
	 * @param {Object} other This another object we to see if it is equal to this object. If it is a HSV, it will compare colors. If not, it will always return false.
	 * @returns A boolean. True means the two objects are equal (the same color), false means they are not.  
	 */
	this.equals = function(other) {
		if(other.toString().indexOf("HSV") >= 0) {
			return (hsv.compareTo(other) === 0);
		} else {
			return (false);
		}
	};

	/**
	 * @param {Object} other This another object we wish to compare this color with. If it is not a HSV object -1 will be returned. Else, the average values will be compared.
	 * @returns An integer. A negative number means it's greater than, zero means its equal to, and a positive number means it's greater than the other HSV object.  
	 */
	this.compareTo = function(other) {
		if(other.toString().indexOf("HSV") >= 0) { 
			var hsvAvg = (hsv.h + hsv.s + hsv.v)/560;
			var otherAvg = (other.h + other.s + other.v)/560;
			return (hsvAvg - otherAvg);
		} else {
			return -1;
		}
	};

	/**
	 * @returns A String representation of this HSV object.
	 */
	this.toString = function() {
		return ("HSV( hue: " + hsv.h + ", saturation: " + hsv.s + ", value: " + hsv.v + " )");
	};

	/**
	 * @returns A copy of this HSV object, as a new object.
	 */
	this.clone = function() {
		return (new HSV(hsv.h, hsv.s, hsv.v));
	};

	/**
	 * @returns An eval()-able String representing this object.
	 */
	this.toSource = function() {
		return ("new HSV(" + hsv.h + "," + hsv.s + ", " + hsv.v + ")");
	};
}

License: (K) All rights reversed. Copy what you like. (no credit required, for me anyways)

Ad blocker interference detected!


Wikia is a free-to-use site that makes money from advertising. We have a modified experience for viewers using ad blockers

Wikia is not accessible if you’ve made further modifications. Remove the custom ad blocker rule(s) and the page will load as expected.