Line.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. var zrUtil = require("zrender/lib/core/util");
  2. var vector = require("zrender/lib/core/vector");
  3. var symbolUtil = require("../../util/symbol");
  4. var LinePath = require("./LinePath");
  5. var graphic = require("../../util/graphic");
  6. var _number = require("../../util/number");
  7. var round = _number.round;
  8. /**
  9. * @module echarts/chart/helper/Line
  10. */
  11. var SYMBOL_CATEGORIES = ['fromSymbol', 'toSymbol'];
  12. function makeSymbolTypeKey(symbolCategory) {
  13. return '_' + symbolCategory + 'Type';
  14. }
  15. /**
  16. * @inner
  17. */
  18. function createSymbol(name, lineData, idx) {
  19. var color = lineData.getItemVisual(idx, 'color');
  20. var symbolType = lineData.getItemVisual(idx, name);
  21. var symbolSize = lineData.getItemVisual(idx, name + 'Size');
  22. if (!symbolType || symbolType === 'none') {
  23. return;
  24. }
  25. if (!zrUtil.isArray(symbolSize)) {
  26. symbolSize = [symbolSize, symbolSize];
  27. }
  28. var symbolPath = symbolUtil.createSymbol(symbolType, -symbolSize[0] / 2, -symbolSize[1] / 2, symbolSize[0], symbolSize[1], color);
  29. symbolPath.name = name;
  30. return symbolPath;
  31. }
  32. function createLine(points) {
  33. var line = new LinePath({
  34. name: 'line'
  35. });
  36. setLinePoints(line.shape, points);
  37. return line;
  38. }
  39. function setLinePoints(targetShape, points) {
  40. var p1 = points[0];
  41. var p2 = points[1];
  42. var cp1 = points[2];
  43. targetShape.x1 = p1[0];
  44. targetShape.y1 = p1[1];
  45. targetShape.x2 = p2[0];
  46. targetShape.y2 = p2[1];
  47. targetShape.percent = 1;
  48. if (cp1) {
  49. targetShape.cpx1 = cp1[0];
  50. targetShape.cpy1 = cp1[1];
  51. } else {
  52. targetShape.cpx1 = NaN;
  53. targetShape.cpy1 = NaN;
  54. }
  55. }
  56. function updateSymbolAndLabelBeforeLineUpdate() {
  57. var lineGroup = this;
  58. var symbolFrom = lineGroup.childOfName('fromSymbol');
  59. var symbolTo = lineGroup.childOfName('toSymbol');
  60. var label = lineGroup.childOfName('label'); // Quick reject
  61. if (!symbolFrom && !symbolTo && label.ignore) {
  62. return;
  63. }
  64. var invScale = 1;
  65. var parentNode = this.parent;
  66. while (parentNode) {
  67. if (parentNode.scale) {
  68. invScale /= parentNode.scale[0];
  69. }
  70. parentNode = parentNode.parent;
  71. }
  72. var line = lineGroup.childOfName('line'); // If line not changed
  73. // FIXME Parent scale changed
  74. if (!this.__dirty && !line.__dirty) {
  75. return;
  76. }
  77. var percent = line.shape.percent;
  78. var fromPos = line.pointAt(0);
  79. var toPos = line.pointAt(percent);
  80. var d = vector.sub([], toPos, fromPos);
  81. vector.normalize(d, d);
  82. if (symbolFrom) {
  83. symbolFrom.attr('position', fromPos);
  84. var tangent = line.tangentAt(0);
  85. symbolFrom.attr('rotation', Math.PI / 2 - Math.atan2(tangent[1], tangent[0]));
  86. symbolFrom.attr('scale', [invScale * percent, invScale * percent]);
  87. }
  88. if (symbolTo) {
  89. symbolTo.attr('position', toPos);
  90. var tangent = line.tangentAt(1);
  91. symbolTo.attr('rotation', -Math.PI / 2 - Math.atan2(tangent[1], tangent[0]));
  92. symbolTo.attr('scale', [invScale * percent, invScale * percent]);
  93. }
  94. if (!label.ignore) {
  95. label.attr('position', toPos);
  96. var textPosition;
  97. var textAlign;
  98. var textVerticalAlign;
  99. var distance = 5 * invScale; // End
  100. if (label.__position === 'end') {
  101. textPosition = [d[0] * distance + toPos[0], d[1] * distance + toPos[1]];
  102. textAlign = d[0] > 0.8 ? 'left' : d[0] < -0.8 ? 'right' : 'center';
  103. textVerticalAlign = d[1] > 0.8 ? 'top' : d[1] < -0.8 ? 'bottom' : 'middle';
  104. } // Middle
  105. else if (label.__position === 'middle') {
  106. var halfPercent = percent / 2;
  107. var tangent = line.tangentAt(halfPercent);
  108. var n = [tangent[1], -tangent[0]];
  109. var cp = line.pointAt(halfPercent);
  110. if (n[1] > 0) {
  111. n[0] = -n[0];
  112. n[1] = -n[1];
  113. }
  114. textPosition = [cp[0] + n[0] * distance, cp[1] + n[1] * distance];
  115. textAlign = 'center';
  116. textVerticalAlign = 'bottom';
  117. var rotation = -Math.atan2(tangent[1], tangent[0]);
  118. if (toPos[0] < fromPos[0]) {
  119. rotation = Math.PI + rotation;
  120. }
  121. label.attr('rotation', rotation);
  122. } // Start
  123. else {
  124. textPosition = [-d[0] * distance + fromPos[0], -d[1] * distance + fromPos[1]];
  125. textAlign = d[0] > 0.8 ? 'right' : d[0] < -0.8 ? 'left' : 'center';
  126. textVerticalAlign = d[1] > 0.8 ? 'bottom' : d[1] < -0.8 ? 'top' : 'middle';
  127. }
  128. label.attr({
  129. style: {
  130. // Use the user specified text align and baseline first
  131. textVerticalAlign: label.__verticalAlign || textVerticalAlign,
  132. textAlign: label.__textAlign || textAlign
  133. },
  134. position: textPosition,
  135. scale: [invScale, invScale]
  136. });
  137. }
  138. }
  139. /**
  140. * @constructor
  141. * @extends {module:zrender/graphic/Group}
  142. * @alias {module:echarts/chart/helper/Line}
  143. */
  144. function Line(lineData, idx, seriesScope) {
  145. graphic.Group.call(this);
  146. this._createLine(lineData, idx, seriesScope);
  147. }
  148. var lineProto = Line.prototype; // Update symbol position and rotation
  149. lineProto.beforeUpdate = updateSymbolAndLabelBeforeLineUpdate;
  150. lineProto._createLine = function (lineData, idx, seriesScope) {
  151. var seriesModel = lineData.hostModel;
  152. var linePoints = lineData.getItemLayout(idx);
  153. var line = createLine(linePoints);
  154. line.shape.percent = 0;
  155. graphic.initProps(line, {
  156. shape: {
  157. percent: 1
  158. }
  159. }, seriesModel, idx);
  160. this.add(line);
  161. var label = new graphic.Text({
  162. name: 'label'
  163. });
  164. this.add(label);
  165. zrUtil.each(SYMBOL_CATEGORIES, function (symbolCategory) {
  166. var symbol = createSymbol(symbolCategory, lineData, idx); // symbols must added after line to make sure
  167. // it will be updated after line#update.
  168. // Or symbol position and rotation update in line#beforeUpdate will be one frame slow
  169. this.add(symbol);
  170. this[makeSymbolTypeKey(symbolCategory)] = lineData.getItemVisual(idx, symbolCategory);
  171. }, this);
  172. this._updateCommonStl(lineData, idx, seriesScope);
  173. };
  174. lineProto.updateData = function (lineData, idx, seriesScope) {
  175. var seriesModel = lineData.hostModel;
  176. var line = this.childOfName('line');
  177. var linePoints = lineData.getItemLayout(idx);
  178. var target = {
  179. shape: {}
  180. };
  181. setLinePoints(target.shape, linePoints);
  182. graphic.updateProps(line, target, seriesModel, idx);
  183. zrUtil.each(SYMBOL_CATEGORIES, function (symbolCategory) {
  184. var symbolType = lineData.getItemVisual(idx, symbolCategory);
  185. var key = makeSymbolTypeKey(symbolCategory); // Symbol changed
  186. if (this[key] !== symbolType) {
  187. this.remove(this.childOfName(symbolCategory));
  188. var symbol = createSymbol(symbolCategory, lineData, idx);
  189. this.add(symbol);
  190. }
  191. this[key] = symbolType;
  192. }, this);
  193. this._updateCommonStl(lineData, idx, seriesScope);
  194. };
  195. lineProto._updateCommonStl = function (lineData, idx, seriesScope) {
  196. var seriesModel = lineData.hostModel;
  197. var line = this.childOfName('line');
  198. var lineStyle = seriesScope && seriesScope.lineStyle;
  199. var hoverLineStyle = seriesScope && seriesScope.hoverLineStyle;
  200. var labelModel = seriesScope && seriesScope.labelModel;
  201. var hoverLabelModel = seriesScope && seriesScope.hoverLabelModel; // Optimization for large dataset
  202. if (!seriesScope || lineData.hasItemOption) {
  203. var itemModel = lineData.getItemModel(idx);
  204. lineStyle = itemModel.getModel('lineStyle.normal').getLineStyle();
  205. hoverLineStyle = itemModel.getModel('lineStyle.emphasis').getLineStyle();
  206. labelModel = itemModel.getModel('label.normal');
  207. hoverLabelModel = itemModel.getModel('label.emphasis');
  208. }
  209. var visualColor = lineData.getItemVisual(idx, 'color');
  210. var visualOpacity = zrUtil.retrieve3(lineData.getItemVisual(idx, 'opacity'), lineStyle.opacity, 1);
  211. line.useStyle(zrUtil.defaults({
  212. strokeNoScale: true,
  213. fill: 'none',
  214. stroke: visualColor,
  215. opacity: visualOpacity
  216. }, lineStyle));
  217. line.hoverStyle = hoverLineStyle; // Update symbol
  218. zrUtil.each(SYMBOL_CATEGORIES, function (symbolCategory) {
  219. var symbol = this.childOfName(symbolCategory);
  220. if (symbol) {
  221. symbol.setColor(visualColor);
  222. symbol.setStyle({
  223. opacity: visualOpacity
  224. });
  225. }
  226. }, this);
  227. var showLabel = labelModel.getShallow('show');
  228. var hoverShowLabel = hoverLabelModel.getShallow('show');
  229. var label = this.childOfName('label');
  230. var defaultLabelColor;
  231. var defaultText;
  232. var normalText;
  233. var emphasisText;
  234. if (showLabel || hoverShowLabel) {
  235. var rawVal = seriesModel.getRawValue(idx);
  236. defaultText = rawVal == null ? defaultText = lineData.getName(idx) : isFinite(rawVal) ? round(rawVal) : rawVal;
  237. defaultLabelColor = visualColor || '#000';
  238. normalText = zrUtil.retrieve2(seriesModel.getFormattedLabel(idx, 'normal', lineData.dataType), defaultText);
  239. emphasisText = zrUtil.retrieve2(seriesModel.getFormattedLabel(idx, 'emphasis', lineData.dataType), normalText);
  240. } // label.afterUpdate = lineAfterUpdate;
  241. if (showLabel) {
  242. var labelStyle = graphic.setTextStyle(label.style, labelModel, {
  243. text: normalText
  244. }, {
  245. autoColor: defaultLabelColor
  246. });
  247. label.__textAlign = labelStyle.textAlign;
  248. label.__verticalAlign = labelStyle.textVerticalAlign; // 'start', 'middle', 'end'
  249. label.__position = labelModel.get('position') || 'middle';
  250. } else {
  251. label.setStyle('text', null);
  252. }
  253. if (hoverShowLabel) {
  254. // Only these properties supported in this emphasis style here.
  255. label.hoverStyle = {
  256. text: emphasisText,
  257. textFill: hoverLabelModel.getTextColor(true),
  258. // For merging hover style to normal style, do not use
  259. // `hoverLabelModel.getFont()` here.
  260. fontStyle: hoverLabelModel.getShallow('fontStyle'),
  261. fontWeight: hoverLabelModel.getShallow('fontWeight'),
  262. fontSize: hoverLabelModel.getShallow('fontSize'),
  263. fontFamily: hoverLabelModel.getShallow('fontFamily')
  264. };
  265. } else {
  266. label.hoverStyle = {
  267. text: null
  268. };
  269. }
  270. label.ignore = !showLabel && !hoverShowLabel;
  271. graphic.setHoverStyle(this);
  272. };
  273. lineProto.highlight = function () {
  274. this.trigger('emphasis');
  275. };
  276. lineProto.downplay = function () {
  277. this.trigger('normal');
  278. };
  279. lineProto.updateLayout = function (lineData, idx) {
  280. this.setLinePoints(lineData.getItemLayout(idx));
  281. };
  282. lineProto.setLinePoints = function (points) {
  283. var linePath = this.childOfName('line');
  284. setLinePoints(linePath.shape, points);
  285. linePath.dirty();
  286. };
  287. zrUtil.inherits(Line, graphic.Group);
  288. var _default = Line;
  289. module.exports = _default;