1 ;(function(root, factory) { |
|
2 |
|
3 // Support AMD |
|
4 if (typeof define === 'function' && define.amd) { |
|
5 define([], factory); |
|
6 |
|
7 // Support CommonJS |
|
8 } else if (typeof exports === 'object') { |
|
9 var randomColor = factory(); |
|
10 |
|
11 // Support NodeJS & Component, which allow module.exports to be a function |
|
12 if (typeof module === 'object' && module && module.exports) { |
|
13 exports = module.exports = randomColor; |
|
14 } |
|
15 |
|
16 // Support CommonJS 1.1.1 spec |
|
17 exports.randomColor = randomColor; |
|
18 |
|
19 // Support vanilla script loading |
|
20 } else { |
|
21 root.randomColor = factory(); |
|
22 }; |
|
23 |
|
24 }(this, function() { |
|
25 |
|
26 // Shared color dictionary |
|
27 var colorDictionary = {}; |
|
28 |
|
29 // Populate the color dictionary |
|
30 loadColorBounds(); |
|
31 |
|
32 var randomColor = function(options) { |
|
33 options = options || {}; |
|
34 |
|
35 var H,S,B; |
|
36 |
|
37 // Check if we need to generate multiple colors |
|
38 if (options.count) { |
|
39 |
|
40 var totalColors = options.count, |
|
41 colors = []; |
|
42 |
|
43 options.count = false; |
|
44 |
|
45 while (totalColors > colors.length) { |
|
46 colors.push(randomColor(options)); |
|
47 } |
|
48 |
|
49 return colors; |
|
50 } |
|
51 |
|
52 // First we pick a hue (H) |
|
53 H = pickHue(options); |
|
54 |
|
55 // Then use H to determine saturation (S) |
|
56 S = pickSaturation(H, options); |
|
57 |
|
58 // Then use S and H to determine brightness (B). |
|
59 B = pickBrightness(H, S, options); |
|
60 |
|
61 // Then we return the HSB color in the desired format |
|
62 return setFormat([H,S,B], options); |
|
63 }; |
|
64 |
|
65 function pickHue (options) { |
|
66 |
|
67 var hueRange = getHueRange(options.hue), |
|
68 hue = randomWithin(hueRange); |
|
69 |
|
70 // Instead of storing red as two seperate ranges, |
|
71 // we group them, using negative numbers |
|
72 if (hue < 0) {hue = 360 + hue} |
|
73 |
|
74 return hue; |
|
75 |
|
76 } |
|
77 |
|
78 function pickSaturation (hue, options) { |
|
79 |
|
80 if (options.luminosity === 'random') { |
|
81 return randomWithin([0,100]); |
|
82 } |
|
83 |
|
84 if (options.hue === 'monochrome') { |
|
85 return 0; |
|
86 } |
|
87 |
|
88 var saturationRange = getSaturationRange(hue); |
|
89 |
|
90 var sMin = saturationRange[0], |
|
91 sMax = saturationRange[1]; |
|
92 |
|
93 switch (options.luminosity) { |
|
94 |
|
95 case 'bright': |
|
96 sMin = 55; |
|
97 break; |
|
98 |
|
99 case 'dark': |
|
100 sMin = sMax - 10; |
|
101 break; |
|
102 |
|
103 case 'light': |
|
104 sMax = 55; |
|
105 break; |
|
106 } |
|
107 |
|
108 return randomWithin([sMin, sMax]); |
|
109 |
|
110 } |
|
111 |
|
112 function pickBrightness (H, S, options) { |
|
113 |
|
114 var brightness, |
|
115 bMin = getMinimumBrightness(H, S), |
|
116 bMax = 100; |
|
117 |
|
118 switch (options.luminosity) { |
|
119 |
|
120 case 'dark': |
|
121 bMax = bMin + 20; |
|
122 break; |
|
123 |
|
124 case 'light': |
|
125 bMin = (bMax + bMin)/2; |
|
126 break; |
|
127 |
|
128 case 'random': |
|
129 bMin = 0; |
|
130 bMax = 100; |
|
131 break; |
|
132 } |
|
133 |
|
134 return randomWithin([bMin, bMax]); |
|
135 |
|
136 } |
|
137 |
|
138 function setFormat (hsv, options) { |
|
139 |
|
140 switch (options.format) { |
|
141 |
|
142 case 'hsvArray': |
|
143 return hsv; |
|
144 |
|
145 case 'hsv': |
|
146 return colorString('hsv', hsv); |
|
147 |
|
148 case 'rgbArray': |
|
149 return HSVtoRGB(hsv); |
|
150 |
|
151 case 'rgb': |
|
152 return colorString('rgb', HSVtoRGB(hsv)); |
|
153 |
|
154 default: |
|
155 return HSVtoHex(hsv); |
|
156 } |
|
157 |
|
158 } |
|
159 |
|
160 function getMinimumBrightness(H, S) { |
|
161 |
|
162 var lowerBounds = getColorInfo(H).lowerBounds; |
|
163 |
|
164 for (var i = 0; i < lowerBounds.length - 1; i++) { |
|
165 |
|
166 var s1 = lowerBounds[i][0], |
|
167 v1 = lowerBounds[i][1]; |
|
168 |
|
169 var s2 = lowerBounds[i+1][0], |
|
170 v2 = lowerBounds[i+1][1]; |
|
171 |
|
172 if (S >= s1 && S <= s2) { |
|
173 |
|
174 var m = (v2 - v1)/(s2 - s1), |
|
175 b = v1 - m*s1; |
|
176 |
|
177 return m*S + b; |
|
178 } |
|
179 |
|
180 } |
|
181 |
|
182 return 0; |
|
183 } |
|
184 |
|
185 function getHueRange (colorInput) { |
|
186 |
|
187 if (typeof parseInt(colorInput) === 'number') { |
|
188 |
|
189 var number = parseInt(colorInput); |
|
190 |
|
191 if (number < 360 && number > 0) { |
|
192 return [number, number]; |
|
193 } |
|
194 |
|
195 } |
|
196 |
|
197 if (typeof colorInput === 'string') { |
|
198 |
|
199 if (colorDictionary[colorInput]) { |
|
200 var color = colorDictionary[colorInput]; |
|
201 if (color.hueRange) {return color.hueRange} |
|
202 } |
|
203 } |
|
204 |
|
205 return [0,360]; |
|
206 |
|
207 } |
|
208 |
|
209 function getSaturationRange (hue) { |
|
210 return getColorInfo(hue).saturationRange; |
|
211 } |
|
212 |
|
213 function getColorInfo (hue) { |
|
214 |
|
215 // Maps red colors to make picking hue easier |
|
216 if (hue >= 334 && hue <= 360) { |
|
217 hue-= 360; |
|
218 } |
|
219 |
|
220 for (var colorName in colorDictionary) { |
|
221 var color = colorDictionary[colorName]; |
|
222 if (color.hueRange && |
|
223 hue >= color.hueRange[0] && |
|
224 hue <= color.hueRange[1]) { |
|
225 return colorDictionary[colorName]; |
|
226 } |
|
227 } return 'Color not found'; |
|
228 } |
|
229 |
|
230 function randomWithin (range) { |
|
231 return Math.floor(range[0] + Math.random()*(range[1] + 1 - range[0])); |
|
232 } |
|
233 |
|
234 function shiftHue (h, degrees) { |
|
235 return (h + degrees)%360; |
|
236 } |
|
237 |
|
238 function HSVtoHex (hsv){ |
|
239 |
|
240 var rgb = HSVtoRGB(hsv); |
|
241 |
|
242 function componentToHex(c) { |
|
243 var hex = c.toString(16); |
|
244 return hex.length == 1 ? "0" + hex : hex; |
|
245 } |
|
246 |
|
247 var hex = "#" + componentToHex(rgb[0]) + componentToHex(rgb[1]) + componentToHex(rgb[2]); |
|
248 |
|
249 return hex; |
|
250 |
|
251 } |
|
252 |
|
253 function defineColor (name, hueRange, lowerBounds) { |
|
254 |
|
255 var sMin = lowerBounds[0][0], |
|
256 sMax = lowerBounds[lowerBounds.length - 1][0], |
|
257 |
|
258 bMin = lowerBounds[lowerBounds.length - 1][1], |
|
259 bMax = lowerBounds[0][1]; |
|
260 |
|
261 colorDictionary[name] = { |
|
262 hueRange: hueRange, |
|
263 lowerBounds: lowerBounds, |
|
264 saturationRange: [sMin, sMax], |
|
265 brightnessRange: [bMin, bMax] |
|
266 }; |
|
267 |
|
268 } |
|
269 |
|
270 function loadColorBounds () { |
|
271 |
|
272 defineColor( |
|
273 'monochrome', |
|
274 null, |
|
275 [[0,0],[100,0]] |
|
276 ); |
|
277 |
|
278 defineColor( |
|
279 'red', |
|
280 [-26,18], |
|
281 [[20,100],[30,92],[40,89],[50,85],[60,78],[70,70],[80,60],[90,55],[100,50]] |
|
282 ); |
|
283 |
|
284 defineColor( |
|
285 'orange', |
|
286 [19,46], |
|
287 [[20,100],[30,93],[40,88],[50,86],[60,85],[70,70],[100,70]] |
|
288 ); |
|
289 |
|
290 defineColor( |
|
291 'yellow', |
|
292 [47,62], |
|
293 [[25,100],[40,94],[50,89],[60,86],[70,84],[80,82],[90,80],[100,75]] |
|
294 ); |
|
295 |
|
296 defineColor( |
|
297 'green', |
|
298 [63,158], |
|
299 [[30,100],[40,90],[50,85],[60,81],[70,74],[80,64],[90,50],[100,40]] |
|
300 ); |
|
301 |
|
302 defineColor( |
|
303 'blue', |
|
304 [159, 257], |
|
305 [[20,100],[30,86],[40,80],[50,74],[60,60],[70,52],[80,44],[90,39],[100,35]] |
|
306 ); |
|
307 |
|
308 defineColor( |
|
309 'purple', |
|
310 [258, 282], |
|
311 [[20,100],[30,87],[40,79],[50,70],[60,65],[70,59],[80,52],[90,45],[100,42]] |
|
312 ); |
|
313 |
|
314 defineColor( |
|
315 'pink', |
|
316 [283, 334], |
|
317 [[20,100],[30,90],[40,86],[60,84],[80,80],[90,75],[100,73]] |
|
318 ); |
|
319 |
|
320 } |
|
321 |
|
322 function HSVtoRGB (hsv) { |
|
323 |
|
324 // this doesn't work for the values of 0 and 360 |
|
325 // here's the hacky fix |
|
326 var h = hsv[0]; |
|
327 if (h === 0) {h = 1} |
|
328 if (h === 360) {h = 359} |
|
329 |
|
330 // Rebase the h,s,v values |
|
331 h = h/360; |
|
332 var s = hsv[1]/100, |
|
333 v = hsv[2]/100; |
|
334 |
|
335 var h_i = Math.floor(h*6), |
|
336 f = h * 6 - h_i, |
|
337 p = v * (1 - s), |
|
338 q = v * (1 - f*s), |
|
339 t = v * (1 - (1 - f)*s), |
|
340 r = 256, |
|
341 g = 256, |
|
342 b = 256; |
|
343 |
|
344 switch(h_i) { |
|
345 case 0: r = v, g = t, b = p; break; |
|
346 case 1: r = q, g = v, b = p; break; |
|
347 case 2: r = p, g = v, b = t; break; |
|
348 case 3: r = p, g = q, b = v; break; |
|
349 case 4: r = t, g = p, b = v; break; |
|
350 case 5: r = v, g = p, b = q; break; |
|
351 } |
|
352 var result = [Math.floor(r*255), Math.floor(g*255), Math.floor(b*255)]; |
|
353 return result; |
|
354 } |
|
355 |
|
356 function colorString (prefix, values) { |
|
357 return prefix + '(' + values.join(', ') + ')'; |
|
358 } |
|
359 |
|
360 return randomColor; |
|
361 })); |
|