GaugeView.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. var PointerPath = require("./PointerPath");
  2. var graphic = require("../../util/graphic");
  3. var ChartView = require("../../view/Chart");
  4. var _number = require("../../util/number");
  5. var parsePercent = _number.parsePercent;
  6. var round = _number.round;
  7. var linearMap = _number.linearMap;
  8. function parsePosition(seriesModel, api) {
  9. var center = seriesModel.get('center');
  10. var width = api.getWidth();
  11. var height = api.getHeight();
  12. var size = Math.min(width, height);
  13. var cx = parsePercent(center[0], api.getWidth());
  14. var cy = parsePercent(center[1], api.getHeight());
  15. var r = parsePercent(seriesModel.get('radius'), size / 2);
  16. return {
  17. cx: cx,
  18. cy: cy,
  19. r: r
  20. };
  21. }
  22. function formatLabel(label, labelFormatter) {
  23. if (labelFormatter) {
  24. if (typeof labelFormatter === 'string') {
  25. label = labelFormatter.replace('{value}', label != null ? label : '');
  26. } else if (typeof labelFormatter === 'function') {
  27. label = labelFormatter(label);
  28. }
  29. }
  30. return label;
  31. }
  32. var PI2 = Math.PI * 2;
  33. var GaugeView = ChartView.extend({
  34. type: 'gauge',
  35. render: function (seriesModel, ecModel, api) {
  36. this.group.removeAll();
  37. var colorList = seriesModel.get('axisLine.lineStyle.color');
  38. var posInfo = parsePosition(seriesModel, api);
  39. this._renderMain(seriesModel, ecModel, api, colorList, posInfo);
  40. },
  41. dispose: function () {},
  42. _renderMain: function (seriesModel, ecModel, api, colorList, posInfo) {
  43. var group = this.group;
  44. var axisLineModel = seriesModel.getModel('axisLine');
  45. var lineStyleModel = axisLineModel.getModel('lineStyle');
  46. var clockwise = seriesModel.get('clockwise');
  47. var startAngle = -seriesModel.get('startAngle') / 180 * Math.PI;
  48. var endAngle = -seriesModel.get('endAngle') / 180 * Math.PI;
  49. var angleRangeSpan = (endAngle - startAngle) % PI2;
  50. var prevEndAngle = startAngle;
  51. var axisLineWidth = lineStyleModel.get('width');
  52. for (var i = 0; i < colorList.length; i++) {
  53. // Clamp
  54. var percent = Math.min(Math.max(colorList[i][0], 0), 1);
  55. var endAngle = startAngle + angleRangeSpan * percent;
  56. var sector = new graphic.Sector({
  57. shape: {
  58. startAngle: prevEndAngle,
  59. endAngle: endAngle,
  60. cx: posInfo.cx,
  61. cy: posInfo.cy,
  62. clockwise: clockwise,
  63. r0: posInfo.r - axisLineWidth,
  64. r: posInfo.r
  65. },
  66. silent: true
  67. });
  68. sector.setStyle({
  69. fill: colorList[i][1]
  70. });
  71. sector.setStyle(lineStyleModel.getLineStyle( // Because we use sector to simulate arc
  72. // so the properties for stroking are useless
  73. ['color', 'borderWidth', 'borderColor']));
  74. group.add(sector);
  75. prevEndAngle = endAngle;
  76. }
  77. var getColor = function (percent) {
  78. // Less than 0
  79. if (percent <= 0) {
  80. return colorList[0][1];
  81. }
  82. for (var i = 0; i < colorList.length; i++) {
  83. if (colorList[i][0] >= percent && (i === 0 ? 0 : colorList[i - 1][0]) < percent) {
  84. return colorList[i][1];
  85. }
  86. } // More than 1
  87. return colorList[i - 1][1];
  88. };
  89. if (!clockwise) {
  90. var tmp = startAngle;
  91. startAngle = endAngle;
  92. endAngle = tmp;
  93. }
  94. this._renderTicks(seriesModel, ecModel, api, getColor, posInfo, startAngle, endAngle, clockwise);
  95. this._renderPointer(seriesModel, ecModel, api, getColor, posInfo, startAngle, endAngle, clockwise);
  96. this._renderTitle(seriesModel, ecModel, api, getColor, posInfo);
  97. this._renderDetail(seriesModel, ecModel, api, getColor, posInfo);
  98. },
  99. _renderTicks: function (seriesModel, ecModel, api, getColor, posInfo, startAngle, endAngle, clockwise) {
  100. var group = this.group;
  101. var cx = posInfo.cx;
  102. var cy = posInfo.cy;
  103. var r = posInfo.r;
  104. var minVal = +seriesModel.get('min');
  105. var maxVal = +seriesModel.get('max');
  106. var splitLineModel = seriesModel.getModel('splitLine');
  107. var tickModel = seriesModel.getModel('axisTick');
  108. var labelModel = seriesModel.getModel('axisLabel');
  109. var splitNumber = seriesModel.get('splitNumber');
  110. var subSplitNumber = tickModel.get('splitNumber');
  111. var splitLineLen = parsePercent(splitLineModel.get('length'), r);
  112. var tickLen = parsePercent(tickModel.get('length'), r);
  113. var angle = startAngle;
  114. var step = (endAngle - startAngle) / splitNumber;
  115. var subStep = step / subSplitNumber;
  116. var splitLineStyle = splitLineModel.getModel('lineStyle').getLineStyle();
  117. var tickLineStyle = tickModel.getModel('lineStyle').getLineStyle();
  118. for (var i = 0; i <= splitNumber; i++) {
  119. var unitX = Math.cos(angle);
  120. var unitY = Math.sin(angle); // Split line
  121. if (splitLineModel.get('show')) {
  122. var splitLine = new graphic.Line({
  123. shape: {
  124. x1: unitX * r + cx,
  125. y1: unitY * r + cy,
  126. x2: unitX * (r - splitLineLen) + cx,
  127. y2: unitY * (r - splitLineLen) + cy
  128. },
  129. style: splitLineStyle,
  130. silent: true
  131. });
  132. if (splitLineStyle.stroke === 'auto') {
  133. splitLine.setStyle({
  134. stroke: getColor(i / splitNumber)
  135. });
  136. }
  137. group.add(splitLine);
  138. } // Label
  139. if (labelModel.get('show')) {
  140. var label = formatLabel(round(i / splitNumber * (maxVal - minVal) + minVal), labelModel.get('formatter'));
  141. var distance = labelModel.get('distance');
  142. var autoColor = getColor(i / splitNumber);
  143. group.add(new graphic.Text({
  144. style: graphic.setTextStyle({}, labelModel, {
  145. text: label,
  146. x: unitX * (r - splitLineLen - distance) + cx,
  147. y: unitY * (r - splitLineLen - distance) + cy,
  148. textVerticalAlign: unitY < -0.4 ? 'top' : unitY > 0.4 ? 'bottom' : 'middle',
  149. textAlign: unitX < -0.4 ? 'left' : unitX > 0.4 ? 'right' : 'center'
  150. }, {
  151. autoColor: autoColor
  152. }),
  153. silent: true
  154. }));
  155. } // Axis tick
  156. if (tickModel.get('show') && i !== splitNumber) {
  157. for (var j = 0; j <= subSplitNumber; j++) {
  158. var unitX = Math.cos(angle);
  159. var unitY = Math.sin(angle);
  160. var tickLine = new graphic.Line({
  161. shape: {
  162. x1: unitX * r + cx,
  163. y1: unitY * r + cy,
  164. x2: unitX * (r - tickLen) + cx,
  165. y2: unitY * (r - tickLen) + cy
  166. },
  167. silent: true,
  168. style: tickLineStyle
  169. });
  170. if (tickLineStyle.stroke === 'auto') {
  171. tickLine.setStyle({
  172. stroke: getColor((i + j / subSplitNumber) / splitNumber)
  173. });
  174. }
  175. group.add(tickLine);
  176. angle += subStep;
  177. }
  178. angle -= subStep;
  179. } else {
  180. angle += step;
  181. }
  182. }
  183. },
  184. _renderPointer: function (seriesModel, ecModel, api, getColor, posInfo, startAngle, endAngle, clockwise) {
  185. var group = this.group;
  186. var oldData = this._data;
  187. if (!seriesModel.get('pointer.show')) {
  188. // Remove old element
  189. oldData && oldData.eachItemGraphicEl(function (el) {
  190. group.remove(el);
  191. });
  192. return;
  193. }
  194. var valueExtent = [+seriesModel.get('min'), +seriesModel.get('max')];
  195. var angleExtent = [startAngle, endAngle];
  196. var data = seriesModel.getData();
  197. data.diff(oldData).add(function (idx) {
  198. var pointer = new PointerPath({
  199. shape: {
  200. angle: startAngle
  201. }
  202. });
  203. graphic.initProps(pointer, {
  204. shape: {
  205. angle: linearMap(data.get('value', idx), valueExtent, angleExtent, true)
  206. }
  207. }, seriesModel);
  208. group.add(pointer);
  209. data.setItemGraphicEl(idx, pointer);
  210. }).update(function (newIdx, oldIdx) {
  211. var pointer = oldData.getItemGraphicEl(oldIdx);
  212. graphic.updateProps(pointer, {
  213. shape: {
  214. angle: linearMap(data.get('value', newIdx), valueExtent, angleExtent, true)
  215. }
  216. }, seriesModel);
  217. group.add(pointer);
  218. data.setItemGraphicEl(newIdx, pointer);
  219. }).remove(function (idx) {
  220. var pointer = oldData.getItemGraphicEl(idx);
  221. group.remove(pointer);
  222. }).execute();
  223. data.eachItemGraphicEl(function (pointer, idx) {
  224. var itemModel = data.getItemModel(idx);
  225. var pointerModel = itemModel.getModel('pointer');
  226. pointer.setShape({
  227. x: posInfo.cx,
  228. y: posInfo.cy,
  229. width: parsePercent(pointerModel.get('width'), posInfo.r),
  230. r: parsePercent(pointerModel.get('length'), posInfo.r)
  231. });
  232. pointer.useStyle(itemModel.getModel('itemStyle.normal').getItemStyle());
  233. if (pointer.style.fill === 'auto') {
  234. pointer.setStyle('fill', getColor(linearMap(data.get('value', idx), valueExtent, [0, 1], true)));
  235. }
  236. graphic.setHoverStyle(pointer, itemModel.getModel('itemStyle.emphasis').getItemStyle());
  237. });
  238. this._data = data;
  239. },
  240. _renderTitle: function (seriesModel, ecModel, api, getColor, posInfo) {
  241. var titleModel = seriesModel.getModel('title');
  242. if (titleModel.get('show')) {
  243. var offsetCenter = titleModel.get('offsetCenter');
  244. var x = posInfo.cx + parsePercent(offsetCenter[0], posInfo.r);
  245. var y = posInfo.cy + parsePercent(offsetCenter[1], posInfo.r);
  246. var minVal = +seriesModel.get('min');
  247. var maxVal = +seriesModel.get('max');
  248. var value = seriesModel.getData().get('value', 0);
  249. var autoColor = getColor(linearMap(value, [minVal, maxVal], [0, 1], true));
  250. this.group.add(new graphic.Text({
  251. silent: true,
  252. style: graphic.setTextStyle({}, titleModel, {
  253. x: x,
  254. y: y,
  255. // FIXME First data name ?
  256. text: seriesModel.getData().getName(0),
  257. textAlign: 'center',
  258. textVerticalAlign: 'middle'
  259. }, {
  260. autoColor: autoColor,
  261. forceRich: true
  262. })
  263. }));
  264. }
  265. },
  266. _renderDetail: function (seriesModel, ecModel, api, getColor, posInfo) {
  267. var detailModel = seriesModel.getModel('detail');
  268. var minVal = +seriesModel.get('min');
  269. var maxVal = +seriesModel.get('max');
  270. if (detailModel.get('show')) {
  271. var offsetCenter = detailModel.get('offsetCenter');
  272. var x = posInfo.cx + parsePercent(offsetCenter[0], posInfo.r);
  273. var y = posInfo.cy + parsePercent(offsetCenter[1], posInfo.r);
  274. var width = parsePercent(detailModel.get('width'), posInfo.r);
  275. var height = parsePercent(detailModel.get('height'), posInfo.r);
  276. var value = seriesModel.getData().get('value', 0);
  277. var autoColor = getColor(linearMap(value, [minVal, maxVal], [0, 1], true));
  278. this.group.add(new graphic.Text({
  279. silent: true,
  280. style: graphic.setTextStyle({}, detailModel, {
  281. x: x,
  282. y: y,
  283. text: formatLabel( // FIXME First data name ?
  284. value, detailModel.get('formatter')),
  285. textWidth: isNaN(width) ? null : width,
  286. textHeight: isNaN(height) ? null : height,
  287. textAlign: 'center',
  288. textVerticalAlign: 'middle'
  289. }, {
  290. autoColor: autoColor,
  291. forceRich: true
  292. })
  293. }));
  294. }
  295. }
  296. });
  297. var _default = GaugeView;
  298. module.exports = _default;