API Documentation for: 1.0.0
Show:

File:ColorPlugin.js

/*
* ColorPlugin
* Visit http://createjs.com/ for documentation, updates and examples.
*
* Copyright (c) 2010 gskinner.com, inc.
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/

/**
 * @module TweenJS
 */
 
this.createjs = this.createjs||{};

(function() {
	"use strict";
	
	/**
	 * The ColorPlugin enables tweening of almost any CSS color values. This includes 3 or 6 digit hex colors (`#00FF00`),
	 * rgb, rgba, hsl, and hsla colors (but not named colors, such as `red`).
	 * 
	 * It can operate in either `rgb` or `hsl` mode. It will convert all colors into that mode, and output them accordingly.
	 * @class ColorPlugin
	 * @constructor
	 */
	function ColorPlugin() {
		throw("ColorPlugin cannot be instantiated.");
	}
	var s = ColorPlugin;
	
	
	/**
	 * READ-ONLY. RegExp pattern that detects CSS color values.
	 * @property COLOR_RE
	 * @type {RegExp}
	 * @static
	 * @readonly
	 */
	s.COLOR_RE = /^#[0-9a-fA-F]{3}|^hsla?\(|^rgba?\(/
	
	/**
	 * READ-ONLY. RegExp pattern that matches rgb or hsl color strings, with groups for each value.
	 * @property RGB_HSL_RE
	 * @type {RegExp}
	 * @static
	 * @readonly
	 */
	s.RGB_HSL_RE = /^(?:rgb|hsl)a?\((\d{1,3})%?, ?(\d{1,3})%?, ?(\d{1,3})%?(?:, ?([0-9.]+))?\)$/;
	
	/**
	 * READ-ONLY. RegExp pattern that matches a 3 or 6 digit RGB string with a preceding #.
	 * @property HEX_RE
	 * @type {RegExp}
	 * @static
	 * @readonly
	 */
	s.HEX_RE = /^#((?:[a-f0-9]{3}){1,2})$/i;

	/**
	 * @property _mode
	 * @type {string}
	 * @static
	 * @default rgb
	 * @protected
	 */
	s._mode = "rgb";
	
	/**
	 * READ-ONLY. A unique identifying string for this plugin. Used by TweenJS to ensure duplicate plugins are not installed on a tween.
	 * @property ID
	 * @type {String}
	 * @static
	 * @readonly
	 **/
	s.ID = "Color";

	/**
	 * Installs this plugin for use with TweenJS. Call this once after TweenJS is loaded to enable this plugin.
	 * @method install
	 * @param {String} mode A string equalling either "rgb" or "hsl" indicating what color mode should be used for calculations
	 * and output. You can input any color type regardless of the mode setting.
	 * @static
	 **/
	s.install = function(mode) {
		s._mode = mode || s._mode;
		createjs.Tween._installPlugin(s);
	};
	
	/**
	 * Called by TweenJS when a new property initializes on a tween.
	 * See {{#crossLink "SamplePlugin/init"}}{{/crossLink}} for more info.
	 * @method init
	 * @param {Tween} tween
	 * @param {String} prop
	 * @param {any} value
	 * @return {any}
	 * @static
	 **/
	s.init = function(tween, prop, value) {
		var data = tween.pluginData;
		value = value === undefined ? tween.target[prop] : value;
		if (!data.Color_disabled && typeof value === "string" && s.COLOR_RE.exec(value)) {
			tween._addPlugin(s);
			var colorData = data.Color || (data.Color = {});
			colorData[prop] = true;
			return getColorObj(value, s._mode);
		}
	};
	
	/**
	 * Called when a new step is added to a tween (ie. a new "to" action is added to a tween).
	 * See {{#crossLink "SamplePlugin/step"}}{{/crossLink}} for more info.
	 * @method step
	 * @param {Tween} tween
	 * @param {TweenStep} step
	 * @param {Object} props
	 * @static
	 **/
	s.step = function(tween, step, props) {
		var n, colorData = tween.pluginData.Color;
		for (n in props) {
			if (!colorData[n]) { continue; }
			step.props[n] = getColorObj(step.props[n], s._mode);
		}
	};

	/**
	 * Called before a property is updated by the tween.
	 * See {{#crossLink "SamplePlugin/change"}}{{/crossLink}} for more info.
	 * @method change
	 * @param {Tween} tween
	 * @param {TweenStep} step
	 * @param {String} prop
	 * @param {any} value
	 * @param {Number} ratio
	 * @param {Boolean} end
	 * @return {any}
	 * @static
	 **/
	s.change = function(tween, step, prop, value, ratio, end) {
		if (!tween.pluginData.Color[prop]) { return; }
		var o0=step.prev.props[prop], o1 = step.props[prop];
		
		var d0 = o1[0]-o0[0], v1=o0[1]+(o1[1]-o0[1])*ratio+0.5|0, v2=o0[2]+(o1[2]-o0[2])*ratio+0.5|0, a=(o0[3]+(o1[3]-o0[3])*ratio).toFixed(3);
		if (s._mode === "rgb") {
			return "rgba("+(o0[0]+d0*ratio+0.5|0)+", "+v1+", "+v2+", "+a+")";
		} else {
			if (d0 > 180) { d0 -= 360; }
			else if (d0 < -180) { d0 += 360; }
			return "hsla("+((o0[0]+d0*ratio+360)%360+0.5|0)+", "+v1+"%, "+v2+"%, "+a+")";
		}
	};
	
	
// private helper methods:
	function getColorObj(value, mode) {
		if (value[0] === "#") { return parseHex(value, mode); }
		else { return parseRgbOrHsl(value, mode); }
	}
	
	function parseRgbOrHsl(value, mode) {
		var o=[0,0,0,1], result = s.RGB_HSL_RE.exec(value);
		if (!result) { return o;}
		
		var rgb = value[0] === "r", m = (rgb && value.lastIndexOf("%") !== -1) ? 255/100 : 1;
		o[0] = parseInt(result[1]) * m;
		o[1] = parseInt(result[2]) * m;
		o[2] = parseInt(result[3]) * m;
		o[3] = result[4] === undefined ? 1 : parseFloat(result[4]);
		
		if (!rgb && mode === "rgb") { hslToRgb(o); }
		else if (rgb && mode !== "rgb") { rgbToHsl(o); }
		return o;
	};
	
	function parseHex(value, mode) {
		var o=[0,0,0,1], result = s.HEX_RE.exec(value);
		if (!result) { return o;}
		var hex = result[1];
		if (hex.length === 3) { hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2]; }
		
		var num = parseInt(hex, 16);
		o[0] = num>>16;
		o[1] = num>>8&0xFF;
		o[2] = num&0xFF;
		
		if (mode !== "rgb") { rgbToHsl(o); }
		
		return o;
	};
	
	function rgbToHsl(o) {
		var r=o[0], g=o[1], b=o[2], m = 1/255;
		r *= m, g *= m, b *= m;
		// TODO: Math.max/min are pretty slow vs conditional assignment
		var max = Math.max(r, g, b), min = Math.min(r, g, b);
		var h, s, l = (max + min) * 0.5;

		if (max === min) {
			h = s = 0; // achromatic
		} else {
			var d = max - min;
			s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
			if (max === r) { h = (g - b) / d + (g < b ? 6 : 0); }
			else if (max === g) { h = (b - r) / d + 2; }
			else { h = (r - g) / d + 4; }
			h /= 6;
		}
		o[0] = h*360;
		o[1] = s*100;
		o[2] = l*100;
	}

	function hslToRgb(o) {
		var h=(o[0]%360)/360, s=o[1]/100, l=o[2]/100;
		var r, g, b;
		if (s == 0) {
			r = g = b = l; // achromatic
		} else {
			var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
			var p = 2 * l - q;
			r = hue2rgb(p, q, h + 1 / 3);
			g = hue2rgb(p, q, h);
			b = hue2rgb(p, q, h - 1 / 3);
		}
		o[0] = r*255;
		o[1] = g*255;
		o[2] = b*255;
	}
	
	function hue2rgb(p, q, t) {
		if (t < 0) { t += 1; }
		else if (t > 1) { t -= 1; }
		
		if (t < 1 / 6) { return p + (q - p) * 6 * t; }
		if (t < 0.5) { return q; }
		if (t < 2 / 3) { return p + (q - p) * (2 / 3 - t) * 6; }
		return p;
	}

	createjs.ColorPlugin = s;
}());