mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2024-12-29 09:22:22 -05:00
Color: Improve CSS string parser and docs
This commit is contained in:
parent
e41ed5e723
commit
da3a36230f
2 changed files with 89 additions and 39 deletions
|
@ -56,9 +56,9 @@ var Color = Base.extend(new function() {
|
|||
colorCache = {},
|
||||
colorCtx;
|
||||
|
||||
// TODO: Implement hsv, etc. CSS parsing!
|
||||
function fromCSS(string) {
|
||||
var match = string.match(/^#(\w{1,2})(\w{1,2})(\w{1,2})$/),
|
||||
type = 'rgb',
|
||||
components;
|
||||
if (match) {
|
||||
// Hex
|
||||
|
@ -68,12 +68,34 @@ var Color = Base.extend(new function() {
|
|||
components[i] = parseInt(value.length == 1
|
||||
? value + value : value, 16) / 255;
|
||||
}
|
||||
} else if (match = string.match(/^rgba?\((.*)\)$/)) {
|
||||
// RGB / RGBA
|
||||
components = match[1].split(',');
|
||||
for (var i = 0, l = components.length; i < l; i++) {
|
||||
var value = +components[i];
|
||||
components[i] = i < 3 ? value / 255 : value;
|
||||
} else if (match = string.match(/^(rgb|hsl)a?\((.*)\)$/)) {
|
||||
// RGB / RGBA or HSL / HSLA
|
||||
type = match[1];
|
||||
components = match[2].split(/[,\s]+/g);
|
||||
var isHSL = type === 'hsl';
|
||||
for (var i = 0, l = Math.min(components.length, 4); i < l; i++) {
|
||||
var component = components[i];
|
||||
// Use `parseFloat()` instead of `+value` to parse '\d+%' to
|
||||
// float for HSL:
|
||||
var value = parseFloat(component);
|
||||
if (isHSL) {
|
||||
if (i === 0) {
|
||||
// handle 'deg', 'turn', 'rad' 'grad':
|
||||
var unit = component.match(/([a-z]*)$/)[1];
|
||||
value *= ({
|
||||
turn: 360,
|
||||
rad: 180 / Math.PI,
|
||||
grad: 0.9 // 360 / 400
|
||||
}[unit] || 1);
|
||||
} else if (i < 3) {
|
||||
// Percentages to 0..1
|
||||
value /= 100;
|
||||
}
|
||||
} else if (i < 3) {
|
||||
// RGB color values to 0..1
|
||||
value /= 255;
|
||||
}
|
||||
components[i] = value;
|
||||
}
|
||||
} else if (window) {
|
||||
// Named
|
||||
|
@ -106,7 +128,7 @@ var Color = Base.extend(new function() {
|
|||
// Web-workers can't resolve CSS color names, for now.
|
||||
components = [0, 0, 0];
|
||||
}
|
||||
return components;
|
||||
return [type, components];
|
||||
}
|
||||
|
||||
// For hsb-rgb conversion, used to lookup the right parameters in the
|
||||
|
@ -237,36 +259,40 @@ var Color = Base.extend(new function() {
|
|||
// hsb and hsl. Handle this here separately, by testing for
|
||||
// overlaps and skipping conversion if the type is /hs[bl]/
|
||||
hasOverlap = /^(hue|saturation)$/.test(name),
|
||||
// Produce value parser function for the given type / propeprty
|
||||
// name combination.
|
||||
parser = componentParsers[type][index] = name === 'gradient'
|
||||
? function(value) {
|
||||
var current = this._components[0];
|
||||
value = Gradient.read(Array.isArray(value) ? value
|
||||
: arguments, 0, { readNull: true });
|
||||
if (current !== value) {
|
||||
if (current)
|
||||
current._removeOwner(this);
|
||||
if (value)
|
||||
value._addOwner(this);
|
||||
// Produce value parser function for the given type / property
|
||||
parser = componentParsers[type][index] = type === 'gradient'
|
||||
? name === 'gradient'
|
||||
// gradient property of gradient color:
|
||||
? function(value) {
|
||||
var current = this._components[0];
|
||||
value = Gradient.read(
|
||||
Array.isArray(value)
|
||||
? value
|
||||
: arguments, 0, { readNull: true }
|
||||
);
|
||||
if (current !== value) {
|
||||
if (current)
|
||||
current._removeOwner(this);
|
||||
if (value)
|
||||
value._addOwner(this);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
: type === 'gradient'
|
||||
? function(/* value */) {
|
||||
// all other (point) properties of gradient color:
|
||||
: function(/* value */) {
|
||||
return Point.read(arguments, 0, {
|
||||
readNull: name === 'highlight',
|
||||
clone: true
|
||||
});
|
||||
}
|
||||
: function(value) {
|
||||
// NOTE: We don't clamp values here, they're only
|
||||
// clamped once the actual CSS values are produced.
|
||||
// Gotta love the fact that isNaN(null) is false,
|
||||
// while isNaN(undefined) is true.
|
||||
return value == null || isNaN(value) ? 0 : value;
|
||||
};
|
||||
|
||||
// Normal number component properties:
|
||||
: function(value) {
|
||||
// NOTE: We don't clamp values here, they're only
|
||||
// clamped once the actual CSS values are produced.
|
||||
// Gotta love the fact that isNaN(null) is false,
|
||||
// while isNaN(undefined) is true.
|
||||
return value == null || isNaN(value) ? 0 : +value;
|
||||
};
|
||||
this['get' + part] = function() {
|
||||
return this._type === type
|
||||
|| hasOverlap && /^hs[bl]$/.test(this._type)
|
||||
|
@ -411,6 +437,25 @@ var Color = Base.extend(new function() {
|
|||
* }
|
||||
* });
|
||||
*/
|
||||
/**
|
||||
* Creates a Color object from a CSS string. All common CSS color string
|
||||
* formats are supported:
|
||||
* - Named colors (e.g. `'red'`, `'fuchsia'`, …)
|
||||
* - Hex strings (`'#ffff00'`, `'#ff0'`, …)
|
||||
* - RGB strings (`'rgb(255, 128, 0)'`, `'rgba(255, 128, 0, 0.5)'`, …)
|
||||
* - HSL strings (`'hsl(180deg, 20%, 50%)'`,
|
||||
* `'hsla(3.14rad, 20%, 50%, 0.5)'`, …)
|
||||
*
|
||||
* @name Color#initialize
|
||||
* @param {String} color the color's CSS string representation
|
||||
*
|
||||
* @example {@paperscript}
|
||||
* var circle = new Path.Circle({
|
||||
* center: [80, 50],
|
||||
* radius: 30,
|
||||
* fillColor: new Color('rgba(255, 255, 0, 0.5)')
|
||||
* });
|
||||
*/
|
||||
/**
|
||||
* Creates a gradient Color object.
|
||||
*
|
||||
|
@ -544,8 +589,9 @@ var Color = Base.extend(new function() {
|
|||
if (values.length > length)
|
||||
values = Base.slice(values, 0, length);
|
||||
} else if (argType === 'string') {
|
||||
type = 'rgb';
|
||||
components = fromCSS(arg);
|
||||
var converted = fromCSS(arg);
|
||||
type = converted[0];
|
||||
components = converted[1];
|
||||
if (components.length === 4) {
|
||||
alpha = components[3];
|
||||
components.length--;
|
||||
|
|
|
@ -61,22 +61,26 @@ test('Creating Colors', function() {
|
|||
'Color from name (red)');
|
||||
|
||||
equals(new Color('#ff0000'), new Color(1, 0, 0),
|
||||
'Color from hex code');
|
||||
'Color from hex string');
|
||||
|
||||
equals(new Color('rgb(255, 0, 0)'), new Color(1, 0, 0),
|
||||
'Color from RGB code');
|
||||
'Color from rgb() string');
|
||||
|
||||
equals(new Color('rgba(255, 0, 0, 0.5)'), new Color(1, 0, 0, 0.5),
|
||||
'Color from RGBA code');
|
||||
'Color from rgba() string');
|
||||
|
||||
equals(new Color('hsl(180deg, 20%, 40%)'),
|
||||
new Color({ hue: 180, saturation: 0.2, lightness: 0.4 }),
|
||||
'Color from hsl() string');
|
||||
|
||||
equals(new Color({ red: 1, green: 0, blue: 1}),
|
||||
new Color(1, 0, 1), 'Color from rgb object literal');
|
||||
new Color(1, 0, 1), 'Color from RGB object literal');
|
||||
|
||||
equals(new Color({ gray: 0.2 }),
|
||||
new Color(0.2), 'Color from gray object literal');
|
||||
|
||||
equals(new Color({ hue: 0, saturation: 1, brightness: 1}),
|
||||
new Color(1, 0, 0).convert('hsb'), 'Color from hsb object literal');
|
||||
new Color(1, 0, 0).convert('hsb'), 'Color from HSB object literal');
|
||||
|
||||
equals(new Color([1, 0, 0]), new Color(1, 0, 0),
|
||||
'RGB Color from array');
|
||||
|
|
Loading…
Reference in a new issue