g.line.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. /*!
  2. * g.Raphael 0.5 - Charting library, based on Raphaël
  3. *
  4. * Copyright (c) 2009 Dmitry Baranovskiy (http://g.raphaeljs.com)
  5. * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
  6. */
  7. (function () {
  8. function shrink(values, dim) {
  9. var k = values.length / dim,
  10. j = 0,
  11. l = k,
  12. sum = 0,
  13. res = [];
  14. while (j < values.length) {
  15. l--;
  16. if (l < 0) {
  17. sum += values[j] * (1 + l);
  18. res.push(sum / k);
  19. sum = values[j++] * -l;
  20. l += k;
  21. } else {
  22. sum += values[j++];
  23. }
  24. }
  25. return res;
  26. }
  27. function getAnchors(p1x, p1y, p2x, p2y, p3x, p3y) {
  28. var l1 = (p2x - p1x) / 2,
  29. l2 = (p3x - p2x) / 2,
  30. a = Math.atan((p2x - p1x) / Math.abs(p2y - p1y)),
  31. b = Math.atan((p3x - p2x) / Math.abs(p2y - p3y));
  32. a = p1y < p2y ? Math.PI - a : a;
  33. b = p3y < p2y ? Math.PI - b : b;
  34. var alpha = Math.PI / 2 - ((a + b) % (Math.PI * 2)) / 2,
  35. dx1 = l1 * Math.sin(alpha + a),
  36. dy1 = l1 * Math.cos(alpha + a),
  37. dx2 = l2 * Math.sin(alpha + b),
  38. dy2 = l2 * Math.cos(alpha + b);
  39. return {
  40. x1: p2x - dx1,
  41. y1: p2y + dy1,
  42. x2: p2x + dx2,
  43. y2: p2y + dy2
  44. };
  45. }
  46. function Linechart(paper, x, y, width, height, valuesx, valuesy, opts) {
  47. var chartinst = this;
  48. opts = opts || {};
  49. if (!paper.raphael.is(valuesx[0], "array")) {
  50. valuesx = [valuesx];
  51. }
  52. if (!paper.raphael.is(valuesy[0], "array")) {
  53. valuesy = [valuesy];
  54. }
  55. var gutter = opts.gutter || 10,
  56. len = Math.max(valuesx[0].length, valuesy[0].length),
  57. symbol = opts.symbol || "",
  58. colors = opts.colors || chartinst.colors,
  59. columns = null,
  60. dots = null,
  61. chart = paper.set(),
  62. path = [];
  63. for (var i = 0, ii = valuesy.length; i < ii; i++) {
  64. len = Math.max(len, valuesy[i].length);
  65. }
  66. var shades = paper.set();
  67. for (i = 0, ii = valuesy.length; i < ii; i++) {
  68. if (opts.shade) {
  69. shades.push(paper.path().attr({ stroke: "none", fill: colors[i], opacity: opts.nostroke ? 1 : .3 }));
  70. }
  71. if (valuesy[i].length > width - 2 * gutter) {
  72. valuesy[i] = shrink(valuesy[i], width - 2 * gutter);
  73. len = width - 2 * gutter;
  74. }
  75. if (valuesx[i] && valuesx[i].length > width - 2 * gutter) {
  76. valuesx[i] = shrink(valuesx[i], width - 2 * gutter);
  77. }
  78. }
  79. var allx = Array.prototype.concat.apply([], valuesx),
  80. ally = Array.prototype.concat.apply([], valuesy),
  81. xdim = chartinst.snapEnds(Math.min.apply(Math, allx), Math.max.apply(Math, allx), valuesx[0].length - 1),
  82. minx = xdim.from,
  83. maxx = xdim.to,
  84. ydim = chartinst.snapEnds(Math.min.apply(Math, ally), Math.max.apply(Math, ally), valuesy[0].length - 1),
  85. miny = ydim.from,
  86. maxy = ydim.to,
  87. kx = (width - gutter * 2) / ((maxx - minx) || 1),
  88. ky = (height - gutter * 2) / ((maxy - miny) || 1);
  89. var axis = paper.set();
  90. if (opts.axis) {
  91. miny = 0;
  92. maxy = ydim.to;
  93. ky = (height - gutter * 2) / ((maxy - miny) || 1);
  94. var ax = (opts.axis + "").split(/[,\s]+/);
  95. +ax[0] && axis.push(chartinst.axis(x + gutter, y + gutter, width - 2 * gutter, minx, maxx, opts.axisxstep || Math.floor((width - 2 * gutter) / 20), 2, opts.axisxlables, paper));
  96. +ax[1] && axis.push(chartinst.axis(x + width - gutter, y + height - gutter, height - 2 * gutter, ydim.from, ydim.to, opts.axisystep || Math.floor((height - 2 * gutter) / 20), 3, paper));
  97. +ax[2] && axis.push(chartinst.axis(x + gutter, y + height - gutter, width - 2 * gutter, minx, maxx, opts.axisxstep || Math.floor((width - 2 * gutter) / 20), 0, opts.axisxlables, paper));
  98. //+ax[3] && axis.push(chartinst.axis(x + gutter, y + height - gutter, height - 2 * gutter, ydim.from, ydim.to, opts.axisystep || Math.floor((height - 2 * gutter) / 20), 1, paper));
  99. +ax[3] && axis.push(chartinst.axis(x + gutter, y + height - gutter, height - 2 * gutter, miny, maxy, opts.axisystep || Math.floor((height - 2 * gutter) / 20), 1, paper));
  100. }
  101. var lines = paper.set(),
  102. symbols = paper.set(),
  103. line;
  104. for (i = 0, ii = valuesy.length; i < ii; i++) {
  105. if (!opts.nostroke) {
  106. lines.push(line = paper.path().attr({
  107. stroke: colors[i],
  108. "stroke-width": opts.width || 2,
  109. "stroke-linejoin": "round",
  110. "stroke-linecap": "round",
  111. "stroke-dasharray": opts.dash || ""
  112. }));
  113. }
  114. var sym = Raphael.is(symbol, "array") ? symbol[i] : symbol,
  115. symset = paper.set();
  116. path = [];
  117. for (var j = 0, jj = valuesy[i].length; j < jj; j++) {
  118. var X = x + gutter + ((valuesx[i] || valuesx[0])[j] - minx) * kx,
  119. Y = y + height - gutter - (valuesy[i][j] - miny) * ky;
  120. (Raphael.is(sym, "array") ? sym[j] : sym) && symset.push(paper[Raphael.is(sym, "array") ? sym[j] : sym](X, Y, (opts.width || 2) * 3).attr({ fill: colors[i], stroke: "none" }));
  121. if (opts.smooth) {
  122. if (j && j != jj - 1) {
  123. var X0 = x + gutter + ((valuesx[i] || valuesx[0])[j - 1] - minx) * kx,
  124. Y0 = y + height - gutter - (valuesy[i][j - 1] - miny) * ky,
  125. X2 = x + gutter + ((valuesx[i] || valuesx[0])[j + 1] - minx) * kx,
  126. Y2 = y + height - gutter - (valuesy[i][j + 1] - miny) * ky,
  127. a = getAnchors(X0, Y0, X, Y, X2, Y2);
  128. path = path.concat([a.x1, a.y1, X, Y, a.x2, a.y2]);
  129. }
  130. if (!j) {
  131. path = ["M", X, Y, "C", X, Y];
  132. }
  133. } else {
  134. path = path.concat([j ? "L" : "M", X, Y]);
  135. }
  136. }
  137. if (opts.smooth) {
  138. path = path.concat([X, Y, X, Y]);
  139. }
  140. symbols.push(symset);
  141. if (opts.shade) {
  142. shades[i].attr({ path: path.concat(["L", X, y + height - gutter, "L", x + gutter + ((valuesx[i] || valuesx[0])[0] - minx) * kx, y + height - gutter, "z"]).join(",") });
  143. }
  144. !opts.nostroke && line.attr({ path: path.join(",") });
  145. }
  146. function createColumns(f) {
  147. // unite Xs together
  148. var Xs = [];
  149. for (var i = 0, ii = valuesx.length; i < ii; i++) {
  150. Xs = Xs.concat(valuesx[i]);
  151. }
  152. Xs.sort(function(a,b){return a-b;});
  153. // remove duplicates
  154. var Xs2 = [],
  155. xs = [];
  156. for (i = 0, ii = Xs.length; i < ii; i++) {
  157. Xs[i] != Xs[i - 1] && Xs2.push(Xs[i]) && xs.push(x + gutter + (Xs[i] - minx) * kx);
  158. }
  159. Xs = Xs2;
  160. ii = Xs.length;
  161. var cvrs = f || paper.set();
  162. for (i = 0; i < ii; i++) {
  163. var X = xs[i] - (xs[i] - (xs[i - 1] || x)) / 2,
  164. w = ((xs[i + 1] || x + width) - xs[i]) / 2 + (xs[i] - (xs[i - 1] || x)) / 2,
  165. C;
  166. f ? (C = {}) : cvrs.push(C = paper.rect(X - 1, y, Math.max(w + 1, 1), height).attr({ stroke: "none", fill: "#000", opacity: 0 }));
  167. C.values = [];
  168. C.symbols = paper.set();
  169. C.y = [];
  170. C.x = xs[i];
  171. C.axis = Xs[i];
  172. for (var j = 0, jj = valuesy.length; j < jj; j++) {
  173. Xs2 = valuesx[j] || valuesx[0];
  174. for (var k = 0, kk = Xs2.length; k < kk; k++) {
  175. if (Xs2[k] == Xs[i]) {
  176. C.values.push(valuesy[j][k]);
  177. C.y.push(y + height - gutter - (valuesy[j][k] - miny) * ky);
  178. C.symbols.push(chart.symbols[j][k]);
  179. }
  180. }
  181. }
  182. f && f.call(C);
  183. }
  184. !f && (columns = cvrs);
  185. }
  186. function createDots(f) {
  187. var cvrs = f || paper.set(),
  188. C;
  189. for (var i = 0, ii = valuesy.length; i < ii; i++) {
  190. for (var j = 0, jj = valuesy[i].length; j < jj; j++) {
  191. var X = x + gutter + ((valuesx[i] || valuesx[0])[j] - minx) * kx,
  192. nearX = x + gutter + ((valuesx[i] || valuesx[0])[j ? j - 1 : 1] - minx) * kx,
  193. Y = y + height - gutter - (valuesy[i][j] - miny) * ky;
  194. f ? (C = {}) : cvrs.push(C = paper.circle(X, Y, Math.abs(nearX - X) / 2).attr({ stroke: "none", fill: "#000", opacity: 0 }));
  195. C.x = X;
  196. C.y = Y;
  197. C.value = valuesy[i][j];
  198. C.line = chart.lines[i];
  199. C.shade = chart.shades[i];
  200. C.symbol = chart.symbols[i][j];
  201. C.symbols = chart.symbols[i];
  202. C.axis = (valuesx[i] || valuesx[0])[j];
  203. f && f.call(C);
  204. }
  205. }
  206. !f && (dots = cvrs);
  207. }
  208. chart.push(lines, shades, symbols, axis, columns, dots);
  209. chart.lines = lines;
  210. chart.shades = shades;
  211. chart.symbols = symbols;
  212. chart.axis = axis;
  213. chart.hoverColumn = function (fin, fout) {
  214. !columns && createColumns();
  215. columns.mouseover(fin).mouseout(fout);
  216. return this;
  217. };
  218. chart.clickColumn = function (f) {
  219. !columns && createColumns();
  220. columns.click(f);
  221. return this;
  222. };
  223. chart.hrefColumn = function (cols) {
  224. var hrefs = paper.raphael.is(arguments[0], "array") ? arguments[0] : arguments;
  225. if (!(arguments.length - 1) && typeof cols == "object") {
  226. for (var x in cols) {
  227. for (var i = 0, ii = columns.length; i < ii; i++) if (columns[i].axis == x) {
  228. columns[i].attr("href", cols[x]);
  229. }
  230. }
  231. }
  232. !columns && createColumns();
  233. for (i = 0, ii = hrefs.length; i < ii; i++) {
  234. columns[i] && columns[i].attr("href", hrefs[i]);
  235. }
  236. return this;
  237. };
  238. chart.hover = function (fin, fout) {
  239. !dots && createDots();
  240. dots.mouseover(fin).mouseout(fout);
  241. return this;
  242. };
  243. chart.click = function (f) {
  244. !dots && createDots();
  245. dots.click(f);
  246. return this;
  247. };
  248. chart.each = function (f) {
  249. createDots(f);
  250. return this;
  251. };
  252. chart.eachColumn = function (f) {
  253. createColumns(f);
  254. return this;
  255. };
  256. return chart;
  257. };
  258. //inheritance
  259. var F = function() {};
  260. F.prototype = Raphael.g;
  261. Linechart.prototype = new F;
  262. //public
  263. Raphael.fn.linechart = function(x, y, width, height, valuesx, valuesy, opts) {
  264. return new Linechart(this, x, y, width, height, valuesx, valuesy, opts);
  265. }
  266. })();