7b8f9e2719fc28c9efb1f9b68ce7e6a9d4c271464f1790a7316bfaa8b343e932bed496bcac7d04d319479533085472e17e71858fbf122f2ceecad2c5331121 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  1. import LRU from '../core/LRU';
  2. const kCSSColorTable = {
  3. 'transparent': [0, 0, 0, 0], 'aliceblue': [240, 248, 255, 1],
  4. 'antiquewhite': [250, 235, 215, 1], 'aqua': [0, 255, 255, 1],
  5. 'aquamarine': [127, 255, 212, 1], 'azure': [240, 255, 255, 1],
  6. 'beige': [245, 245, 220, 1], 'bisque': [255, 228, 196, 1],
  7. 'black': [0, 0, 0, 1], 'blanchedalmond': [255, 235, 205, 1],
  8. 'blue': [0, 0, 255, 1], 'blueviolet': [138, 43, 226, 1],
  9. 'brown': [165, 42, 42, 1], 'burlywood': [222, 184, 135, 1],
  10. 'cadetblue': [95, 158, 160, 1], 'chartreuse': [127, 255, 0, 1],
  11. 'chocolate': [210, 105, 30, 1], 'coral': [255, 127, 80, 1],
  12. 'cornflowerblue': [100, 149, 237, 1], 'cornsilk': [255, 248, 220, 1],
  13. 'crimson': [220, 20, 60, 1], 'cyan': [0, 255, 255, 1],
  14. 'darkblue': [0, 0, 139, 1], 'darkcyan': [0, 139, 139, 1],
  15. 'darkgoldenrod': [184, 134, 11, 1], 'darkgray': [169, 169, 169, 1],
  16. 'darkgreen': [0, 100, 0, 1], 'darkgrey': [169, 169, 169, 1],
  17. 'darkkhaki': [189, 183, 107, 1], 'darkmagenta': [139, 0, 139, 1],
  18. 'darkolivegreen': [85, 107, 47, 1], 'darkorange': [255, 140, 0, 1],
  19. 'darkorchid': [153, 50, 204, 1], 'darkred': [139, 0, 0, 1],
  20. 'darksalmon': [233, 150, 122, 1], 'darkseagreen': [143, 188, 143, 1],
  21. 'darkslateblue': [72, 61, 139, 1], 'darkslategray': [47, 79, 79, 1],
  22. 'darkslategrey': [47, 79, 79, 1], 'darkturquoise': [0, 206, 209, 1],
  23. 'darkviolet': [148, 0, 211, 1], 'deeppink': [255, 20, 147, 1],
  24. 'deepskyblue': [0, 191, 255, 1], 'dimgray': [105, 105, 105, 1],
  25. 'dimgrey': [105, 105, 105, 1], 'dodgerblue': [30, 144, 255, 1],
  26. 'firebrick': [178, 34, 34, 1], 'floralwhite': [255, 250, 240, 1],
  27. 'forestgreen': [34, 139, 34, 1], 'fuchsia': [255, 0, 255, 1],
  28. 'gainsboro': [220, 220, 220, 1], 'ghostwhite': [248, 248, 255, 1],
  29. 'gold': [255, 215, 0, 1], 'goldenrod': [218, 165, 32, 1],
  30. 'gray': [128, 128, 128, 1], 'green': [0, 128, 0, 1],
  31. 'greenyellow': [173, 255, 47, 1], 'grey': [128, 128, 128, 1],
  32. 'honeydew': [240, 255, 240, 1], 'hotpink': [255, 105, 180, 1],
  33. 'indianred': [205, 92, 92, 1], 'indigo': [75, 0, 130, 1],
  34. 'ivory': [255, 255, 240, 1], 'khaki': [240, 230, 140, 1],
  35. 'lavender': [230, 230, 250, 1], 'lavenderblush': [255, 240, 245, 1],
  36. 'lawngreen': [124, 252, 0, 1], 'lemonchiffon': [255, 250, 205, 1],
  37. 'lightblue': [173, 216, 230, 1], 'lightcoral': [240, 128, 128, 1],
  38. 'lightcyan': [224, 255, 255, 1], 'lightgoldenrodyellow': [250, 250, 210, 1],
  39. 'lightgray': [211, 211, 211, 1], 'lightgreen': [144, 238, 144, 1],
  40. 'lightgrey': [211, 211, 211, 1], 'lightpink': [255, 182, 193, 1],
  41. 'lightsalmon': [255, 160, 122, 1], 'lightseagreen': [32, 178, 170, 1],
  42. 'lightskyblue': [135, 206, 250, 1], 'lightslategray': [119, 136, 153, 1],
  43. 'lightslategrey': [119, 136, 153, 1], 'lightsteelblue': [176, 196, 222, 1],
  44. 'lightyellow': [255, 255, 224, 1], 'lime': [0, 255, 0, 1],
  45. 'limegreen': [50, 205, 50, 1], 'linen': [250, 240, 230, 1],
  46. 'magenta': [255, 0, 255, 1], 'maroon': [128, 0, 0, 1],
  47. 'mediumaquamarine': [102, 205, 170, 1], 'mediumblue': [0, 0, 205, 1],
  48. 'mediumorchid': [186, 85, 211, 1], 'mediumpurple': [147, 112, 219, 1],
  49. 'mediumseagreen': [60, 179, 113, 1], 'mediumslateblue': [123, 104, 238, 1],
  50. 'mediumspringgreen': [0, 250, 154, 1], 'mediumturquoise': [72, 209, 204, 1],
  51. 'mediumvioletred': [199, 21, 133, 1], 'midnightblue': [25, 25, 112, 1],
  52. 'mintcream': [245, 255, 250, 1], 'mistyrose': [255, 228, 225, 1],
  53. 'moccasin': [255, 228, 181, 1], 'navajowhite': [255, 222, 173, 1],
  54. 'navy': [0, 0, 128, 1], 'oldlace': [253, 245, 230, 1],
  55. 'olive': [128, 128, 0, 1], 'olivedrab': [107, 142, 35, 1],
  56. 'orange': [255, 165, 0, 1], 'orangered': [255, 69, 0, 1],
  57. 'orchid': [218, 112, 214, 1], 'palegoldenrod': [238, 232, 170, 1],
  58. 'palegreen': [152, 251, 152, 1], 'paleturquoise': [175, 238, 238, 1],
  59. 'palevioletred': [219, 112, 147, 1], 'papayawhip': [255, 239, 213, 1],
  60. 'peachpuff': [255, 218, 185, 1], 'peru': [205, 133, 63, 1],
  61. 'pink': [255, 192, 203, 1], 'plum': [221, 160, 221, 1],
  62. 'powderblue': [176, 224, 230, 1], 'purple': [128, 0, 128, 1],
  63. 'red': [255, 0, 0, 1], 'rosybrown': [188, 143, 143, 1],
  64. 'royalblue': [65, 105, 225, 1], 'saddlebrown': [139, 69, 19, 1],
  65. 'salmon': [250, 128, 114, 1], 'sandybrown': [244, 164, 96, 1],
  66. 'seagreen': [46, 139, 87, 1], 'seashell': [255, 245, 238, 1],
  67. 'sienna': [160, 82, 45, 1], 'silver': [192, 192, 192, 1],
  68. 'skyblue': [135, 206, 235, 1], 'slateblue': [106, 90, 205, 1],
  69. 'slategray': [112, 128, 144, 1], 'slategrey': [112, 128, 144, 1],
  70. 'snow': [255, 250, 250, 1], 'springgreen': [0, 255, 127, 1],
  71. 'steelblue': [70, 130, 180, 1], 'tan': [210, 180, 140, 1],
  72. 'teal': [0, 128, 128, 1], 'thistle': [216, 191, 216, 1],
  73. 'tomato': [255, 99, 71, 1], 'turquoise': [64, 224, 208, 1],
  74. 'violet': [238, 130, 238, 1], 'wheat': [245, 222, 179, 1],
  75. 'white': [255, 255, 255, 1], 'whitesmoke': [245, 245, 245, 1],
  76. 'yellow': [255, 255, 0, 1], 'yellowgreen': [154, 205, 50, 1]
  77. };
  78. function clampCssByte(i: number): number { // Clamp to integer 0 .. 255.
  79. i = Math.round(i); // Seems to be what Chrome does (vs truncation).
  80. return i < 0 ? 0 : i > 255 ? 255 : i;
  81. }
  82. function clampCssAngle(i: number): number { // Clamp to integer 0 .. 360.
  83. i = Math.round(i); // Seems to be what Chrome does (vs truncation).
  84. return i < 0 ? 0 : i > 360 ? 360 : i;
  85. }
  86. function clampCssFloat(f: number): number { // Clamp to float 0.0 .. 1.0.
  87. return f < 0 ? 0 : f > 1 ? 1 : f;
  88. }
  89. function parseCssInt(val: string | number): number { // int or percentage.
  90. let str = val as string;
  91. if (str.length && str.charAt(str.length - 1) === '%') {
  92. return clampCssByte(parseFloat(str) / 100 * 255);
  93. }
  94. return clampCssByte(parseInt(str, 10));
  95. }
  96. function parseCssFloat(val: string | number): number { // float or percentage.
  97. let str = val as string;
  98. if (str.length && str.charAt(str.length - 1) === '%') {
  99. return clampCssFloat(parseFloat(str) / 100);
  100. }
  101. return clampCssFloat(parseFloat(str));
  102. }
  103. function cssHueToRgb(m1: number, m2: number, h: number): number {
  104. if (h < 0) {
  105. h += 1;
  106. }
  107. else if (h > 1) {
  108. h -= 1;
  109. }
  110. if (h * 6 < 1) {
  111. return m1 + (m2 - m1) * h * 6;
  112. }
  113. if (h * 2 < 1) {
  114. return m2;
  115. }
  116. if (h * 3 < 2) {
  117. return m1 + (m2 - m1) * (2 / 3 - h) * 6;
  118. }
  119. return m1;
  120. }
  121. function lerpNumber(a: number, b: number, p: number): number {
  122. return a + (b - a) * p;
  123. }
  124. function setRgba(out: number[], r: number, g: number, b: number, a: number): number[] {
  125. out[0] = r;
  126. out[1] = g;
  127. out[2] = b;
  128. out[3] = a;
  129. return out;
  130. }
  131. function copyRgba(out: number[], a: number[]) {
  132. out[0] = a[0];
  133. out[1] = a[1];
  134. out[2] = a[2];
  135. out[3] = a[3];
  136. return out;
  137. }
  138. const colorCache = new LRU<number[]>(20);
  139. let lastRemovedArr: number[] = null;
  140. function putToCache(colorStr: string, rgbaArr: number[]) {
  141. // Reuse removed array
  142. if (lastRemovedArr) {
  143. copyRgba(lastRemovedArr, rgbaArr);
  144. }
  145. lastRemovedArr = colorCache.put(colorStr, lastRemovedArr || (rgbaArr.slice()));
  146. }
  147. export function parse(colorStr: string, rgbaArr?: number[]): number[] {
  148. if (!colorStr) {
  149. return;
  150. }
  151. rgbaArr = rgbaArr || [];
  152. let cached = colorCache.get(colorStr);
  153. if (cached) {
  154. return copyRgba(rgbaArr, cached);
  155. }
  156. // colorStr may be not string
  157. colorStr = colorStr + '';
  158. // Remove all whitespace, not compliant, but should just be more accepting.
  159. let str = colorStr.replace(/ /g, '').toLowerCase();
  160. // Color keywords (and transparent) lookup.
  161. if (str in kCSSColorTable) {
  162. copyRgba(rgbaArr, kCSSColorTable[str as keyof typeof kCSSColorTable]);
  163. putToCache(colorStr, rgbaArr);
  164. return rgbaArr;
  165. }
  166. // supports the forms #rgb, #rrggbb, #rgba, #rrggbbaa
  167. // #rrggbbaa(use the last pair of digits as alpha)
  168. // see https://drafts.csswg.org/css-color/#hex-notation
  169. const strLen = str.length;
  170. if (str.charAt(0) === '#') {
  171. if (strLen === 4 || strLen === 5) {
  172. const iv = parseInt(str.slice(1, 4), 16); // TODO(deanm): Stricter parsing.
  173. if (!(iv >= 0 && iv <= 0xfff)) {
  174. setRgba(rgbaArr, 0, 0, 0, 1);
  175. return; // Covers NaN.
  176. }
  177. // interpret values of the form #rgb as #rrggbb and #rgba as #rrggbbaa
  178. setRgba(rgbaArr,
  179. ((iv & 0xf00) >> 4) | ((iv & 0xf00) >> 8),
  180. (iv & 0xf0) | ((iv & 0xf0) >> 4),
  181. (iv & 0xf) | ((iv & 0xf) << 4),
  182. strLen === 5 ? parseInt(str.slice(4), 16) / 0xf : 1
  183. );
  184. putToCache(colorStr, rgbaArr);
  185. return rgbaArr;
  186. }
  187. else if (strLen === 7 || strLen === 9) {
  188. const iv = parseInt(str.slice(1, 7), 16); // TODO(deanm): Stricter parsing.
  189. if (!(iv >= 0 && iv <= 0xffffff)) {
  190. setRgba(rgbaArr, 0, 0, 0, 1);
  191. return; // Covers NaN.
  192. }
  193. setRgba(rgbaArr,
  194. (iv & 0xff0000) >> 16,
  195. (iv & 0xff00) >> 8,
  196. iv & 0xff,
  197. strLen === 9 ? parseInt(str.slice(7), 16) / 0xff : 1
  198. );
  199. putToCache(colorStr, rgbaArr);
  200. return rgbaArr;
  201. }
  202. return;
  203. }
  204. let op = str.indexOf('(');
  205. let ep = str.indexOf(')');
  206. if (op !== -1 && ep + 1 === strLen) {
  207. let fname = str.substr(0, op);
  208. let params: (number | string)[] = str.substr(op + 1, ep - (op + 1)).split(',');
  209. let alpha = 1; // To allow case fallthrough.
  210. switch (fname) {
  211. case 'rgba':
  212. if (params.length !== 4) {
  213. return params.length === 3
  214. // to be compatible with rgb
  215. ? setRgba(rgbaArr, +params[0], +params[1], +params[2], 1)
  216. : setRgba(rgbaArr, 0, 0, 0, 1);
  217. }
  218. alpha = parseCssFloat(params.pop()); // jshint ignore:line
  219. // Fall through.
  220. case 'rgb':
  221. if (params.length >= 3) {
  222. setRgba(rgbaArr,
  223. parseCssInt(params[0]),
  224. parseCssInt(params[1]),
  225. parseCssInt(params[2]),
  226. params.length === 3 ? alpha : parseCssFloat(params[3])
  227. );
  228. putToCache(colorStr, rgbaArr);
  229. return rgbaArr;
  230. }
  231. else {
  232. setRgba(rgbaArr, 0, 0, 0, 1);
  233. return;
  234. }
  235. case 'hsla':
  236. if (params.length !== 4) {
  237. setRgba(rgbaArr, 0, 0, 0, 1);
  238. return;
  239. }
  240. params[3] = parseCssFloat(params[3]);
  241. hsla2rgba(params, rgbaArr);
  242. putToCache(colorStr, rgbaArr);
  243. return rgbaArr;
  244. case 'hsl':
  245. if (params.length !== 3) {
  246. setRgba(rgbaArr, 0, 0, 0, 1);
  247. return;
  248. }
  249. hsla2rgba(params, rgbaArr);
  250. putToCache(colorStr, rgbaArr);
  251. return rgbaArr;
  252. default:
  253. return;
  254. }
  255. }
  256. setRgba(rgbaArr, 0, 0, 0, 1);
  257. return;
  258. }
  259. function hsla2rgba(hsla: (number | string) [], rgba?: number[]): number[] {
  260. const h = (((parseFloat(hsla[0] as string) % 360) + 360) % 360) / 360; // 0 .. 1
  261. // NOTE(deanm): According to the CSS spec s/l should only be
  262. // percentages, but we don't bother and let float or percentage.
  263. const s = parseCssFloat(hsla[1]);
  264. const l = parseCssFloat(hsla[2]);
  265. const m2 = l <= 0.5 ? l * (s + 1) : l + s - l * s;
  266. const m1 = l * 2 - m2;
  267. rgba = rgba || [];
  268. setRgba(rgba,
  269. clampCssByte(cssHueToRgb(m1, m2, h + 1 / 3) * 255),
  270. clampCssByte(cssHueToRgb(m1, m2, h) * 255),
  271. clampCssByte(cssHueToRgb(m1, m2, h - 1 / 3) * 255),
  272. 1
  273. );
  274. if (hsla.length === 4) {
  275. rgba[3] = hsla[3] as number;
  276. }
  277. return rgba;
  278. }
  279. function rgba2hsla(rgba: number[]): number[] {
  280. if (!rgba) {
  281. return;
  282. }
  283. // RGB from 0 to 255
  284. const R = rgba[0] / 255;
  285. const G = rgba[1] / 255;
  286. const B = rgba[2] / 255;
  287. const vMin = Math.min(R, G, B); // Min. value of RGB
  288. const vMax = Math.max(R, G, B); // Max. value of RGB
  289. const delta = vMax - vMin; // Delta RGB value
  290. const L = (vMax + vMin) / 2;
  291. let H;
  292. let S;
  293. // HSL results from 0 to 1
  294. if (delta === 0) {
  295. H = 0;
  296. S = 0;
  297. }
  298. else {
  299. if (L < 0.5) {
  300. S = delta / (vMax + vMin);
  301. }
  302. else {
  303. S = delta / (2 - vMax - vMin);
  304. }
  305. const deltaR = (((vMax - R) / 6) + (delta / 2)) / delta;
  306. const deltaG = (((vMax - G) / 6) + (delta / 2)) / delta;
  307. const deltaB = (((vMax - B) / 6) + (delta / 2)) / delta;
  308. if (R === vMax) {
  309. H = deltaB - deltaG;
  310. }
  311. else if (G === vMax) {
  312. H = (1 / 3) + deltaR - deltaB;
  313. }
  314. else if (B === vMax) {
  315. H = (2 / 3) + deltaG - deltaR;
  316. }
  317. if (H < 0) {
  318. H += 1;
  319. }
  320. if (H > 1) {
  321. H -= 1;
  322. }
  323. }
  324. const hsla = [H * 360, S, L];
  325. if (rgba[3] != null) {
  326. hsla.push(rgba[3]);
  327. }
  328. return hsla;
  329. }
  330. export function lift(color: string, level: number) {
  331. const colorArr = parse(color);
  332. if (colorArr) {
  333. for (let i = 0; i < 3; i++) {
  334. if (level < 0) {
  335. colorArr[i] = colorArr[i] * (1 - level) | 0;
  336. }
  337. else {
  338. colorArr[i] = ((255 - colorArr[i]) * level + colorArr[i]) | 0;
  339. }
  340. if (colorArr[i] > 255) {
  341. colorArr[i] = 255;
  342. }
  343. else if (colorArr[i] < 0) {
  344. colorArr[i] = 0;
  345. }
  346. }
  347. return stringify(colorArr, colorArr.length === 4 ? 'rgba' : 'rgb');
  348. }
  349. }
  350. export function toHex(color: string): string {
  351. const colorArr = parse(color);
  352. if (colorArr) {
  353. return ((1 << 24) + (colorArr[0] << 16) + (colorArr[1] << 8) + (+colorArr[2])).toString(16).slice(1);
  354. }
  355. }
  356. /**
  357. * Map value to color. Faster than lerp methods because color is represented by rgba array.
  358. * @param normalizedValue A float between 0 and 1.
  359. * @param colors List of rgba color array
  360. * @param out Mapped gba color array
  361. * @return will be null/undefined if input illegal.
  362. */
  363. export function fastLerp(
  364. normalizedValue: number,
  365. colors: number[][],
  366. out?: number[]
  367. ): number[] {
  368. if (!(colors && colors.length)
  369. || !(normalizedValue >= 0 && normalizedValue <= 1)
  370. ) {
  371. return;
  372. }
  373. out = out || [];
  374. const value = normalizedValue * (colors.length - 1);
  375. const leftIndex = Math.floor(value);
  376. const rightIndex = Math.ceil(value);
  377. const leftColor = colors[leftIndex];
  378. const rightColor = colors[rightIndex];
  379. const dv = value - leftIndex;
  380. out[0] = clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv));
  381. out[1] = clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv));
  382. out[2] = clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv));
  383. out[3] = clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv));
  384. return out;
  385. }
  386. /**
  387. * @deprecated
  388. */
  389. export const fastMapToColor = fastLerp;
  390. type LerpFullOutput = {
  391. color: string
  392. leftIndex: number
  393. rightIndex: number
  394. value: number
  395. }
  396. /**
  397. * @param normalizedValue A float between 0 and 1.
  398. * @param colors Color list.
  399. * @param fullOutput Default false.
  400. * @return Result color. If fullOutput,
  401. return {color: ..., leftIndex: ..., rightIndex: ..., value: ...},
  402. */
  403. export function lerp(
  404. normalizedValue: number,
  405. colors: string[],
  406. fullOutput: boolean
  407. ): LerpFullOutput
  408. export function lerp(
  409. normalizedValue: number,
  410. colors: string[]
  411. ): string
  412. export function lerp(
  413. normalizedValue: number,
  414. colors: string[],
  415. fullOutput?: boolean
  416. ): string | LerpFullOutput {
  417. if (!(colors && colors.length)
  418. || !(normalizedValue >= 0 && normalizedValue <= 1)
  419. ) {
  420. return;
  421. }
  422. const value = normalizedValue * (colors.length - 1);
  423. const leftIndex = Math.floor(value);
  424. const rightIndex = Math.ceil(value);
  425. const leftColor = parse(colors[leftIndex]);
  426. const rightColor = parse(colors[rightIndex]);
  427. const dv = value - leftIndex;
  428. const color = stringify(
  429. [
  430. clampCssByte(lerpNumber(leftColor[0], rightColor[0], dv)),
  431. clampCssByte(lerpNumber(leftColor[1], rightColor[1], dv)),
  432. clampCssByte(lerpNumber(leftColor[2], rightColor[2], dv)),
  433. clampCssFloat(lerpNumber(leftColor[3], rightColor[3], dv))
  434. ],
  435. 'rgba'
  436. );
  437. return fullOutput
  438. ? {
  439. color: color,
  440. leftIndex: leftIndex,
  441. rightIndex: rightIndex,
  442. value: value
  443. }
  444. : color;
  445. }
  446. /**
  447. * @deprecated
  448. */
  449. export const mapToColor = lerp;
  450. /**
  451. * @param color
  452. * @param h 0 ~ 360, ignore when null.
  453. * @param s 0 ~ 1, ignore when null.
  454. * @param l 0 ~ 1, ignore when null.
  455. * @return Color string in rgba format.
  456. * @memberOf module:zrender/util/color
  457. */
  458. export function modifyHSL(color: string, h?: number, s?: number, l?: number): string {
  459. let colorArr = parse(color);
  460. if (color) {
  461. colorArr = rgba2hsla(colorArr);
  462. h != null && (colorArr[0] = clampCssAngle(h));
  463. s != null && (colorArr[1] = parseCssFloat(s));
  464. l != null && (colorArr[2] = parseCssFloat(l));
  465. return stringify(hsla2rgba(colorArr), 'rgba');
  466. }
  467. }
  468. /**
  469. * @param color
  470. * @param alpha 0 ~ 1
  471. * @return Color string in rgba format.
  472. * @memberOf module:zrender/util/color
  473. */
  474. export function modifyAlpha(color: string, alpha?: number): string {
  475. const colorArr = parse(color);
  476. if (colorArr && alpha != null) {
  477. colorArr[3] = clampCssFloat(alpha);
  478. return stringify(colorArr, 'rgba');
  479. }
  480. }
  481. /**
  482. * @param arrColor like [12,33,44,0.4]
  483. * @param type 'rgba', 'hsva', ...
  484. * @return Result color. (If input illegal, return undefined).
  485. */
  486. export function stringify(arrColor: number[], type: string): string {
  487. if (!arrColor || !arrColor.length) {
  488. return;
  489. }
  490. let colorStr = arrColor[0] + ',' + arrColor[1] + ',' + arrColor[2];
  491. if (type === 'rgba' || type === 'hsva' || type === 'hsla') {
  492. colorStr += ',' + arrColor[3];
  493. }
  494. return type + '(' + colorStr + ')';
  495. }
  496. /**
  497. * Calculate luminance. It will include alpha.
  498. */
  499. export function lum(color: string, backgroundLum: number) {
  500. const arr = parse(color);
  501. return arr
  502. ? (0.299 * arr[0] + 0.587 * arr[1] + 0.114 * arr[2]) * arr[3] / 255
  503. + (1 - arr[3]) * backgroundLum // Blending with assumed white background.
  504. : 0;
  505. }
  506. /**
  507. * Generate a random color
  508. */
  509. export function random(): string {
  510. return stringify([
  511. Math.round(Math.random() * 255),
  512. Math.round(Math.random() * 255),
  513. Math.round(Math.random() * 255)
  514. ], 'rgb');
  515. }