GaugeView.js 13 KB

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