randomColor.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. // randomColor by David Merfield under the CC0 license
  2. // https://github.com/davidmerfield/randomColor/
  3. ;(function(root, factory) {
  4. // Support CommonJS
  5. if (typeof exports === 'object') {
  6. var randomColor = factory();
  7. // Support NodeJS & Component, which allow module.exports to be a function
  8. if (typeof module === 'object' && module && module.exports) {
  9. exports = module.exports = randomColor;
  10. }
  11. // Support CommonJS 1.1.1 spec
  12. exports.randomColor = randomColor;
  13. // Support AMD
  14. } else if (typeof define === 'function' && define.amd) {
  15. define([], factory);
  16. // Support vanilla script loading
  17. } else {
  18. root.randomColor = factory();
  19. }
  20. }(this, function() {
  21. // Seed to get repeatable colors
  22. var seed = null;
  23. // Shared color dictionary
  24. var colorDictionary = {};
  25. // Populate the color dictionary
  26. loadColorBounds();
  27. // check if a range is taken
  28. var colorRanges = [];
  29. var randomColor = function (options) {
  30. options = options || {};
  31. // Check if there is a seed and ensure it's an
  32. // integer. Otherwise, reset the seed value.
  33. if (options.seed !== undefined && options.seed !== null && options.seed === parseInt(options.seed, 10)) {
  34. seed = options.seed;
  35. // A string was passed as a seed
  36. } else if (typeof options.seed === 'string') {
  37. seed = stringToInteger(options.seed);
  38. // Something was passed as a seed but it wasn't an integer or string
  39. } else if (options.seed !== undefined && options.seed !== null) {
  40. throw new TypeError('The seed value must be an integer or string');
  41. // No seed, reset the value outside.
  42. } else {
  43. seed = null;
  44. }
  45. var H,S,B;
  46. // Check if we need to generate multiple colors
  47. if (options.count !== null && options.count !== undefined) {
  48. var totalColors = options.count,
  49. colors = [];
  50. // Value false at index i means the range i is not taken yet.
  51. for (var i = 0; i < options.count; i++) {
  52. colorRanges.push(false)
  53. }
  54. options.count = null;
  55. while (totalColors > colors.length) {
  56. var color = randomColor(options);
  57. if (seed !== null) {
  58. options.seed = seed;
  59. }
  60. colors.push(color);
  61. }
  62. options.count = totalColors;
  63. return colors;
  64. }
  65. // First we pick a hue (H)
  66. H = pickHue(options);
  67. // Then use H to determine saturation (S)
  68. S = pickSaturation(H, options);
  69. // Then use S and H to determine brightness (B).
  70. B = pickBrightness(H, S, options);
  71. // Then we return the HSB color in the desired format
  72. return setFormat([H,S,B], options);
  73. };
  74. function pickHue(options) {
  75. if (colorRanges.length > 0) {
  76. var hueRange = getRealHueRange(options.hue)
  77. var hue = randomWithin(hueRange)
  78. //Each of colorRanges.length ranges has a length equal approximatelly one step
  79. var step = (hueRange[1] - hueRange[0]) / colorRanges.length
  80. var j = parseInt((hue - hueRange[0]) / step)
  81. //Check if the range j is taken
  82. if (colorRanges[j] === true) {
  83. j = (j + 2) % colorRanges.length
  84. }
  85. else {
  86. colorRanges[j] = true
  87. }
  88. var min = (hueRange[0] + j * step) % 359,
  89. max = (hueRange[0] + (j + 1) * step) % 359;
  90. hueRange = [min, max]
  91. hue = randomWithin(hueRange)
  92. if (hue < 0) {hue = 360 + hue;}
  93. return hue
  94. }
  95. else {
  96. var hueRange = getHueRange(options.hue)
  97. hue = randomWithin(hueRange);
  98. // Instead of storing red as two seperate ranges,
  99. // we group them, using negative numbers
  100. if (hue < 0) {
  101. hue = 360 + hue;
  102. }
  103. return hue;
  104. }
  105. }
  106. function pickSaturation (hue, options) {
  107. if (options.hue === 'monochrome') {
  108. return 0;
  109. }
  110. if (options.luminosity === 'random') {
  111. return randomWithin([0,100]);
  112. }
  113. var saturationRange = getSaturationRange(hue);
  114. var sMin = saturationRange[0],
  115. sMax = saturationRange[1];
  116. switch (options.luminosity) {
  117. case 'bright':
  118. sMin = 55;
  119. break;
  120. case 'dark':
  121. sMin = sMax - 10;
  122. break;
  123. case 'light':
  124. sMax = 55;
  125. break;
  126. }
  127. return randomWithin([sMin, sMax]);
  128. }
  129. function pickBrightness (H, S, options) {
  130. var bMin = getMinimumBrightness(H, S),
  131. bMax = 100;
  132. switch (options.luminosity) {
  133. case 'dark':
  134. bMax = bMin + 20;
  135. break;
  136. case 'light':
  137. bMin = (bMax + bMin)/2;
  138. break;
  139. case 'random':
  140. bMin = 0;
  141. bMax = 100;
  142. break;
  143. }
  144. return randomWithin([bMin, bMax]);
  145. }
  146. function setFormat (hsv, options) {
  147. switch (options.format) {
  148. case 'hsvArray':
  149. return hsv;
  150. case 'hslArray':
  151. return HSVtoHSL(hsv);
  152. case 'hsl':
  153. var hsl = HSVtoHSL(hsv);
  154. return 'hsl('+hsl[0]+', '+hsl[1]+'%, '+hsl[2]+'%)';
  155. case 'hsla':
  156. var hslColor = HSVtoHSL(hsv);
  157. var alpha = options.alpha || Math.random();
  158. return 'hsla('+hslColor[0]+', '+hslColor[1]+'%, '+hslColor[2]+'%, ' + alpha + ')';
  159. case 'rgbArray':
  160. return HSVtoRGB(hsv);
  161. case 'rgb':
  162. var rgb = HSVtoRGB(hsv);
  163. return 'rgb(' + rgb.join(', ') + ')';
  164. case 'rgba':
  165. var rgbColor = HSVtoRGB(hsv);
  166. var alpha = options.alpha || Math.random();
  167. return 'rgba(' + rgbColor.join(', ') + ', ' + alpha + ')';
  168. default:
  169. return HSVtoHex(hsv);
  170. }
  171. }
  172. function getMinimumBrightness(H, S) {
  173. var lowerBounds = getColorInfo(H).lowerBounds;
  174. for (var i = 0; i < lowerBounds.length - 1; i++) {
  175. var s1 = lowerBounds[i][0],
  176. v1 = lowerBounds[i][1];
  177. var s2 = lowerBounds[i+1][0],
  178. v2 = lowerBounds[i+1][1];
  179. if (S >= s1 && S <= s2) {
  180. var m = (v2 - v1)/(s2 - s1),
  181. b = v1 - m*s1;
  182. return m*S + b;
  183. }
  184. }
  185. return 0;
  186. }
  187. function getHueRange (colorInput) {
  188. if (typeof parseInt(colorInput) === 'number') {
  189. var number = parseInt(colorInput);
  190. if (number < 360 && number > 0) {
  191. return [number, number];
  192. }
  193. }
  194. if (typeof colorInput === 'string') {
  195. if (colorDictionary[colorInput]) {
  196. var color = colorDictionary[colorInput];
  197. if (color.hueRange) {return color.hueRange;}
  198. } else if (colorInput.match(/^#?([0-9A-F]{3}|[0-9A-F]{6})$/i)) {
  199. var hue = HexToHSB(colorInput)[0];
  200. return [ hue, hue ];
  201. }
  202. }
  203. return [0,360];
  204. }
  205. function getSaturationRange (hue) {
  206. return getColorInfo(hue).saturationRange;
  207. }
  208. function getColorInfo (hue) {
  209. // Maps red colors to make picking hue easier
  210. if (hue >= 334 && hue <= 360) {
  211. hue-= 360;
  212. }
  213. for (var colorName in colorDictionary) {
  214. var color = colorDictionary[colorName];
  215. if (color.hueRange &&
  216. hue >= color.hueRange[0] &&
  217. hue <= color.hueRange[1]) {
  218. return colorDictionary[colorName];
  219. }
  220. } return 'Color not found';
  221. }
  222. function randomWithin (range) {
  223. if (seed === null) {
  224. //generate random evenly destinct number from : https://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/
  225. var golden_ratio = 0.618033988749895
  226. var r=Math.random()
  227. r += golden_ratio
  228. r %= 1
  229. return Math.floor(range[0] + r*(range[1] + 1 - range[0]));
  230. } else {
  231. //Seeded random algorithm from http://indiegamr.com/generate-repeatable-random-numbers-in-js/
  232. var max = range[1] || 1;
  233. var min = range[0] || 0;
  234. seed = (seed * 9301 + 49297) % 233280;
  235. var rnd = seed / 233280.0;
  236. return Math.floor(min + rnd * (max - min));
  237. }
  238. }
  239. function HSVtoHex (hsv){
  240. var rgb = HSVtoRGB(hsv);
  241. function componentToHex(c) {
  242. var hex = c.toString(16);
  243. return hex.length == 1 ? '0' + hex : hex;
  244. }
  245. var hex = '#' + componentToHex(rgb[0]) + componentToHex(rgb[1]) + componentToHex(rgb[2]);
  246. return hex;
  247. }
  248. function defineColor (name, hueRange, lowerBounds) {
  249. var sMin = lowerBounds[0][0],
  250. sMax = lowerBounds[lowerBounds.length - 1][0],
  251. bMin = lowerBounds[lowerBounds.length - 1][1],
  252. bMax = lowerBounds[0][1];
  253. colorDictionary[name] = {
  254. hueRange: hueRange,
  255. lowerBounds: lowerBounds,
  256. saturationRange: [sMin, sMax],
  257. brightnessRange: [bMin, bMax]
  258. };
  259. }
  260. function loadColorBounds () {
  261. defineColor(
  262. 'monochrome',
  263. null,
  264. [[0,0],[100,0]]
  265. );
  266. defineColor(
  267. 'red',
  268. [-26,18],
  269. [[20,100],[30,92],[40,89],[50,85],[60,78],[70,70],[80,60],[90,55],[100,50]]
  270. );
  271. defineColor(
  272. 'orange',
  273. [18,46],
  274. [[20,100],[30,93],[40,88],[50,86],[60,85],[70,70],[100,70]]
  275. );
  276. defineColor(
  277. 'yellow',
  278. [46,62],
  279. [[25,100],[40,94],[50,89],[60,86],[70,84],[80,82],[90,80],[100,75]]
  280. );
  281. defineColor(
  282. 'green',
  283. [62,178],
  284. [[30,100],[40,90],[50,85],[60,81],[70,74],[80,64],[90,50],[100,40]]
  285. );
  286. defineColor(
  287. 'blue',
  288. [178, 257],
  289. [[20,100],[30,86],[40,80],[50,74],[60,60],[70,52],[80,44],[90,39],[100,35]]
  290. );
  291. defineColor(
  292. 'purple',
  293. [257, 282],
  294. [[20,100],[30,87],[40,79],[50,70],[60,65],[70,59],[80,52],[90,45],[100,42]]
  295. );
  296. defineColor(
  297. 'pink',
  298. [282, 334],
  299. [[20,100],[30,90],[40,86],[60,84],[80,80],[90,75],[100,73]]
  300. );
  301. }
  302. function HSVtoRGB (hsv) {
  303. // this doesn't work for the values of 0 and 360
  304. // here's the hacky fix
  305. var h = hsv[0];
  306. if (h === 0) {h = 1;}
  307. if (h === 360) {h = 359;}
  308. // Rebase the h,s,v values
  309. h = h/360;
  310. var s = hsv[1]/100,
  311. v = hsv[2]/100;
  312. var h_i = Math.floor(h*6),
  313. f = h * 6 - h_i,
  314. p = v * (1 - s),
  315. q = v * (1 - f*s),
  316. t = v * (1 - (1 - f)*s),
  317. r = 256,
  318. g = 256,
  319. b = 256;
  320. switch(h_i) {
  321. case 0: r = v; g = t; b = p; break;
  322. case 1: r = q; g = v; b = p; break;
  323. case 2: r = p; g = v; b = t; break;
  324. case 3: r = p; g = q; b = v; break;
  325. case 4: r = t; g = p; b = v; break;
  326. case 5: r = v; g = p; b = q; break;
  327. }
  328. var result = [Math.floor(r*255), Math.floor(g*255), Math.floor(b*255)];
  329. return result;
  330. }
  331. function HexToHSB (hex) {
  332. hex = hex.replace(/^#/, '');
  333. hex = hex.length === 3 ? hex.replace(/(.)/g, '$1$1') : hex;
  334. var red = parseInt(hex.substr(0, 2), 16) / 255,
  335. green = parseInt(hex.substr(2, 2), 16) / 255,
  336. blue = parseInt(hex.substr(4, 2), 16) / 255;
  337. var cMax = Math.max(red, green, blue),
  338. delta = cMax - Math.min(red, green, blue),
  339. saturation = cMax ? (delta / cMax) : 0;
  340. switch (cMax) {
  341. case red: return [ 60 * (((green - blue) / delta) % 6) || 0, saturation, cMax ];
  342. case green: return [ 60 * (((blue - red) / delta) + 2) || 0, saturation, cMax ];
  343. case blue: return [ 60 * (((red - green) / delta) + 4) || 0, saturation, cMax ];
  344. }
  345. }
  346. function HSVtoHSL (hsv) {
  347. var h = hsv[0],
  348. s = hsv[1]/100,
  349. v = hsv[2]/100,
  350. k = (2-s)*v;
  351. return [
  352. h,
  353. Math.round(s*v / (k<1 ? k : 2-k) * 10000) / 100,
  354. k/2 * 100
  355. ];
  356. }
  357. function stringToInteger (string) {
  358. var total = 0
  359. for (var i = 0; i !== string.length; i++) {
  360. if (total >= Number.MAX_SAFE_INTEGER) break;
  361. total += string.charCodeAt(i)
  362. }
  363. return total
  364. }
  365. // get The range of given hue when options.count!=0
  366. function getRealHueRange(colorHue)
  367. { if (!isNaN(colorHue)) {
  368. var number = parseInt(colorHue);
  369. if (number < 360 && number > 0) {
  370. return getColorInfo(colorHue).hueRange
  371. }
  372. }
  373. else if (typeof colorHue === 'string') {
  374. if (colorDictionary[colorHue]) {
  375. var color = colorDictionary[colorHue];
  376. if (color.hueRange) {
  377. return color.hueRange
  378. }
  379. } else if (colorHue.match(/^#?([0-9A-F]{3}|[0-9A-F]{6})$/i)) {
  380. var hue = HexToHSB(colorHue)[0]
  381. return getColorInfo(hue).hueRange
  382. }
  383. }
  384. return [0,360]
  385. }
  386. return randomColor;
  387. }));