colorjs.io
Version:
Let’s get serious about color
212 lines (178 loc) • 3.92 kB
JavaScript
import * as util from "./util.js";
import hooks from "./hooks.js";
import defaults from "./defaults.js";
import ColorSpace from "./space.js";
import {WHITES} from "./adapt.js";
import {
getColor,
parse,
to,
serialize,
inGamut,
toGamut,
distance,
equals,
get,
getAll,
set,
setAll,
display,
} from "./index-fn.js";
import "./spaces/xyz-d50.js";
import "./spaces/srgb.js";
/**
* Class that represents a color
*/
export default class Color {
/**
* Creates an instance of Color.
* Signatures:
* - `new Color(stringToParse)`
* - `new Color(otherColor)`
* - `new Color({space, coords, alpha})`
* - `new Color(space, coords, alpha)`
* - `new Color(spaceId, coords, alpha)`
*/
constructor (...args) {
let color;
if (args.length === 1) {
color = getColor(args[0]);
}
let space, coords, alpha;
if (color) {
space = color.space || color.spaceId;
coords = color.coords;
alpha = color.alpha;
}
else {
// default signature new Color(ColorSpace, array [, alpha])
[space, coords, alpha] = args;
}
this.#space = ColorSpace.get(space);
this.coords = coords? coords.slice() : [0, 0, 0];
this.alpha = alpha < 1? alpha : 1; // this also deals with NaN etc
// Convert "NaN" to NaN
for (let i = 0; i < this.coords.length; i++) {
if (this.coords[i] === "NaN") {
this.coords[i] = NaN;
}
}
// Define getters and setters for each coordinate
for (let id in this.#space.coords) {
Object.defineProperty(this, id, {
get: () => this.get(id),
set: value => this.set(id, value)
});
}
}
#space
get space() {
return this.#space;
}
get spaceId() {
return this.#space.id;
}
clone () {
return new Color(this.space, this.coords, this.alpha);
}
toJSON () {
return {
spaceId: this.spaceId,
coords: this.coords,
alpha: this.alpha
};
}
display (...args) {
let ret = display(this, ...args);
// Convert color object to Color instance
ret.color = new Color(ret.color);
return ret;
}
/**
* Get a color from the argument passed
* Basically gets us the same result as new Color(color) but doesn't clone an existing color object
*/
static get (color, ...args) {
if (color instanceof Color) {
return color;
}
return new Color(color, ...args);
}
static defineFunction (name, code, o = code) {
if (arguments.length === 1) {
[name, code, o] = [arguments[0].name, arguments[0], arguments[0]];
}
let {instance = true, returns} = o;
let func = function (...args) {
let ret = code(...args);
if (returns === "color") {
ret = Color.get(ret);
}
else if (returns === "function<color>") {
let f = ret;
ret = function(...args) {
let ret = f(...args);
return Color.get(ret);
}
// Copy any function metadata
Object.assign(ret, f);
}
else if (returns === "array<color>") {
ret = ret.map(c => Color.get(c));
}
return ret;
}
if (!(name in Color)) {
Color[name] = func;
}
if (instance) {
Color.prototype[name] = function (...args) {
return func(this, ...args);
}
}
}
static defineFunctions(o) {
for (let name in o) {
Color.defineFunction(name, o[name], o[name]);
}
}
static extend (exports) {
if (exports.register) {
exports.register(Color);
}
else if (exports.default) {
Color.defineFunction(exports.default.name, exports.default);
}
else if (typeof exports === "function") {
Color.defineFunction(exports);
}
else {
// No register method, just add the module's functions
for (let name in exports) {
Color.defineFunction(name, exports[name]);
}
}
}
};
Color.defineFunctions({
get,
getAll,
set,
setAll,
to,
equals,
inGamut,
toGamut,
distance,
toString: serialize,
});
Object.assign(Color, {
util,
hooks,
WHITES,
Space: ColorSpace,
spaces: ColorSpace.registry,
parse,
// Global defaults one may want to configure
defaults
});