index.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. /**
  2. * Colorz (or Colz) is a Javascript "library" to help
  3. * in color conversion between the usual color-spaces
  4. * Hex - Rgb - Hsl / Hsv - Hsb
  5. *
  6. * It provides some helpers to output Canvas / CSS
  7. * color strings.
  8. *
  9. * by Carlos Cabo 2013
  10. * http://carloscabo.com
  11. *
  12. * Some formulas borrowed from Wikipedia or other authors.
  13. */
  14. const round = Math.round
  15. /*
  16. ==================================
  17. Color constructors
  18. ==================================
  19. */
  20. export class Rgb {
  21. constructor (col) {
  22. this.r = col[0]
  23. this.g = col[1]
  24. this.b = col[2]
  25. }
  26. toString () {
  27. return `rgb(${this.r},${this.g},${this.b})`
  28. }
  29. }
  30. export class Rgba extends Rgb {
  31. constructor (col) {
  32. super(col)
  33. this.a = col[3]
  34. }
  35. toString () {
  36. return `rgba(${this.r},${this.g},${this.b},${this.a})`
  37. }
  38. }
  39. export class Hsl {
  40. constructor (col) {
  41. this.h = col[0]
  42. this.s = col[1]
  43. this.l = col[2]
  44. }
  45. toString () {
  46. return `hsl(${this.h},${this.s}%,${this.l}%)`
  47. }
  48. }
  49. export class Hsla extends Hsl {
  50. constructor (col) {
  51. super(col)
  52. this.a = col[3]
  53. }
  54. toString () {
  55. return `hsla(${this.h},${this.s}%,${this.l}%,${this.a})`
  56. }
  57. }
  58. /*
  59. ==================================
  60. Main Colz color object
  61. ==================================
  62. */
  63. export class Color {
  64. constructor (r, g, b, a = 1.0) {
  65. // If args are not given in (r, g, b, [a]) format, convert
  66. if (typeof r === 'string') {
  67. let str = r
  68. // Add initial '#' if missing
  69. if (str.charAt(0) !== '#') { str = '#' + str }
  70. // If Hex in #fff format convert to #ffffff
  71. if (str.length < 7) {
  72. str = '#' + str[1] + str[1] + str[2] + str[2] + str[3] + str[3]
  73. }
  74. ([r, g, b] = hexToRgb(str))
  75. } else if (r instanceof Array) {
  76. a = r[3] || a
  77. b = r[2]
  78. g = r[1]
  79. r = r[0]
  80. }
  81. this.r = r
  82. this.g = g
  83. this.b = b
  84. this.a = a
  85. this.rgb = new Rgb([this.r, this.g, this.b])
  86. this.rgba = new Rgba([this.r, this.g, this.b, this.a])
  87. this.hex = rgbToHex(this.r, this.g, this.b)
  88. this.hsl = new Hsl(rgbToHsl(this.r, this.g, this.b))
  89. this.h = this.hsl.h
  90. this.s = this.hsl.s
  91. this.l = this.hsl.l
  92. this.hsla = new Hsla([this.h, this.s, this.l, this.a])
  93. }
  94. setHue (newHue) {
  95. this.h = newHue
  96. this.hsl.h = newHue
  97. this.hsla.h = newHue
  98. this.updateFromHsl()
  99. }
  100. setSat (newSat) {
  101. this.s = newSat
  102. this.hsl.s = newSat
  103. this.hsla.s = newSat
  104. this.updateFromHsl()
  105. }
  106. setLum (newLum) {
  107. this.l = newLum
  108. this.hsl.l = newLum
  109. this.hsla.l = newLum
  110. this.updateFromHsl()
  111. }
  112. setAlpha (newAlpha) {
  113. this.a = newAlpha
  114. this.hsla.a = newAlpha
  115. this.rgba.a = newAlpha
  116. }
  117. updateFromHsl () {
  118. // Updates Rgb
  119. this.rgb = null
  120. this.rgb = new Rgb(hslToRgb(this.h, this.s, this.l))
  121. this.r = this.rgb.r
  122. this.g = this.rgb.g
  123. this.b = this.rgb.b
  124. this.rgba.r = this.rgb.r
  125. this.rgba.g = this.rgb.g
  126. this.rgba.b = this.rgb.b
  127. // Updates Hex
  128. this.hex = null
  129. this.hex = rgbToHex([this.r, this.g, this.b])
  130. }
  131. }
  132. /*
  133. ==================================
  134. Public Methods
  135. ==================================
  136. */
  137. export const randomColor = function () {
  138. const r = '#' + Math.random().toString(16).slice(2, 8)
  139. return new Color(r)
  140. }
  141. export const hexToRgb = function (hex) {
  142. const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
  143. return result ? [
  144. parseInt(result[1], 16),
  145. parseInt(result[2], 16),
  146. parseInt(result[3], 16)
  147. ] : null
  148. }
  149. export const componentToHex = function (c) {
  150. const hex = c.toString(16)
  151. return hex.length === 1 ? '0' + hex : hex
  152. }
  153. // You can pass 3 numeric values or 1 Array
  154. export const rgbToHex = function (r, g, b) {
  155. if (r instanceof Array) {
  156. b = r[2]
  157. g = r[1]
  158. r = r[0]
  159. }
  160. return '#' + componentToHex(r) + componentToHex(g) + componentToHex(b)
  161. }
  162. /**
  163. * Converts an RGB color value to HSL. Conversion formula
  164. * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
  165. *
  166. * @param {Number} r The red color value
  167. * @param {Number} g The green color value
  168. * @param {Number} b The blue color value
  169. * @return {Array} The HSL representation
  170. */
  171. export const rgbToHsl = function (r, g, b) {
  172. if (r instanceof Array) {
  173. b = r[2]
  174. g = r[1]
  175. r = r[0]
  176. }
  177. let h, s, l, d, max, min
  178. r /= 255
  179. g /= 255
  180. b /= 255
  181. max = Math.max(r, g, b)
  182. min = Math.min(r, g, b)
  183. l = (max + min) / 2
  184. if (max === min) {
  185. h = s = 0 // achromatic
  186. } else {
  187. d = max - min
  188. s = l > 0.5 ? d / (2 - max - min) : d / (max + min)
  189. switch (max) {
  190. case r:
  191. h = (g - b) / d + (g < b ? 6 : 0)
  192. break
  193. case g:
  194. h = (b - r) / d + 2
  195. break
  196. case b:
  197. h = (r - g) / d + 4
  198. break
  199. }
  200. h /= 6
  201. }
  202. // CARLOS
  203. h = round(h * 360)
  204. s = round(s * 100)
  205. l = round(l * 100)
  206. return [h, s, l]
  207. }
  208. export const hue2rgb = function (p, q, t) {
  209. if (t < 0) { t += 1 }
  210. if (t > 1) { t -= 1 }
  211. if (t < 1 / 6) { return p + (q - p) * 6 * t }
  212. if (t < 1 / 2) { return q }
  213. if (t < 2 / 3) { return p + (q - p) * (2 / 3 - t) * 6 }
  214. return p
  215. }
  216. /**
  217. * Converts an HSL color value to RGB. Conversion formula
  218. * adapted from http://en.wikipedia.org/wiki/HSL_color_space.
  219. *
  220. * @param {Number} h The hue
  221. * @param {Number} s The saturation
  222. * @param {Number} l The lightness
  223. * @return {Array} The RGB representation
  224. */
  225. export const hslToRgb = function (h, s, l) {
  226. if (h instanceof Array) {
  227. l = h[2]
  228. s = h[1]
  229. h = h[0]
  230. }
  231. h = h / 360
  232. s = s / 100
  233. l = l / 100
  234. let r, g, b, q, p
  235. if (s === 0) {
  236. r = g = b = l // achromatic
  237. } else {
  238. q = l < 0.5 ? l * (1 + s) : l + s - l * s
  239. p = 2 * l - q
  240. r = hue2rgb(p, q, h + 1 / 3)
  241. g = hue2rgb(p, q, h)
  242. b = hue2rgb(p, q, h - 1 / 3)
  243. }
  244. return [round(r * 255), round(g * 255), round(b * 255)]
  245. }
  246. /**
  247. * Converts an RGB color value to HSB / HSV. Conversion formula
  248. * adapted from http://en.wikipedia.org/wiki/HSV_color_space.
  249. *
  250. * @param {Number} r The red color value
  251. * @param {Number} g The green color value
  252. * @param {Number} b The blue color value
  253. * @return {Array} The HSB representation
  254. */
  255. export const rgbToHsb = function (r, g, b) {
  256. let max, min, h, s, v, d
  257. r = r / 255
  258. g = g / 255
  259. b = b / 255
  260. max = Math.max(r, g, b)
  261. min = Math.min(r, g, b)
  262. v = max
  263. d = max - min
  264. s = max === 0 ? 0 : d / max
  265. if (max === min) {
  266. h = 0 // achromatic
  267. } else {
  268. switch (max) {
  269. case r:
  270. h = (g - b) / d + (g < b ? 6 : 0)
  271. break
  272. case g:
  273. h = (b - r) / d + 2
  274. break
  275. case b:
  276. h = (r - g) / d + 4
  277. break
  278. }
  279. h /= 6
  280. }
  281. // map top 360,100,100
  282. h = round(h * 360)
  283. s = round(s * 100)
  284. v = round(v * 100)
  285. return [h, s, v]
  286. }
  287. /**
  288. * Converts an HSB / HSV color value to RGB. Conversion formula
  289. * adapted from http://en.wikipedia.org/wiki/HSV_color_space.
  290. *
  291. * @param {Number} h The hue
  292. * @param {Number} s The saturation
  293. * @param {Number} v The value
  294. * @return {Array} The RGB representation
  295. */
  296. export const hsbToRgb = function (h, s, v) {
  297. let r, g, b, i, f, p, q, t
  298. // h = h / 360;
  299. if (v === 0) { return [0, 0, 0] }
  300. s = s / 100
  301. v = v / 100
  302. h = h / 60
  303. i = Math.floor(h)
  304. f = h - i
  305. p = v * (1 - s)
  306. q = v * (1 - (s * f))
  307. t = v * (1 - (s * (1 - f)))
  308. if (i === 0) {
  309. r = v
  310. g = t
  311. b = p
  312. } else if (i === 1) {
  313. r = q
  314. g = v
  315. b = p
  316. } else if (i === 2) {
  317. r = p
  318. g = v
  319. b = t
  320. } else if (i === 3) {
  321. r = p
  322. g = q
  323. b = v
  324. } else if (i === 4) {
  325. r = t
  326. g = p
  327. b = v
  328. } else if (i === 5) {
  329. r = v
  330. g = p
  331. b = q
  332. }
  333. r = Math.floor(r * 255)
  334. g = Math.floor(g * 255)
  335. b = Math.floor(b * 255)
  336. return [r, g, b]
  337. }
  338. export const hsvToRgb = hsbToRgb // alias
  339. /* Convert from Hsv */
  340. export const hsbToHsl = function (h, s, b) {
  341. return rgbToHsl(hsbToRgb(h, s, b))
  342. }
  343. export const hsvToHsl = hsbToHsl // alias
  344. /*
  345. ==================================
  346. Color Scheme Builder
  347. ==================================
  348. */
  349. export class ColorScheme {
  350. constructor (colorVal, angleArray) {
  351. this.palette = []
  352. if (angleArray === undefined && colorVal instanceof Array) {
  353. // Asume you passing a color array ['#f00','#0f0'...]
  354. this.createFromColors(colorVal)
  355. } else {
  356. // Create scheme from color + hue angles
  357. this.createFromAngles(colorVal, angleArray)
  358. }
  359. }
  360. createFromColors (colorVal) {
  361. for (let i in colorVal) {
  362. if (colorVal.hasOwn(i)) {
  363. this.palette.push(new Color(colorVal[i]))
  364. }
  365. }
  366. return this.palette
  367. }
  368. createFromAngles (colorVal, angleArray) {
  369. this.palette.push(new Color(colorVal))
  370. for (let i in angleArray) {
  371. if (angleArray.hasOwn(i)) {
  372. const tempHue = (this.palette[0].h + angleArray[i]) % 360
  373. this.palette.push(new Color(hslToRgb(tempHue, this.palette[0].s, this.palette[0].l)))
  374. }
  375. }
  376. return this.palette
  377. }
  378. /* Complementary colors constructors */
  379. static Compl (colorVal) {
  380. return new this(colorVal, [180])
  381. }
  382. /* Triad */
  383. static Triad (colorVal) {
  384. return new this(colorVal, [120, 240])
  385. }
  386. /* Tetrad */
  387. static Tetrad (colorVal) {
  388. return new this(colorVal, [60, 180, 240])
  389. }
  390. /* Analogous */
  391. static Analog (colorVal) {
  392. return new this(colorVal, [-45, 45])
  393. }
  394. /* Split complementary */
  395. static Split (colorVal) {
  396. return new this(colorVal, [150, 210])
  397. }
  398. /* Accented Analogous */
  399. static Accent (colorVal) {
  400. return new this(colorVal, [-45, 45, 180])
  401. }
  402. }