@ctrl/tinycolor
Version:
Fast, small color manipulation and conversion for JavaScript
280 lines (279 loc) • 7.44 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.bounds = exports.random = void 0;
// randomColor by David Merfield under the CC0 license
// https://github.com/davidmerfield/randomColor/
const index_js_1 = require("./index.js");
function random(options = {}) {
// Check if we need to generate multiple colors
if (options.count !== undefined &&
options.count !== null) {
const totalColors = options.count;
const colors = [];
options.count = undefined;
while (totalColors > colors.length) {
// Since we're generating multiple colors,
// incremement the seed. Otherwise we'd just
// generate the same color each time...
options.count = null;
if (options.seed) {
options.seed += 1;
}
colors.push(random(options));
}
options.count = totalColors;
return colors;
}
// First we pick a hue (H)
const h = pickHue(options.hue, options.seed);
// Then use H to determine saturation (S)
const s = pickSaturation(h, options);
// Then use S and H to determine brightness (B).
const v = pickBrightness(h, s, options);
const res = { h, s, v };
if (options.alpha !== undefined) {
res.a = options.alpha;
}
// Then we return the HSB color in the desired format
return new index_js_1.TinyColor(res);
}
exports.random = random;
function pickHue(hue, seed) {
const hueRange = getHueRange(hue);
let res = randomWithin(hueRange, seed);
// Instead of storing red as two seperate ranges,
// we group them, using negative numbers
if (res < 0) {
res = 360 + res;
}
return res;
}
function pickSaturation(hue, options) {
if (options.hue === 'monochrome') {
return 0;
}
if (options.luminosity === 'random') {
return randomWithin([0, 100], options.seed);
}
const { saturationRange } = getColorInfo(hue);
let sMin = saturationRange[0];
let sMax = saturationRange[1];
switch (options.luminosity) {
case 'bright':
sMin = 55;
break;
case 'dark':
sMin = sMax - 10;
break;
case 'light':
sMax = 55;
break;
default:
break;
}
return randomWithin([sMin, sMax], options.seed);
}
function pickBrightness(H, S, options) {
let bMin = getMinimumBrightness(H, S);
let bMax = 100;
switch (options.luminosity) {
case 'dark':
bMax = bMin + 20;
break;
case 'light':
bMin = (bMax + bMin) / 2;
break;
case 'random':
bMin = 0;
bMax = 100;
break;
default:
break;
}
return randomWithin([bMin, bMax], options.seed);
}
function getMinimumBrightness(H, S) {
const { lowerBounds } = getColorInfo(H);
for (let i = 0; i < lowerBounds.length - 1; i++) {
const s1 = lowerBounds[i][0];
const v1 = lowerBounds[i][1];
const s2 = lowerBounds[i + 1][0];
const v2 = lowerBounds[i + 1][1];
if (S >= s1 && S <= s2) {
const m = (v2 - v1) / (s2 - s1);
const b = v1 - m * s1;
return m * S + b;
}
}
return 0;
}
function getHueRange(colorInput) {
const num = parseInt(colorInput, 10);
if (!Number.isNaN(num) && num < 360 && num > 0) {
return [num, num];
}
if (typeof colorInput === 'string') {
const namedColor = exports.bounds.find(n => n.name === colorInput);
if (namedColor) {
const color = defineColor(namedColor);
if (color.hueRange) {
return color.hueRange;
}
}
const parsed = new index_js_1.TinyColor(colorInput);
if (parsed.isValid) {
const hue = parsed.toHsv().h;
return [hue, hue];
}
}
return [0, 360];
}
function getColorInfo(hue) {
// Maps red colors to make picking hue easier
if (hue >= 334 && hue <= 360) {
hue -= 360;
}
for (const bound of exports.bounds) {
const color = defineColor(bound);
if (color.hueRange && hue >= color.hueRange[0] && hue <= color.hueRange[1]) {
return color;
}
}
throw Error('Color not found');
}
function randomWithin(range, seed) {
if (seed === undefined) {
return Math.floor(range[0] + Math.random() * (range[1] + 1 - range[0]));
}
// Seeded random algorithm from http://indiegamr.com/generate-repeatable-random-numbers-in-js/
const max = range[1] || 1;
const min = range[0] || 0;
seed = (seed * 9301 + 49297) % 233280;
const rnd = seed / 233280.0;
return Math.floor(min + rnd * (max - min));
}
function defineColor(bound) {
const sMin = bound.lowerBounds[0][0];
const sMax = bound.lowerBounds[bound.lowerBounds.length - 1][0];
const bMin = bound.lowerBounds[bound.lowerBounds.length - 1][1];
const bMax = bound.lowerBounds[0][1];
return {
name: bound.name,
hueRange: bound.hueRange,
lowerBounds: bound.lowerBounds,
saturationRange: [sMin, sMax],
brightnessRange: [bMin, bMax],
};
}
/**
* @hidden
*/
exports.bounds = [
{
name: 'monochrome',
hueRange: null,
lowerBounds: [
[ ],
[ ],
],
},
{
name: 'red',
hueRange: [-26, 18],
lowerBounds: [
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
],
},
{
name: 'orange',
hueRange: [19, 46],
lowerBounds: [
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
],
},
{
name: 'yellow',
hueRange: [47, 62],
lowerBounds: [
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
],
},
{
name: 'green',
hueRange: [63, 178],
lowerBounds: [
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
],
},
{
name: 'blue',
hueRange: [179, 257],
lowerBounds: [
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
],
},
{
name: 'purple',
hueRange: [258, 282],
lowerBounds: [
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
],
},
{
name: 'pink',
hueRange: [283, 334],
lowerBounds: [
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
[ ],
],
},
];