pleasejs
Version:
JS library to generate random pleasing colors/color schemes
711 lines (660 loc) • 17.5 kB
JavaScript
/*!Please JS v0.4.2, Jordan Checkman 2014, Checkman.io, MIT License, Have fun.*/
(function( globalName, root, factory ) {
if ( typeof define === 'function' && define.amd ) {
define( [], factory );
}
else if ( typeof exports === 'object' ) {
module.exports = factory();
}
else{
root[globalName] = factory();
}
}('Please', this, function(){
'use strict';
function define_Please(){
var Please = {};
var color_data = {
aliceblue: "F0F8FF",
antiquewhite: "FAEBD7",
aqua: "00FFFF",
aquamarine: "7FFFD4",
azure: "F0FFFF",
beige: "F5F5DC",
bisque: "FFE4C4",
black: "000000",
blanchedalmond: "FFEBCD",
blue: "0000FF",
blueviolet: "8A2BE2",
brown: "A52A2A",
burlywood: "DEB887",
cadetblue: "5F9EA0",
chartreuse: "7FFF00",
chocolate: "D2691E",
coral: "FF7F50",
cornflowerblue: "6495ED",
cornsilk: "FFF8DC",
crimson: "DC143C",
cyan: "00FFFF",
darkblue: "00008B",
darkcyan: "008B8B",
darkgoldenrod: "B8860B",
darkgray: "A9A9A9",
darkgrey: "A9A9A9",
darkgreen: "006400",
darkkhaki: "BDB76B",
darkmagenta: "8B008B",
darkolivegreen: "556B2F",
darkorange: "FF8C00",
darkorchid: "9932CC",
darkred: "8B0000",
darksalmon: "E9967A",
darkseagreen: "8FBC8F",
darkslateblue: "483D8B",
darkslategray: "2F4F4F",
darkslategrey: "2F4F4F",
darkturquoise: "00CED1",
darkviolet: "9400D3",
deeppink: "FF1493",
deepskyblue: "00BFFF",
dimgray: "696969",
dimgrey: "696969",
dodgerblue: "1E90FF",
firebrick: "B22222",
floralwhite: "FFFAF0",
forestgreen: "228B22",
fuchsia: "FF00FF",
gainsboro: "DCDCDC",
ghostwhite: "F8F8FF",
gold: "FFD700",
goldenrod: "DAA520",
gray: "808080",
grey: "808080",
green: "008000",
greenyellow: "ADFF2F",
honeydew: "F0FFF0",
hotpink: "FF69B4",
indianred: "CD5C5C",
indigo: "4B0082",
ivory: "FFFFF0",
khaki: "F0E68C",
lavender: "E6E6FA",
lavenderblush: "FFF0F5",
lawngreen: "7CFC00",
lemonchiffon: "FFFACD",
lightblue: "ADD8E6",
lightcoral: "F08080",
lightcyan: "E0FFFF",
lightgoldenrodyellow: "FAFAD2",
lightgray: "D3D3D3",
lightgrey: "D3D3D3",
lightgreen: "90EE90",
lightpink: "FFB6C1",
lightsalmon: "FFA07A",
lightseagreen: "20B2AA",
lightskyblue: "87CEFA",
lightslategray: "778899",
lightslategrey: "778899",
lightsteelblue: "B0C4DE",
lightyellow: "FFFFE0",
lime: "00FF00",
limegreen: "32CD32",
linen: "FAF0E6",
magenta: "FF00FF",
maroon: "800000",
mediumaquamarine: "66CDAA",
mediumblue: "0000CD",
mediumorchid: "BA55D3",
mediumpurple: "9370D8",
mediumseagreen: "3CB371",
mediumslateblue: "7B68EE",
mediumspringgreen: "00FA9A",
mediumturquoise: "48D1CC",
mediumvioletred: "C71585",
midnightblue: "191970",
mintcream: "F5FFFA",
mistyrose: "FFE4E1",
moccasin: "FFE4B5",
navajowhite: "FFDEAD",
navy: "000080",
oldlace: "FDF5E6",
olive: "808000",
olivedrab: "6B8E23",
orange: "FFA500",
orangered: "FF4500",
orchid: "DA70D6",
palegoldenrod: "EEE8AA",
palegreen: "98FB98",
paleturquoise: "AFEEEE",
palevioletred: "D87093",
papayawhip: "FFEFD5",
peachpuff: "FFDAB9",
peru: "CD853F",
pink: "FFC0CB",
plum: "DDA0DD",
powderblue: "B0E0E6",
purple: "800080",
rebeccapurple: "663399",
red: "FF0000",
rosybrown: "BC8F8F",
royalblue: "4169E1",
saddlebrown: "8B4513",
salmon: "FA8072",
sandybrown: "F4A460",
seagreen: "2E8B57",
seashell: "FFF5EE",
sienna: "A0522D",
silver: "C0C0C0",
skyblue: "87CEEB",
slateblue: "6A5ACD",
slategray: "708090",
slategrey: "708090",
snow: "FFFAFA",
springgreen: "00FF7F",
steelblue: "4682B4",
tan: "D2B48C",
teal: "008080",
thistle: "D8BFD8",
tomato: "FF6347",
turquoise: "40E0D0",
violet: "EE82EE",
wheat: "F5DEB3",
white: "FFFFFF",
whitesmoke: "F5F5F5",
yellow: "FFFF00",
yellowgreen: "9ACD32"
};
var PHI = 0.618033988749895;
var make_color_default = {
hue: null,
saturation: null,
value: null,
base_color: '',
greyscale: false,
grayscale: false, //whatever I support them both, murrica
golden: true,
full_random: false,
colors_returned: 1,
format: 'hex',
seed: null
};
var make_scheme_default = {
scheme_type: 'analogous',
format: 'hex'
};
var make_contrast_default = {
golden: false,
format: 'hex'
};
function random_int( min, max, randomiser ){
var random = Math.random;
if ( randomiser instanceof RC4Random ) {
random = randomiser.random;
}
return Math.floor( random() * ( max - min + 1 )) + min;
}
function random_float( min, max, randomiser ){
var random = Math.random;
if (randomiser instanceof RC4Random) {
random = randomiser.random;
}
return random() * ( max - min ) + min;
}
function clamp( num, min, max ){
return Math.max( min, Math.min( num, max ));
}
function convert_to_format( format_string, array ){
var i;
switch( format_string ){
case 'hex':
for ( i = 0; i < array.length; i++ ) {
array[i] = Please.HSV_to_HEX( array[i] );
}
break;
case 'rgb':
for ( i = 0; i < array.length; i++ ) {
array[i] = Please.HSV_to_RGB( array[i] );
}
break;
case 'rgb-string':
for ( i = 0; i < array.length; i++ ) {
var raw_rgb = Please.HSV_to_RGB( array[i] );
array[i] =
"rgb(" +
raw_rgb.r + "," +
raw_rgb.g + "," +
raw_rgb.b + ")";
}
break;
case 'hsv':
break;
default:
console.error( 'Format not recognized.' );
break;
}
return array;
}
function contrast_quotient( HSV ){
var RGB = Please.HSV_to_RGB( HSV );
var YIQ = ( ( RGB.r * 299 ) +
( RGB.g * 587 ) +
( RGB.b * 114 )
) / 1000;
return ( YIQ >= 128 ) ? 'dark' : 'light';
}
function copy_object( object ){
var copy = {};
for( var key in object ){
if( object.hasOwnProperty( key )){
copy[key] = object[key];
}
}
return copy;
}
function RC4Random( seed ) {
var key_schedule = [];
var key_schedule_i = 0;
var key_schedule_j = 0;
for ( var k = 0; k < 256; k++ ) key_schedule[k] = k;
for ( var i = 0, j = 0; i < 256; i++ ) {
j = ( j + key_schedule[i] + seed.charCodeAt( i % seed.length )) % 256;
var t = key_schedule[i];
key_schedule[i] = key_schedule[j];
key_schedule[j] = t;
}
function get_random_byte() {
key_schedule_i = ( key_schedule_i + 1 ) % 256;
key_schedule_j = ( key_schedule_j + key_schedule[key_schedule_i] ) % 256;
var t = key_schedule[key_schedule_i];
key_schedule[key_schedule_i] = key_schedule[key_schedule_j];
key_schedule[key_schedule_j] = t;
return key_schedule[( key_schedule[key_schedule_i] + key_schedule[key_schedule_j] ) % 256];
}
this.random = function() {
for ( var i = 0, number = 0, multiplier = 1; i < 8; i++ ) {
number += get_random_byte() * multiplier;
multiplier *= 256;
}
return number / 18446744073709551616;
};
}
Please.NAME_to_HEX = function( name ){
name = name.toLowerCase();
if( name in color_data ){
return color_data[name];
}
else{
console.error( 'Color name not recognized.' );
}
};
Please.NAME_to_RGB = function( name ){
return Please.HEX_to_RGB( Please.NAME_to_HEX( name ));
};
Please.NAME_to_HSV = function( name ){
return Please.HEX_to_HSV( Please.NAME_to_HEX( name ));
};
//accepts hex string, produces RGB object
Please.HEX_to_RGB = function( hex ){
var regex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace( regex, function( m, r, g, b ) {
return r + r + g + g + b + b;
});
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec( hex );
return result ? {
r: parseInt( result[1], 16 ),
g: parseInt( result[2], 16 ),
b: parseInt( result[3], 16 )
} : null;
};
//accepts RGB object, produces hex string
Please.RGB_to_HEX = function( RGB ){
return "#" +
(( 1 << 24 ) + ( RGB.r << 16 ) + ( RGB.g << 8 ) + RGB.b )
.toString( 16 ).slice( 1 );
};
//accepts HSV object, returns RGB object
Please.HSV_to_RGB = function( HSV ){
var h = HSV.h,
s = HSV.s,
v = HSV.v;
var r, g, b;
var i, f, p, q, t;
if( s === 0 ){
return {
r: v,
g: v,
b: v
};
}
h /= 60;
i = Math.floor( h );
f = h - i;
p = v * ( 1 - s );
q = v * ( 1 - s * f );
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;
case 5:
r = v;
g = p;
b = q;
break;
}
return {
r: Math.floor(r * 255),
g: Math.floor(g * 255),
b: Math.floor(b * 255)
};
};
//accepts RGB object, returns HSV object
Please.RGB_to_HSV = function( RGB ){
var r = ( RGB.r / 255 ),
g = ( RGB.g / 255 ),
b = ( RGB.b / 255 );
var computed_H = 0,
computed_S = 0,
computed_V = 0;
var min_RGB = Math.min( r, Math.min( g, b ) ),
max_RGB = Math.max( r, Math.max( g, b ) );
// Black-gray-white
if ( min_RGB === max_RGB ) {
computed_V = min_RGB;
return{
h: 0,
s: 0,
v: computed_V
};
}
// Colors other than black-gray-white:
var d = ( r === min_RGB ) ? g - b : (( b === min_RGB ) ? r - g : b - r);
var h = ( r === min_RGB ) ? 3 : (( b === min_RGB ) ? 1 : 5 );
computed_H = 60 * ( h - d / ( max_RGB - min_RGB ));
computed_S = ( max_RGB - min_RGB ) / max_RGB;
computed_V = max_RGB;
return {
h: computed_H,
s: computed_S,
v: computed_V
};
};
//accepts HSV object, returns hex string
Please.HSV_to_HEX = function( HSV ){
return Please.RGB_to_HEX( Please.HSV_to_RGB( HSV ));
};
//accepts hex string, returns HSV object
Please.HEX_to_HSV = function( hex ){
return Please.RGB_to_HSV( Please.HEX_to_RGB( hex ));
};
//accepts HSV object and options object, returns list or single object depending on options
Please.make_scheme = function( HSV, options ){
//clone base please options
var scheme_options = copy_object( make_scheme_default ),
adjusted,
secondary,
adjusted_h,
adjusted_s,
adjusted_v,
i;
if( options !== null ){
//override base Please options
for( var key in options ){
if( options.hasOwnProperty( key )){
scheme_options[key] = options[key];
}
}
}
var scheme = [HSV];
//DRY for repeated cloning
function clone( HSV ){
return{
h: HSV.h,
s: HSV.s,
v: HSV.v
};
}
switch( scheme_options.scheme_type.toLowerCase() ){
case 'monochromatic':
case 'mono':
for ( i = 1; i <= 2; i++ ) {
adjusted = clone( HSV );
adjusted_s = adjusted.s + ( 0.1 * i );
adjusted_s = clamp( adjusted_s, 0, 1 );
adjusted_v = adjusted.v + ( 0.1 * i );
adjusted_v = clamp( adjusted_v, 0, 1 );
adjusted.s = adjusted_s;
adjusted.v = adjusted_v;
scheme.push( adjusted );
}
for ( i = 1; i <= 2; i++ ) {
adjusted = clone( HSV );
adjusted_s = adjusted.s - ( 0.1 * i );
adjusted_s = clamp( adjusted_s, 0, 1 );
adjusted_v = adjusted.v - ( 0.1 * i );
adjusted_v = clamp( adjusted_v, 0, 1 );
adjusted.s = adjusted_s;
adjusted.v = adjusted_v;
scheme.push( adjusted );
}
break;
case 'complementary':
case 'complement':
case 'comp':
adjusted = clone( HSV );
adjusted.h = ( adjusted.h + 180 ) % 360;
scheme.push( adjusted );
break;
//30 degree seperation
case 'split-complementary':
case 'split-complement':
case 'split':
adjusted = clone( HSV );
adjusted.h = ( adjusted.h + 165 ) % 360;
scheme.push( adjusted );
adjusted = clone( HSV );
adjusted.h = Math.abs( ( adjusted.h - 165 ) % 360 );
scheme.push( adjusted );
break;
case 'double-complementary':
case 'double-complement':
case 'double':
//first basic complement
adjusted = clone( HSV );
adjusted.h = ( adjusted.h + 180 ) % 360;
scheme.push( adjusted );
//then offset
adjusted.h = ( adjusted.h + 30 ) % 360;
secondary = clone( adjusted );
scheme.push( adjusted );
//complement offset
adjusted.h = ( adjusted.h + 180 ) % 360;
scheme.push( secondary );
break;
case 'analogous':
case 'ana':
for ( i = 1; i <= 5; i++ ) {
adjusted = clone( HSV );
adjusted.h = ( adjusted.h + ( 20 * i ) ) % 360;
scheme.push( adjusted );
}
break;
case 'triadic':
case 'triad':
case 'tri':
for ( i = 1; i < 3; i++ ) {
adjusted = clone( HSV );
adjusted.h = ( adjusted.h + ( 120 * i ) ) % 360;
scheme.push( adjusted );
}
break;
default:
console.error( 'Color scheme not recognized.' );
break;
}
convert_to_format( scheme_options.format.toLowerCase(), scheme );
return scheme;
};
//accepts options object returns list or single color
Please.make_color = function( options ){
var color = [],
//clone base please options
color_options = copy_object( make_color_default ),
base_color = null;
if( options !== null ){
//override base Please options
for( var key in options ){
if( options.hasOwnProperty( key )){
color_options[key] = options[key];
}
}
}
var randomiser = null;
if (typeof color_options.seed === 'string') {
randomiser = new RC4Random(color_options.seed);
}
//first, check for a base color
if ( color_options.base_color.length > 0 ) {
//then determine if its a hex string or a named color
if( color_options.base_color.match( /^#?([0-9a-f]{3})([0-9a-f]{3})?$/i ) ){
base_color = Please.HEX_to_HSV( color_options.base_color );
}
else{
base_color = Please.NAME_to_HSV( color_options.base_color );
}
}
for ( var i = 0; i < color_options.colors_returned; i++ ) {
var random_hue = random_int( 0, 360, randomiser ),
hue,
saturation,
value;
if( base_color !== null ){
hue = clamp( random_int( ( base_color.h - 5 ), ( base_color.h + 5 ), randomiser), 0, 360);
//fix for black/gray as a base color returning reds
if( base_color.s === 0 ) {
saturation = 0;
}
else{
saturation = random_float( 0.4, 0.85, randomiser );
}
value = random_float( 0.4, 0.85, randomiser );
color.push({
h: hue,
s: saturation,
v: value
});
}
else{
if( color_options.greyscale === true || color_options.grayscale === true ){
hue = 0;
}
//make hue goldennnnnnnn
else if( color_options.golden === true ){
hue = ( random_hue + ( random_hue / PHI )) % 360;
}
else if( color_options.hue === null || color_options.full_random === true ){
hue = random_hue;
}
else{
hue = clamp( color_options.hue, 0, 360 );
}
//set saturation
if ( color_options.greyscale === true || color_options.grayscale === true ) {
saturation = 0; //if they want greyscale no saturation allowed
}
else if ( color_options.full_random === true ){
saturation = random_float( 0, 1, randomiser );
}
else if ( color_options.saturation === null ){
saturation = 0.4;
}
else{
saturation = clamp( color_options.saturation, 0, 1 );
}
//set value
if( color_options.full_random === true ){
value = random_float( 0, 1, randomiser );
}
else if( color_options.greyscale === true || color_options.grayscale === true ){
value = random_float( 0.15, 0.75, randomiser );
}
else if( color_options.value === null ){
value = 0.75;
}
else{
value = clamp( color_options.value, 0 , 1 );
}
color.push( {h: hue, s: saturation, v: value} );
}
}
//output options based on format
convert_to_format( color_options.format.toLowerCase(), color );
return color;
};
//accepts HSV object returns contrasting color
Please.make_contrast = function( HSV, options ){
//clone base please options
var contrast_options = copy_object( make_contrast_default );
if( options !== null ){
//override base Please options
for( var key in options ){
if( options.hasOwnProperty( key )){
contrast_options[key] = options[key];
}
}
}
var contrast,
value_range = contrast_quotient( HSV ),
adjusted_hue;
if( contrast_options.golden === true ){
adjusted_hue = ( HSV.h * ( 1 + PHI ) ) % 360;
}
else{
var contrast_base =
Please.make_scheme( HSV,
{
scheme_type: 'complementary',
format: 'hsv'
})[1];
adjusted_hue = clamp( ( contrast_base.h - 30 ), 0, 360 );
}
var adjusted_value;
if ( value_range === 'dark' ){
adjusted_value = clamp( ( HSV.v - 0.25 ), 0, 1 );
}
else if ( value_range === 'light' ){
adjusted_value = clamp( ( HSV.v + 0.25 ), 0, 1 );
}
contrast = [{
h: adjusted_hue,
s: HSV.s,
v: adjusted_value
}];
convert_to_format( contrast_options.format.toLowerCase(), contrast );
return contrast[0];
};
return Please;
}
return define_Please();
}));