Gauge2.html 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  5. <title>The source code</title>
  6. <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
  7. <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
  8. <style type="text/css">
  9. .highlight { display: block; background-color: #ddd; }
  10. </style>
  11. <script type="text/javascript">
  12. function highlight() {
  13. document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
  14. }
  15. </script>
  16. </head>
  17. <body onload="prettyPrint(); highlight();">
  18. <pre class="prettyprint lang-js"><span id='Ext-chart-series-Gauge'>/**
  19. </span> * @class Ext.chart.series.Gauge
  20. *
  21. * Creates a Gauge Chart. Gauge Charts are used to show progress in a certain variable. There are two ways of using the Gauge chart.
  22. * One is setting a store element into the Gauge and selecting the field to be used from that store. Another one is instantiating the
  23. * visualization and using the `setValue` method to adjust the value you want.
  24. *
  25. * An example of Gauge visualization:
  26. *
  27. * @example
  28. * var store = Ext.create('Ext.data.JsonStore', {
  29. * fields: ['data'],
  30. * data: [
  31. * { 'value':80 }
  32. * ]
  33. * });
  34. *
  35. * Ext.create('Ext.chart.Chart', {
  36. * renderTo: Ext.getBody(),
  37. * store: store,
  38. * width: 400,
  39. * height: 250,
  40. * animate: true,
  41. * insetPadding: 30,
  42. * axes: [{
  43. * type: 'gauge',
  44. * position: 'gauge',
  45. * minimum: 0,
  46. * maximum: 100,
  47. * steps: 10,
  48. * margin: 10
  49. * }],
  50. * series: [{
  51. * type: 'gauge',
  52. * field: 'value',
  53. * donut: 30,
  54. * colorSet: ['#F49D10', '#ddd']
  55. * }]
  56. * });
  57. *
  58. * Ext.widget(&quot;button&quot;, {
  59. * renderTo: Ext.getBody(),
  60. * text: &quot;Refresh&quot;,
  61. * handler: function() {
  62. * store.getAt(0).set('value', Math.round(Math.random()*100));
  63. * }
  64. * });
  65. *
  66. * In this example we create a special Gauge axis to be used with the gauge visualization (describing half-circle markers), and also we're
  67. * setting a maximum, minimum and steps configuration options into the axis. The Gauge series configuration contains the store field to be bound to
  68. * the visual display and the color set to be used with the visualization.
  69. *
  70. * @xtype gauge
  71. */
  72. Ext.define('Ext.chart.series.Gauge', {
  73. /* Begin Definitions */
  74. extend: 'Ext.chart.series.Series',
  75. /* End Definitions */
  76. type: &quot;gauge&quot;,
  77. alias: 'series.gauge',
  78. rad: Math.PI / 180,
  79. <span id='Ext-chart-series-Gauge-cfg-highlightDuration'> /**
  80. </span> * @cfg {Number} highlightDuration
  81. * The duration for the pie slice highlight effect.
  82. */
  83. highlightDuration: 150,
  84. <span id='Ext-chart-series-Gauge-cfg-angleField'> /**
  85. </span> * @cfg {String} angleField (required)
  86. * The store record field name to be used for the pie angles.
  87. * The values bound to this field name must be positive real numbers.
  88. */
  89. angleField: false,
  90. <span id='Ext-chart-series-Gauge-cfg-needle'> /**
  91. </span> * @cfg {Boolean} needle
  92. * Use the Gauge Series as an area series or add a needle to it. Default's false.
  93. */
  94. needle: false,
  95. <span id='Ext-chart-series-Gauge-cfg-donut'> /**
  96. </span> * @cfg {Boolean/Number} donut
  97. * Use the entire disk or just a fraction of it for the gauge. Default's false.
  98. */
  99. donut: false,
  100. <span id='Ext-chart-series-Gauge-cfg-showInLegend'> /**
  101. </span> * @cfg {Boolean} showInLegend
  102. * Whether to add the pie chart elements as legend items. Default's false.
  103. */
  104. showInLegend: false,
  105. <span id='Ext-chart-series-Gauge-cfg-style'> /**
  106. </span> * @cfg {Object} style
  107. * An object containing styles for overriding series styles from Theming.
  108. */
  109. style: {},
  110. constructor: function(config) {
  111. this.callParent(arguments);
  112. var me = this,
  113. chart = me.chart,
  114. surface = chart.surface,
  115. store = chart.store,
  116. shadow = chart.shadow, i, l, cfg;
  117. Ext.apply(me, config, {
  118. shadowAttributes: [{
  119. &quot;stroke-width&quot;: 6,
  120. &quot;stroke-opacity&quot;: 1,
  121. stroke: 'rgb(200, 200, 200)',
  122. translate: {
  123. x: 1.2,
  124. y: 2
  125. }
  126. },
  127. {
  128. &quot;stroke-width&quot;: 4,
  129. &quot;stroke-opacity&quot;: 1,
  130. stroke: 'rgb(150, 150, 150)',
  131. translate: {
  132. x: 0.9,
  133. y: 1.5
  134. }
  135. },
  136. {
  137. &quot;stroke-width&quot;: 2,
  138. &quot;stroke-opacity&quot;: 1,
  139. stroke: 'rgb(100, 100, 100)',
  140. translate: {
  141. x: 0.6,
  142. y: 1
  143. }
  144. }]
  145. });
  146. me.group = surface.getGroup(me.seriesId);
  147. if (shadow) {
  148. for (i = 0, l = me.shadowAttributes.length; i &lt; l; i++) {
  149. me.shadowGroups.push(surface.getGroup(me.seriesId + '-shadows' + i));
  150. }
  151. }
  152. surface.customAttributes.segment = function(opt) {
  153. return me.getSegment(opt);
  154. };
  155. },
  156. // @private updates some onbefore render parameters.
  157. initialize: function() {
  158. var me = this,
  159. store = me.chart.getChartStore(),
  160. data = store.data.items,
  161. i, ln, rec;
  162. //Add yFields to be used in Legend.js
  163. me.yField = [];
  164. if (me.label.field) {
  165. for (i = 0, ln = data.length; i &lt; ln; i++) {
  166. rec = data[i];
  167. me.yField.push(rec.get(me.label.field));
  168. }
  169. }
  170. },
  171. // @private returns an object with properties for a Slice
  172. getSegment: function(opt) {
  173. var me = this,
  174. rad = me.rad,
  175. cos = Math.cos,
  176. sin = Math.sin,
  177. abs = Math.abs,
  178. x = me.centerX,
  179. y = me.centerY,
  180. x1 = 0, x2 = 0, x3 = 0, x4 = 0,
  181. y1 = 0, y2 = 0, y3 = 0, y4 = 0,
  182. delta = 1e-2,
  183. r = opt.endRho - opt.startRho,
  184. startAngle = opt.startAngle,
  185. endAngle = opt.endAngle,
  186. midAngle = (startAngle + endAngle) / 2 * rad,
  187. margin = opt.margin || 0,
  188. flag = abs(endAngle - startAngle) &gt; 180,
  189. a1 = Math.min(startAngle, endAngle) * rad,
  190. a2 = Math.max(startAngle, endAngle) * rad,
  191. singleSlice = false;
  192. x += margin * cos(midAngle);
  193. y += margin * sin(midAngle);
  194. x1 = x + opt.startRho * cos(a1);
  195. y1 = y + opt.startRho * sin(a1);
  196. x2 = x + opt.endRho * cos(a1);
  197. y2 = y + opt.endRho * sin(a1);
  198. x3 = x + opt.startRho * cos(a2);
  199. y3 = y + opt.startRho * sin(a2);
  200. x4 = x + opt.endRho * cos(a2);
  201. y4 = y + opt.endRho * sin(a2);
  202. if (abs(x1 - x3) &lt;= delta &amp;&amp; abs(y1 - y3) &lt;= delta) {
  203. singleSlice = true;
  204. }
  205. //Solves mysterious clipping bug with IE
  206. if (singleSlice) {
  207. return {
  208. path: [
  209. [&quot;M&quot;, x1, y1],
  210. [&quot;L&quot;, x2, y2],
  211. [&quot;A&quot;, opt.endRho, opt.endRho, 0, +flag, 1, x4, y4],
  212. [&quot;Z&quot;]]
  213. };
  214. } else {
  215. return {
  216. path: [
  217. [&quot;M&quot;, x1, y1],
  218. [&quot;L&quot;, x2, y2],
  219. [&quot;A&quot;, opt.endRho, opt.endRho, 0, +flag, 1, x4, y4],
  220. [&quot;L&quot;, x3, y3],
  221. [&quot;A&quot;, opt.startRho, opt.startRho, 0, +flag, 0, x1, y1],
  222. [&quot;Z&quot;]]
  223. };
  224. }
  225. },
  226. // @private utility function to calculate the middle point of a pie slice.
  227. calcMiddle: function(item) {
  228. var me = this,
  229. rad = me.rad,
  230. slice = item.slice,
  231. x = me.centerX,
  232. y = me.centerY,
  233. startAngle = slice.startAngle,
  234. endAngle = slice.endAngle,
  235. radius = Math.max(('rho' in slice) ? slice.rho: me.radius, me.label.minMargin),
  236. donut = +me.donut,
  237. a1 = Math.min(startAngle, endAngle) * rad,
  238. a2 = Math.max(startAngle, endAngle) * rad,
  239. midAngle = -(a1 + (a2 - a1) / 2),
  240. xm = x + (item.endRho + item.startRho) / 2 * Math.cos(midAngle),
  241. ym = y - (item.endRho + item.startRho) / 2 * Math.sin(midAngle);
  242. item.middle = {
  243. x: xm,
  244. y: ym
  245. };
  246. },
  247. <span id='Ext-chart-series-Gauge-method-drawSeries'> /**
  248. </span> * Draws the series for the current chart.
  249. */
  250. drawSeries: function() {
  251. var me = this,
  252. chart = me.chart,
  253. store = chart.getChartStore(),
  254. group = me.group,
  255. animate = me.chart.animate,
  256. axis = me.chart.axes.get(0),
  257. minimum = axis &amp;&amp; axis.minimum || me.minimum || 0,
  258. maximum = axis &amp;&amp; axis.maximum || me.maximum || 0,
  259. field = me.angleField || me.field || me.xField,
  260. surface = chart.surface,
  261. chartBBox = chart.chartBBox,
  262. rad = me.rad,
  263. donut = +me.donut,
  264. values = {},
  265. items = [],
  266. seriesStyle = me.seriesStyle,
  267. seriesLabelStyle = me.seriesLabelStyle,
  268. colorArrayStyle = me.colorArrayStyle,
  269. colorArrayLength = colorArrayStyle &amp;&amp; colorArrayStyle.length || 0,
  270. gutterX = chart.maxGutter[0],
  271. gutterY = chart.maxGutter[1],
  272. cos = Math.cos,
  273. sin = Math.sin,
  274. rendererAttributes, centerX, centerY, slice, slices, sprite, value,
  275. item, ln, record, i, j, startAngle, endAngle, middleAngle, sliceLength, path,
  276. p, spriteOptions, bbox, splitAngle, sliceA, sliceB;
  277. Ext.apply(seriesStyle, me.style || {});
  278. me.setBBox();
  279. bbox = me.bbox;
  280. //override theme colors
  281. if (me.colorSet) {
  282. colorArrayStyle = me.colorSet;
  283. colorArrayLength = colorArrayStyle.length;
  284. }
  285. //if not store or store is empty then there's nothing to draw
  286. if (!store || !store.getCount() || me.seriesIsHidden) {
  287. me.hide();
  288. me.items = [];
  289. return;
  290. }
  291. centerX = me.centerX = chartBBox.x + (chartBBox.width / 2);
  292. centerY = me.centerY = chartBBox.y + chartBBox.height;
  293. me.radius = Math.min(centerX - chartBBox.x, centerY - chartBBox.y);
  294. me.slices = slices = [];
  295. me.items = items = [];
  296. if (!me.value) {
  297. record = store.getAt(0);
  298. me.value = record.get(field);
  299. }
  300. value = me.value;
  301. if (me.needle) {
  302. sliceA = {
  303. series: me,
  304. value: value,
  305. startAngle: -180,
  306. endAngle: 0,
  307. rho: me.radius
  308. };
  309. splitAngle = -180 * (1 - (value - minimum) / (maximum - minimum));
  310. slices.push(sliceA);
  311. } else {
  312. splitAngle = -180 * (1 - (value - minimum) / (maximum - minimum));
  313. sliceA = {
  314. series: me,
  315. value: value,
  316. startAngle: -180,
  317. endAngle: splitAngle,
  318. rho: me.radius
  319. };
  320. sliceB = {
  321. series: me,
  322. value: me.maximum - value,
  323. startAngle: splitAngle,
  324. endAngle: 0,
  325. rho: me.radius
  326. };
  327. slices.push(sliceA, sliceB);
  328. }
  329. //do pie slices after.
  330. for (i = 0, ln = slices.length; i &lt; ln; i++) {
  331. slice = slices[i];
  332. sprite = group.getAt(i);
  333. //set pie slice properties
  334. rendererAttributes = Ext.apply({
  335. segment: {
  336. startAngle: slice.startAngle,
  337. endAngle: slice.endAngle,
  338. margin: 0,
  339. rho: slice.rho,
  340. startRho: slice.rho * +donut / 100,
  341. endRho: slice.rho
  342. }
  343. }, Ext.apply(seriesStyle, colorArrayStyle &amp;&amp; { fill: colorArrayStyle[i % colorArrayLength] } || {}));
  344. item = Ext.apply({},
  345. rendererAttributes.segment, {
  346. slice: slice,
  347. series: me,
  348. storeItem: record,
  349. index: i
  350. });
  351. items[i] = item;
  352. // Create a new sprite if needed (no height)
  353. if (!sprite) {
  354. spriteOptions = Ext.apply({
  355. type: &quot;path&quot;,
  356. group: group
  357. }, Ext.apply(seriesStyle, colorArrayStyle &amp;&amp; { fill: colorArrayStyle[i % colorArrayLength] } || {}));
  358. sprite = surface.add(Ext.apply(spriteOptions, rendererAttributes));
  359. }
  360. slice.sprite = slice.sprite || [];
  361. item.sprite = sprite;
  362. slice.sprite.push(sprite);
  363. if (animate) {
  364. rendererAttributes = me.renderer(sprite, record, rendererAttributes, i, store);
  365. sprite._to = rendererAttributes;
  366. me.onAnimate(sprite, {
  367. to: rendererAttributes
  368. });
  369. } else {
  370. rendererAttributes = me.renderer(sprite, record, Ext.apply(rendererAttributes, {
  371. hidden: false
  372. }), i, store);
  373. sprite.setAttributes(rendererAttributes, true);
  374. }
  375. }
  376. if (me.needle) {
  377. splitAngle = splitAngle * Math.PI / 180;
  378. if (!me.needleSprite) {
  379. me.needleSprite = me.chart.surface.add({
  380. type: 'path',
  381. path: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle),
  382. centerY + -Math.abs((me.radius * +donut / 100) * sin(splitAngle)),
  383. 'L', centerX + me.radius * cos(splitAngle),
  384. centerY + -Math.abs(me.radius * sin(splitAngle))],
  385. 'stroke-width': 4,
  386. 'stroke': '#222'
  387. });
  388. } else {
  389. if (animate) {
  390. me.onAnimate(me.needleSprite, {
  391. to: {
  392. path: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle),
  393. centerY + -Math.abs((me.radius * +donut / 100) * sin(splitAngle)),
  394. 'L', centerX + me.radius * cos(splitAngle),
  395. centerY + -Math.abs(me.radius * sin(splitAngle))]
  396. }
  397. });
  398. } else {
  399. me.needleSprite.setAttributes({
  400. type: 'path',
  401. path: ['M', centerX + (me.radius * +donut / 100) * cos(splitAngle),
  402. centerY + -Math.abs((me.radius * +donut / 100) * sin(splitAngle)),
  403. 'L', centerX + me.radius * cos(splitAngle),
  404. centerY + -Math.abs(me.radius * sin(splitAngle))]
  405. });
  406. }
  407. }
  408. me.needleSprite.setAttributes({
  409. hidden: false
  410. }, true);
  411. }
  412. delete me.value;
  413. },
  414. <span id='Ext-chart-series-Gauge-method-setValue'> /**
  415. </span> * Sets the Gauge chart to the current specified value.
  416. */
  417. setValue: function (value) {
  418. this.value = value;
  419. this.drawSeries();
  420. },
  421. // @private callback for when creating a label sprite.
  422. onCreateLabel: function(storeItem, item, i, display) {},
  423. // @private callback for when placing a label sprite.
  424. onPlaceLabel: function(label, storeItem, item, i, display, animate, index) {},
  425. // @private callback for when placing a callout.
  426. onPlaceCallout: function() {},
  427. // @private handles sprite animation for the series.
  428. onAnimate: function(sprite, attr) {
  429. sprite.show();
  430. return this.callParent(arguments);
  431. },
  432. isItemInPoint: function(x, y, item, i) {
  433. var me = this,
  434. cx = me.centerX,
  435. cy = me.centerY,
  436. abs = Math.abs,
  437. dx = abs(x - cx),
  438. dy = abs(y - cy),
  439. startAngle = item.startAngle,
  440. endAngle = item.endAngle,
  441. rho = Math.sqrt(dx * dx + dy * dy),
  442. angle = Math.atan2(y - cy, x - cx) / me.rad;
  443. //Only trigger events for the filled portion of the Gauge.
  444. return (i === 0) &amp;&amp; (angle &gt;= startAngle &amp;&amp; angle &lt; endAngle &amp;&amp;
  445. rho &gt;= item.startRho &amp;&amp; rho &lt;= item.endRho);
  446. },
  447. // @private shows all elements in the series.
  448. showAll: function() {
  449. if (!isNaN(this._index)) {
  450. this.__excludes[this._index] = false;
  451. this.drawSeries();
  452. }
  453. },
  454. <span id='Ext-chart-series-Gauge-method-getLegendColor'> /**
  455. </span> * Returns the color of the series (to be displayed as color for the series legend item).
  456. * @param item {Object} Info about the item; same format as returned by #getItemForPoint
  457. */
  458. getLegendColor: function(index) {
  459. var me = this;
  460. return me.colorArrayStyle[index % me.colorArrayStyle.length];
  461. }
  462. });
  463. </pre>
  464. </body>
  465. </html>