CandlestickView.js 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  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 zrUtil = require("zrender/lib/core/util");
  20. var ChartView = require("../../view/Chart");
  21. var graphic = require("../../util/graphic");
  22. var Path = require("zrender/lib/graphic/Path");
  23. var _createClipPathFromCoordSys = require("../helper/createClipPathFromCoordSys");
  24. var createClipPath = _createClipPathFromCoordSys.createClipPath;
  25. /*
  26. * Licensed to the Apache Software Foundation (ASF) under one
  27. * or more contributor license agreements. See the NOTICE file
  28. * distributed with this work for additional information
  29. * regarding copyright ownership. The ASF licenses this file
  30. * to you under the Apache License, Version 2.0 (the
  31. * "License"); you may not use this file except in compliance
  32. * with the License. You may obtain a copy of the License at
  33. *
  34. * http://www.apache.org/licenses/LICENSE-2.0
  35. *
  36. * Unless required by applicable law or agreed to in writing,
  37. * software distributed under the License is distributed on an
  38. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  39. * KIND, either express or implied. See the License for the
  40. * specific language governing permissions and limitations
  41. * under the License.
  42. */
  43. var NORMAL_ITEM_STYLE_PATH = ['itemStyle'];
  44. var EMPHASIS_ITEM_STYLE_PATH = ['emphasis', 'itemStyle'];
  45. var SKIP_PROPS = ['color', 'color0', 'borderColor', 'borderColor0'];
  46. var CandlestickView = ChartView.extend({
  47. type: 'candlestick',
  48. render: function (seriesModel, ecModel, api) {
  49. // If there is clipPath created in large mode. Remove it.
  50. this.group.removeClipPath();
  51. this._updateDrawMode(seriesModel);
  52. this._isLargeDraw ? this._renderLarge(seriesModel) : this._renderNormal(seriesModel);
  53. },
  54. incrementalPrepareRender: function (seriesModel, ecModel, api) {
  55. this._clear();
  56. this._updateDrawMode(seriesModel);
  57. },
  58. incrementalRender: function (params, seriesModel, ecModel, api) {
  59. this._isLargeDraw ? this._incrementalRenderLarge(params, seriesModel) : this._incrementalRenderNormal(params, seriesModel);
  60. },
  61. _updateDrawMode: function (seriesModel) {
  62. var isLargeDraw = seriesModel.pipelineContext.large;
  63. if (this._isLargeDraw == null || isLargeDraw ^ this._isLargeDraw) {
  64. this._isLargeDraw = isLargeDraw;
  65. this._clear();
  66. }
  67. },
  68. _renderNormal: function (seriesModel) {
  69. var data = seriesModel.getData();
  70. var oldData = this._data;
  71. var group = this.group;
  72. var isSimpleBox = data.getLayout('isSimpleBox');
  73. var needsClip = seriesModel.get('clip', true);
  74. var coord = seriesModel.coordinateSystem;
  75. var clipArea = coord.getArea && coord.getArea(); // There is no old data only when first rendering or switching from
  76. // stream mode to normal mode, where previous elements should be removed.
  77. if (!this._data) {
  78. group.removeAll();
  79. }
  80. data.diff(oldData).add(function (newIdx) {
  81. if (data.hasValue(newIdx)) {
  82. var el;
  83. var itemLayout = data.getItemLayout(newIdx);
  84. if (needsClip && isNormalBoxClipped(clipArea, itemLayout)) {
  85. return;
  86. }
  87. el = createNormalBox(itemLayout, newIdx, true);
  88. graphic.initProps(el, {
  89. shape: {
  90. points: itemLayout.ends
  91. }
  92. }, seriesModel, newIdx);
  93. setBoxCommon(el, data, newIdx, isSimpleBox);
  94. group.add(el);
  95. data.setItemGraphicEl(newIdx, el);
  96. }
  97. }).update(function (newIdx, oldIdx) {
  98. var el = oldData.getItemGraphicEl(oldIdx); // Empty data
  99. if (!data.hasValue(newIdx)) {
  100. group.remove(el);
  101. return;
  102. }
  103. var itemLayout = data.getItemLayout(newIdx);
  104. if (needsClip && isNormalBoxClipped(clipArea, itemLayout)) {
  105. group.remove(el);
  106. return;
  107. }
  108. if (!el) {
  109. el = createNormalBox(itemLayout, newIdx);
  110. } else {
  111. graphic.updateProps(el, {
  112. shape: {
  113. points: itemLayout.ends
  114. }
  115. }, seriesModel, newIdx);
  116. }
  117. setBoxCommon(el, data, newIdx, isSimpleBox);
  118. group.add(el);
  119. data.setItemGraphicEl(newIdx, el);
  120. }).remove(function (oldIdx) {
  121. var el = oldData.getItemGraphicEl(oldIdx);
  122. el && group.remove(el);
  123. }).execute();
  124. this._data = data;
  125. },
  126. _renderLarge: function (seriesModel) {
  127. this._clear();
  128. createLarge(seriesModel, this.group);
  129. var clipPath = seriesModel.get('clip', true) ? createClipPath(seriesModel.coordinateSystem, false, seriesModel) : null;
  130. if (clipPath) {
  131. this.group.setClipPath(clipPath);
  132. } else {
  133. this.group.removeClipPath();
  134. }
  135. },
  136. _incrementalRenderNormal: function (params, seriesModel) {
  137. var data = seriesModel.getData();
  138. var isSimpleBox = data.getLayout('isSimpleBox');
  139. var dataIndex;
  140. while ((dataIndex = params.next()) != null) {
  141. var el;
  142. var itemLayout = data.getItemLayout(dataIndex);
  143. el = createNormalBox(itemLayout, dataIndex);
  144. setBoxCommon(el, data, dataIndex, isSimpleBox);
  145. el.incremental = true;
  146. this.group.add(el);
  147. }
  148. },
  149. _incrementalRenderLarge: function (params, seriesModel) {
  150. createLarge(seriesModel, this.group, true);
  151. },
  152. remove: function (ecModel) {
  153. this._clear();
  154. },
  155. _clear: function () {
  156. this.group.removeAll();
  157. this._data = null;
  158. },
  159. dispose: zrUtil.noop
  160. });
  161. var NormalBoxPath = Path.extend({
  162. type: 'normalCandlestickBox',
  163. shape: {},
  164. buildPath: function (ctx, shape) {
  165. var ends = shape.points;
  166. if (this.__simpleBox) {
  167. ctx.moveTo(ends[4][0], ends[4][1]);
  168. ctx.lineTo(ends[6][0], ends[6][1]);
  169. } else {
  170. ctx.moveTo(ends[0][0], ends[0][1]);
  171. ctx.lineTo(ends[1][0], ends[1][1]);
  172. ctx.lineTo(ends[2][0], ends[2][1]);
  173. ctx.lineTo(ends[3][0], ends[3][1]);
  174. ctx.closePath();
  175. ctx.moveTo(ends[4][0], ends[4][1]);
  176. ctx.lineTo(ends[5][0], ends[5][1]);
  177. ctx.moveTo(ends[6][0], ends[6][1]);
  178. ctx.lineTo(ends[7][0], ends[7][1]);
  179. }
  180. }
  181. });
  182. function createNormalBox(itemLayout, dataIndex, isInit) {
  183. var ends = itemLayout.ends;
  184. return new NormalBoxPath({
  185. shape: {
  186. points: isInit ? transInit(ends, itemLayout) : ends
  187. },
  188. z2: 100
  189. });
  190. }
  191. function isNormalBoxClipped(clipArea, itemLayout) {
  192. var clipped = true;
  193. for (var i = 0; i < itemLayout.ends.length; i++) {
  194. // If any point are in the region.
  195. if (clipArea.contain(itemLayout.ends[i][0], itemLayout.ends[i][1])) {
  196. clipped = false;
  197. break;
  198. }
  199. }
  200. return clipped;
  201. }
  202. function setBoxCommon(el, data, dataIndex, isSimpleBox) {
  203. var itemModel = data.getItemModel(dataIndex);
  204. var normalItemStyleModel = itemModel.getModel(NORMAL_ITEM_STYLE_PATH);
  205. var color = data.getItemVisual(dataIndex, 'color');
  206. var borderColor = data.getItemVisual(dataIndex, 'borderColor') || color; // Color must be excluded.
  207. // Because symbol provide setColor individually to set fill and stroke
  208. var itemStyle = normalItemStyleModel.getItemStyle(SKIP_PROPS);
  209. el.useStyle(itemStyle);
  210. el.style.strokeNoScale = true;
  211. el.style.fill = color;
  212. el.style.stroke = borderColor;
  213. el.__simpleBox = isSimpleBox;
  214. var hoverStyle = itemModel.getModel(EMPHASIS_ITEM_STYLE_PATH).getItemStyle();
  215. graphic.setHoverStyle(el, hoverStyle);
  216. }
  217. function transInit(points, itemLayout) {
  218. return zrUtil.map(points, function (point) {
  219. point = point.slice();
  220. point[1] = itemLayout.initBaseline;
  221. return point;
  222. });
  223. }
  224. var LargeBoxPath = Path.extend({
  225. type: 'largeCandlestickBox',
  226. shape: {},
  227. buildPath: function (ctx, shape) {
  228. // Drawing lines is more efficient than drawing
  229. // a whole line or drawing rects.
  230. var points = shape.points;
  231. for (var i = 0; i < points.length;) {
  232. if (this.__sign === points[i++]) {
  233. var x = points[i++];
  234. ctx.moveTo(x, points[i++]);
  235. ctx.lineTo(x, points[i++]);
  236. } else {
  237. i += 3;
  238. }
  239. }
  240. }
  241. });
  242. function createLarge(seriesModel, group, incremental) {
  243. var data = seriesModel.getData();
  244. var largePoints = data.getLayout('largePoints');
  245. var elP = new LargeBoxPath({
  246. shape: {
  247. points: largePoints
  248. },
  249. __sign: 1
  250. });
  251. group.add(elP);
  252. var elN = new LargeBoxPath({
  253. shape: {
  254. points: largePoints
  255. },
  256. __sign: -1
  257. });
  258. group.add(elN);
  259. setLargeStyle(1, elP, seriesModel, data);
  260. setLargeStyle(-1, elN, seriesModel, data);
  261. if (incremental) {
  262. elP.incremental = true;
  263. elN.incremental = true;
  264. }
  265. }
  266. function setLargeStyle(sign, el, seriesModel, data) {
  267. var suffix = sign > 0 ? 'P' : 'N';
  268. var borderColor = data.getVisual('borderColor' + suffix) || data.getVisual('color' + suffix); // Color must be excluded.
  269. // Because symbol provide setColor individually to set fill and stroke
  270. var itemStyle = seriesModel.getModel(NORMAL_ITEM_STYLE_PATH).getItemStyle(SKIP_PROPS);
  271. el.useStyle(itemStyle);
  272. el.style.fill = null;
  273. el.style.stroke = borderColor; // No different
  274. // el.style.lineWidth = .5;
  275. }
  276. var _default = CandlestickView;
  277. module.exports = _default;