jquery.flot.barframe.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. /* Flot plugin for plotting bars frame .
  2. Copyright (c) 2007-2014 IOLA and Ole Laursen.
  3. Licensed under the MIT license.
  4. frame bars are used to show standard deviation and other statistical
  5. properties in a plot.
  6. * Created by Rui Pereira - rui (dot) pereira (at) gmail (dot) com
  7. This plugin allows you to plot frame-bars over points. Set "framebars" inside
  8. the points series to the axis name over which there will be frame values in
  9. your data array (*even* if you do not intend to plot them later, by setting
  10. "show: null" on xerr/yerr).
  11. The plugin supports these options:
  12. series: {
  13. bars: {
  14. frames: {
  15. show: false,
  16. color: null, // null = no fill
  17. lineWidth:1,
  18. shadow:{
  19. show:false,// boolean or "long":long shadow
  20. size:null,//null = auto,in units of the x axis
  21. angle:45,
  22. color:"rgba(200, 200, 200, 0.1)"
  23. }
  24. },
  25. warnings: {
  26. show: false,
  27. color: "#f00",
  28. lineWidth:0, // no border
  29. fillColor: "#f00"
  30. }
  31. }
  32. }
  33. */
  34. (function ($) {
  35. var options = {
  36. series: {
  37. bars: {
  38. frames: {
  39. show: false,
  40. color: null,
  41. lineWidth:1,
  42. shadow:{
  43. show:false,// boolean or "long":long shadow
  44. size:null,//null = auto
  45. angle:45,
  46. color:"rgba(200, 200, 200, 0.1)"
  47. }
  48. },
  49. warnings: {
  50. show: false,
  51. color: "#f00",
  52. lineWidth:0,
  53. fillColor: "#f00"
  54. }
  55. }
  56. },
  57. statement:{
  58. show:false,
  59. height:30,
  60. xaxis:"",
  61. yaxis:""
  62. }
  63. };
  64. function processOffset(plot, offset) {
  65. var options = plot.getOptions();
  66. if (options.statement.show) {
  67. offset.top += options.statement.height;
  68. }
  69. }
  70. function processRawData(plot, series, data, datapoints){
  71. if (!series.bars.frames)
  72. return;
  73. // x,y values
  74. var format = [
  75. { x: true, number: true, required: true },
  76. { y: true, number: true, required: true },
  77. { y: true, number: true, required: true }, // bar bottom
  78. { y: true, number: true, required: false } // warning
  79. ];
  80. datapoints.format = format;
  81. if(series.bars.frames.shadow.size == null) {
  82. series.bars.frames.shadow.size = 1 - series.bars.barWidth;
  83. }
  84. }
  85. function drawBar(x, y, b, barLeft, barRight, fillStyleCallback, axisx, axisy, c, horizontal, lineWidth, shadow) {
  86. var left, right, bottom, top,
  87. drawLeft, drawRight, drawTop, drawBottom,
  88. tmp;
  89. // in horizontal mode, we start the bar from the left
  90. // instead of from the bottom so it appears to be
  91. // horizontal rather than vertical
  92. if (horizontal) {
  93. drawBottom = drawRight = drawTop = true;
  94. drawLeft = false;
  95. left = b;
  96. right = x;
  97. top = y + barLeft;
  98. bottom = y + barRight;
  99. // account for negative bars
  100. if (right < left) {
  101. tmp = right;
  102. right = left;
  103. left = tmp;
  104. drawLeft = true;
  105. drawRight = false;
  106. }
  107. }
  108. else {
  109. drawLeft = drawRight = drawTop = true;
  110. drawBottom = false;
  111. left = x + barLeft;
  112. right = x + barRight;
  113. bottom = b;
  114. top = y;
  115. // account for negative bars
  116. if (top < bottom) {
  117. tmp = top;
  118. top = bottom;
  119. bottom = tmp;
  120. drawBottom = true;
  121. drawTop = false;
  122. }
  123. }
  124. // clip
  125. if (right < axisx.min || left > axisx.max ||
  126. top < axisy.min || bottom > axisy.max)
  127. return;
  128. if (left < axisx.min) {
  129. left = axisx.min;
  130. drawLeft = false;
  131. }
  132. if (right > axisx.max) {
  133. right = axisx.max;
  134. drawRight = false;
  135. }
  136. if (bottom < axisy.min) {
  137. bottom = axisy.min;
  138. drawBottom = false;
  139. }
  140. if (top > axisy.max) {
  141. top = axisy.max;
  142. drawTop = false;
  143. }
  144. var gradient,sBottomright, sBottomleft,sangle,sTop,sSize
  145. ;
  146. // shadow
  147. if (shadow){
  148. sTop = axisy.max;
  149. sangle = shadow.angle % 90;
  150. sSize = shadow.size;
  151. if(right + sSize > axisx.max) sSize = axisx.max- right;
  152. if(shadow.show == "long") {
  153. c.save();
  154. // 数值和画布y周颠倒,axisy.max - sTop
  155. sBottomright = axisy.p2c(axisy.max - sTop) * Math.tan(Math.PI/180 * sangle);
  156. sBottomright = axisx.p2c(right) + sBottomright;
  157. /*
  158. gradient = c.createLinearGradient(axisx.p2c(left),axisy.p2c(sTop)
  159. ,sBottomright,axisy.p2c(axisy.min));
  160. */
  161. gradient = c.createLinearGradient(axisx.p2c(left),axisy.p2c(sTop)
  162. ,axisx.p2c(right + sSize),axisy.p2c(axisy.min));
  163. gradient.addColorStop(0, shadow.color);
  164. gradient.addColorStop(0.8, "rgba(0,0,0,0)");
  165. c.fillStyle = gradient;
  166. //clip
  167. c.beginPath();
  168. c.moveTo(axisx.p2c(left), axisy.p2c(axisy.min));
  169. c.lineTo(axisx.p2c(left), axisy.p2c(sTop));
  170. c.lineTo(axisx.p2c(right + sSize), axisy.p2c(sTop));
  171. c.lineTo(axisx.p2c(right + sSize), axisy.p2c(axisy.min));
  172. c.closePath();
  173. c.clip();
  174. c.beginPath();
  175. // shadow
  176. sBottomleft = axisy.p2c(axisy.max - bottom) * Math.tan(Math.PI/180 * sangle);
  177. sBottomleft = axisx.p2c(left) + sBottomleft;
  178. // left,bottom point
  179. c.moveTo(axisx.p2c(left), axisy.p2c(bottom));
  180. c.lineTo(axisx.p2c(right), axisy.p2c(bottom));
  181. c.lineTo(axisx.p2c(right), axisy.p2c(sTop));
  182. c.lineTo(sBottomright, axisy.p2c(axisy.min));
  183. c.lineTo(sBottomleft, axisy.p2c(axisy.min));
  184. c.closePath();
  185. c.fill();
  186. c.restore();
  187. }
  188. else {
  189. // todo: common shadow
  190. }
  191. }
  192. left = axisx.p2c(left);
  193. bottom = axisy.p2c(bottom);
  194. right = axisx.p2c(right);
  195. top = axisy.p2c(top);
  196. // 1 px bar
  197. if (right == left) right = right +1;
  198. if (top == bottom) bottom = bottom +1;
  199. // fill the bar
  200. if (fillStyleCallback) {
  201. c.fillStyle = fillStyleCallback(bottom, top);
  202. c.fillRect(left, top, right - left, bottom - top)
  203. }
  204. // draw outline
  205. if (lineWidth > 0 && (drawLeft || drawRight || drawTop || drawBottom)) {
  206. c.beginPath();
  207. // FIXME: inline moveTo is buggy with excanvas
  208. c.moveTo(left, bottom);
  209. if (drawLeft)
  210. c.lineTo(left, top);
  211. else
  212. c.moveTo(left, top);
  213. if (drawTop)
  214. c.lineTo(right, top);
  215. else
  216. c.moveTo(right, top);
  217. if (drawRight)
  218. c.lineTo(right, bottom);
  219. else
  220. c.moveTo(right, bottom);
  221. if (drawBottom)
  222. c.lineTo(left, bottom);
  223. else
  224. c.moveTo(left, bottom);
  225. c.stroke();
  226. }
  227. }
  228. function drawSeriesFrames(plot, ctx, series) {
  229. function plotBars(datapoints, barLeft, barRight, fillStyleCallback, axisx, axisy) {
  230. var points = datapoints.points, ps = datapoints.pointsize, i,
  231. lineWidth;
  232. if (series.bars.frames && series.bars.frames.show) {
  233. ctx.lineWidth = lineWidth = series.bars.frames.lineWidth != null? series.bars.frames.lineWidth : series.bars.lineWidth;
  234. ctx.strokeStyle = series.bars.frames.color != null ? series.bars.frames.color : series.color;
  235. for (i = 0; i < points.length; i += ps) {
  236. if (points[i] == null)
  237. continue;
  238. drawBar(points[i], series.yaxis.max, points[i + 2], barLeft, barRight, fillStyleCallback, axisx, axisy, ctx, series.bars.horizontal, lineWidth, series.bars.frames.shadow);
  239. }
  240. }
  241. var top, bottom;
  242. // draw warings
  243. if (series.bars.warnings && series.bars.warnings.show) {
  244. ctx.lineWidth = lineWidth= series.bars.warnings.lineWidth != null? series.bars.warnings.lineWidth : series.bars.lineWidth;
  245. ctx.strokeStyle = series.bars.warnings.color;
  246. fillStyleCallback = function(top, bottom) {return series.bars.warnings.fillColor};
  247. for ( i = 0; i < points.length; i += ps) {
  248. if (points[i] == null || !points[i + 3] )
  249. continue;
  250. if (points[i + 3] >= points[i + 1]) {
  251. top = bottom = points[i + 3];
  252. }
  253. else {
  254. top = points[i + 1];
  255. bottom = points[i + 3];
  256. }
  257. drawBar(points[i], top, bottom, barLeft, barRight, fillStyleCallback, axisx, axisy, ctx, series.bars.horizontal, lineWidth);
  258. }
  259. }
  260. }
  261. var plotOffset = plot.getPlotOffset;
  262. ctx.save();
  263. ctx.translate(plotOffset.left, plotOffset.top);
  264. var barLeft;
  265. switch (series.bars.align) {
  266. case "left":
  267. barLeft = 0;
  268. break;
  269. case "right":
  270. barLeft = -series.bars.barWidth;
  271. break;
  272. default:
  273. barLeft = -series.bars.barWidth / 2;
  274. }
  275. var fillStyleCallback = null;
  276. plotBars(series.datapoints, barLeft, barLeft + series.bars.barWidth, fillStyleCallback, series.xaxis, series.yaxis);
  277. ctx.restore();
  278. }
  279. function draw(plot, ctx){
  280. var plotOffset = plot.getPlotOffset();
  281. insertStatement(plot);
  282. ctx.save();
  283. ctx.translate(plotOffset.left, plotOffset.top);
  284. $.each(plot.getData(), function (i, s) {
  285. drawSeriesFrames(plot, ctx, s);
  286. });
  287. ctx.restore();
  288. }
  289. function insertStatement(plot) {
  290. var options = plot.getOptions();
  291. var placeholder = plot.getPlaceholder();
  292. if (options.statement.container != null) {
  293. $(options.statement.container).html("");
  294. } else {
  295. placeholder.find("#statement").remove();
  296. }
  297. if (!options.statement.show) {
  298. return;
  299. }
  300. // statement container
  301. // Generate markup for the list of entries, in their final order
  302. var $yaxis ,$xaxis ,$statement,$legend,s;
  303. s = options.statement;
  304. $statement = $("<div id='statement'></div>");
  305. $xaxis = $("<div id='stmtXaxis'></div>").appendTo($statement);
  306. $yaxis = $("<div id='stmtYaxis'></div>").appendTo($statement);
  307. $legend = $("<div id='stmtLegend'></div>").appendTo($statement);
  308. $statement.css({
  309. position: "absolute",
  310. top: s.top + "px",
  311. left: s.left + "px",
  312. width: "100%",
  313. height: s.height + "px",
  314. color: options.grid.color,
  315. "font-size":"smaller" });
  316. if(s.yaxis) {
  317. $yaxis.css({position: "absolute",
  318. bottom: "0px",
  319. left: "0px",
  320. "margin-bottom": "1em"
  321. });
  322. $yaxis.text(s.yaxis);
  323. };
  324. $legend.css({position: "absolute",
  325. bottom: "0px",
  326. right: "0px",
  327. "margin-bottom": "1em",
  328. "margin-right": plot.getPlotOffset().right + "px"
  329. });
  330. $legend.append("<div id='warnOverColor' style='float:left; display: inline-block; width: 20px; height: 0.5em;margin-top:0.25em;; background-color: " + options.series.bars.warnings.fillColor + ";'></div>");
  331. $legend.append("<div id='warnOverText' style='float:left; display: inline-block;margin: 0 1em 0 0.2em'>超出预警值</div>");
  332. $legend.append("<div id='warnLineColor' style='float:left; display: inline-block; width: 20px; height: 0px; margin-top:0.5em;border:1px solid " + options.series.bars.warnings.fillColor + ";'></div>");
  333. $legend.append("<div id='warnLineText' style='float:left; display: inline-block;margin: 0 0 0 0.2em'>预警值</div>");
  334. placeholder.append($statement);
  335. }
  336. function init(plot) {
  337. plot.hooks.processOffset.push(processOffset);
  338. plot.hooks.processRawData.push(processRawData);
  339. plot.hooks.draw.push(draw);
  340. }
  341. $.plot.plugins.push({
  342. init: init,
  343. options: options,
  344. name: 'barframe',
  345. version: '1.0'
  346. });
  347. })(jQuery);