phaser
Version:
A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers from the team at Phaser Studio Inc.
296 lines (275 loc) • 9.88 kB
JavaScript
/**
* @author Benjamin D. Richards <benjamindrichards@gmail.com>
* @copyright 2013-2026 Phaser Studio Inc.
* @license {@link https://opensource.org/licenses/MIT|MIT License}
*/
var Circular = require('../math/easing/circular');
var Linear = require('../math/Linear');
var Class = require('../utils/Class');
var Color = require('./color/Color');
var Interpolate = require('./color/Interpolate');
/**
* @classdesc
* The ColorBand class represents a transition from one color to another.
* It is used in a {@see Phaser.Display.ColorRamp}, and forms the basis
* of a {@see Phaser.GameObjects.Gradient}.
*
* ColorBand can control the transition by setting a middle point,
* a color space for blending, and an interpolation style.
*
* This class also records `start` and `end` points for use in a ramp.
* These indicate its position within the ramp.
*
* Colors are handled unpremultiplied, so RGB values may be larger than alpha.
*
* @class ColorBand
* @memberof Phaser.Display
* @since 4.0.0
* @constructor
*
* @param {Phaser.Types.Display.ColorBandConfig} [config] - The configuration to use for the band.
*/
var ColorBand = new Class({
initialize: function ColorBand (config)
{
if (!config) { config = {}; }
/**
* Identifies this object as a ColorBand.
* This property is read-only and must not be modified.
*
* @name Phaser.Display.ColorBand#isColorBand
* @type {boolean}
* @since 4.0.0
* @default true
* @readonly
*/
this.isColorBand = true;
/**
* The color at the start of the color band.
*
* @name Phaser.Display.ColorBand#colorStart
* @type {Phaser.Display.Color}
* @since 4.0.0
*/
this.colorStart = new Color();
/**
* The color at the end of the color band.
*
* @name Phaser.Display.ColorBand#colorEnd
* @type {Phaser.Display.Color}
* @since 4.0.0
*/
this.colorEnd = new Color();
/**
* The start point of this band within a ColorRamp.
* This value should be normalized within the ramp,
* between 0 (ramp start) and 1 (ramp end).
*
* @name Phaser.Display.ColorBand#start
* @type {number}
* @since 4.0.0
*/
this.start = config.start || 0;
/**
* The middle point of this band within a ColorRamp.
* This value should be normalized within the band,
* between 0 (band start) and 1 (band end).
* Middle point alters the shape of the color interpolation.
*
* Mathematically, the gradient should be 0.5 at the middle.
* We use a gamma curve to adjust the gradient.
* Thus, `0.5 = middle^gamma`.
* By the properties of logarithms, therefore,
* `gamma = log base middle of 0.5`.
*
* @name Phaser.Display.ColorBand#middle
* @type {number}
* @since 4.0.0
*/
this.middle = (config.middle === undefined) ? 0.5 : config.middle;
/**
* The end point of this band within a ColorRamp.
* This value should be normalized within the ramp,
* between 0 (ramp start) and 1 (ramp end).
*
* @name Phaser.Display.ColorBand#end
* @type {number}
* @since 4.0.0
*/
this.end = 1;
if (config.end !== undefined) { this.end = config.end; }
else if (config.size !== undefined) { this.end = this.start + config.size; }
/**
* The color interpolation.
* This can be one of the following codes:
*
* - 0: LINEAR - a straight blend.
* - 1: CURVED - color changes quickly at start and end,
* flattening in the middle. Good for convex surfaces.
* - 2: SINUSOIDAL - color changes quickly in the middle,
* flattening at start and end. Good for smooth transitions.
* - 3: CURVE_START - color changes quickly at the start,
* flattening at the end.
* - 4: CURVE_END - color changes quickly at the end,
* flattening at the start.
*
* Modes 2, 3, and 4 use the circular easing function directly.
* Mode 1 uses a related custom formula based on the unit circle.
*
* @name Phaser.Display.ColorBand#interpolation
* @type {number}
* @default 0
* @since 4.0.0
*/
this.interpolation = config.interpolation || 0;
/**
* The color space where interpolation should be done.
* This can be one of the following codes:
*
* - 0: RGBA - channels are blended directly.
* This can produce perceptually inaccurate results, as blending
* in RGB space does not account for how humans perceive color.
* - 1: HSVA_NEAREST - colors are blended in HSVA space,
* better preserving saturation and lightness.
* The hue is blended with the shortest angle, e.g. red and blue
* blend via purple, not green.
* - 2: HSVA_PLUS - as HSVA_NEAREST, but hue angle always increases.
* - 3: HSVA_MINUS - as HSVA_NEAREST, but hue angle always decreases.
*
* @name Phaser.Display.ColorBand#colorSpace
* @type {number}
* @default 0
* @since 4.0.0
*/
this.colorSpace = config.colorSpace || 0;
this.setColors(config.colorStart, config.colorEnd);
},
/**
* Set the colors of the band, from a variety of color formats.
*
* - A number is expected to be a 24 or 32 bit RGB or ARGB value.
* - A string is expected to be a hex code.
* - An array of numbers is expected to be RGB or RGBA in the range 0-1.
* - A Color object can be used.
*
* @method Phaser.Display.ColorBand#setColors
* @since 4.0.0
* @param {number | string | number[] | Phaser.Display.Color} [start=0x000000] - The color at the start of the band.
* @param {number | string | number[] | Phaser.Display.Color} [end] - The color at the end of the band. If not specified, equals `start`.
* @return {this} This ColorBand.
*/
setColors: function (start, end)
{
var alpha;
if (start === undefined)
{
start = 0x000000;
}
if (end === undefined)
{
end = start;
}
if (typeof start === 'number')
{
Color.IntegerToColor(start, this.colorStart);
}
else if (typeof start === 'string')
{
Color.HexStringToColor(start, this.colorStart);
}
else if (Array.isArray(start))
{
alpha = (start[3] === undefined) ? 1 : start[3];
this.colorStart.setGLTo(start[0], start[1], start[2], alpha);
}
else if (start instanceof Color)
{
this.colorStart.setTo(start.red, start.green, start.blue, start.alpha);
}
if (typeof end === 'number')
{
Color.IntegerToColor(end, this.colorEnd);
}
else if (typeof end === 'string')
{
Color.HexStringToColor(end, this.colorEnd);
}
else if (Array.isArray(end))
{
alpha = (end[3] === undefined) ? 1 : end[3];
this.colorEnd.setGLTo(end[0], end[1], end[2], alpha);
}
else if (end instanceof Color)
{
this.colorEnd.setTo(end.red, end.green, end.blue, end.alpha);
}
return this;
},
/**
* Returns the blended color at a normalized position within this band.
* The middle point gamma curve, interpolation mode, and color space are
* all applied before blending between `colorStart` and `colorEnd`.
*
* @method Phaser.Display.ColorBand#getColor
* @since 4.0.0
* @param {number} index - The normalized position within the band, where 0 is the band start and 1 is the band end.
* @return {Phaser.Types.Display.ColorObject} The blended color at that position.
*/
getColor: function (index)
{
// Apply middle gamma curve.
var gamma = Math.log(0.5) / Math.log(this.middle);
index = Math.pow(index, gamma);
index = Math.min(Math.max(0, index), 1);
// Apply interpolation mode.
switch (this.interpolation)
{
case 1:
{
// CURVED
if ((index *= 2) < 1)
{
index = 0.5 * Math.sqrt(1 - (--index * index));
}
else
{
index = 1 - index;
index = 1 - 0.5 * Math.sqrt(1 - index * index);
}
break;
}
case 2:
{
// SINUSOIDAL or circular
index = Circular.InOut(index);
break;
}
case 3:
{
// CURVE_START
index = Circular.Out(index);
break;
}
case 4:
{
// CURVE_END
index = Circular.In(index);
break;
}
}
var hsvSign = 0;
if (this.colorSpace === 2) { hsvSign = 1; }
else if (this.colorSpace === 3) { hsvSign = -1; }
var outColor = Interpolate.ColorWithColor(
this.colorStart,
this.colorEnd,
1,
index,
this.colorSpace !== 0, // Use HSV?
hsvSign
);
outColor.a = Linear(this.colorStart.alpha, this.colorEnd.alpha, index);
return outColor;
}
});
module.exports = ColorBand;