funnelLayout.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  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. /**
  20. * AUTO-GENERATED FILE. DO NOT MODIFY.
  21. */
  22. /*
  23. * Licensed to the Apache Software Foundation (ASF) under one
  24. * or more contributor license agreements. See the NOTICE file
  25. * distributed with this work for additional information
  26. * regarding copyright ownership. The ASF licenses this file
  27. * to you under the Apache License, Version 2.0 (the
  28. * "License"); you may not use this file except in compliance
  29. * with the License. You may obtain a copy of the License at
  30. *
  31. * http://www.apache.org/licenses/LICENSE-2.0
  32. *
  33. * Unless required by applicable law or agreed to in writing,
  34. * software distributed under the License is distributed on an
  35. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  36. * KIND, either express or implied. See the License for the
  37. * specific language governing permissions and limitations
  38. * under the License.
  39. */
  40. import * as layout from '../../util/layout.js';
  41. import { parsePercent, linearMap } from '../../util/number.js';
  42. import { SERIES_TYPE_FUNNEL } from './FunnelSeries.js';
  43. import { indexOf, isFunction, isString } from 'zrender/lib/core/util.js';
  44. import { createSimpleOverallStageHandler } from '../../util/model.js';
  45. function getSortedIndices(data, sort) {
  46. var valueDim = data.mapDimension('value');
  47. var valueArr = data.mapArray(valueDim, function (val) {
  48. return val;
  49. });
  50. var indices = [];
  51. var isAscending = sort === 'ascending';
  52. for (var i = 0, len = data.count(); i < len; i++) {
  53. indices[i] = i;
  54. }
  55. // Add custom sortable function & none sortable opetion by "options.sort"
  56. if (isFunction(sort)) {
  57. indices.sort(sort);
  58. } else if (sort !== 'none') {
  59. indices.sort(function (a, b) {
  60. return isAscending ? valueArr[a] - valueArr[b] : valueArr[b] - valueArr[a];
  61. });
  62. }
  63. return indices;
  64. }
  65. function labelLayout(data) {
  66. var seriesModel = data.hostModel;
  67. var isHorizontal = isOrientHorizontal(seriesModel);
  68. data.each(function (idx) {
  69. var itemModel = data.getItemModel(idx);
  70. var labelModel = itemModel.getModel('label');
  71. var labelPosition = labelModel.get('position');
  72. var labelLineModel = itemModel.getModel('labelLine');
  73. var layout = data.getItemLayout(idx);
  74. var points = layout.points;
  75. var isLabelInside = labelPosition === 'inner' || labelPosition === 'inside' || labelPosition === 'center' || labelPosition === 'insideLeft' || labelPosition === 'insideRight';
  76. var textAlign;
  77. var textX;
  78. var textY;
  79. var linePoints;
  80. if (isLabelInside) {
  81. if (labelPosition === 'insideLeft') {
  82. textX = (points[0][0] + points[3][0]) / 2 + 5;
  83. textY = (points[0][1] + points[3][1]) / 2;
  84. textAlign = 'left';
  85. } else if (labelPosition === 'insideRight') {
  86. textX = (points[1][0] + points[2][0]) / 2 - 5;
  87. textY = (points[1][1] + points[2][1]) / 2;
  88. textAlign = 'right';
  89. } else {
  90. textX = (points[0][0] + points[1][0] + points[2][0] + points[3][0]) / 4;
  91. textY = (points[0][1] + points[1][1] + points[2][1] + points[3][1]) / 4;
  92. textAlign = 'center';
  93. }
  94. linePoints = [[textX, textY], [textX, textY]];
  95. } else {
  96. var x1 = void 0;
  97. var y1 = void 0;
  98. var x2 = void 0;
  99. var y2 = void 0;
  100. var labelLineLen = labelLineModel.get('length');
  101. if (isString(labelPosition)) {
  102. if (!isHorizontal && indexOf(['top', 'bottom'], labelPosition) > -1) {
  103. labelPosition = 'left';
  104. if (process.env.NODE_ENV !== 'production') {
  105. console.warn('Position error: Funnel chart on vertical orient dose not support top and bottom.');
  106. }
  107. }
  108. if (isHorizontal && indexOf(['left', 'right'], labelPosition) > -1) {
  109. labelPosition = 'bottom';
  110. if (process.env.NODE_ENV !== 'production') {
  111. console.warn('Position error: Funnel chart on horizontal orient dose not support left and right.');
  112. }
  113. }
  114. }
  115. if (labelPosition === 'left') {
  116. // Left side
  117. x1 = (points[3][0] + points[0][0]) / 2;
  118. y1 = (points[3][1] + points[0][1]) / 2;
  119. x2 = x1 - labelLineLen;
  120. textX = x2 - 5;
  121. textAlign = 'right';
  122. } else if (labelPosition === 'right') {
  123. // Right side
  124. x1 = (points[1][0] + points[2][0]) / 2;
  125. y1 = (points[1][1] + points[2][1]) / 2;
  126. x2 = x1 + labelLineLen;
  127. textX = x2 + 5;
  128. textAlign = 'left';
  129. } else if (labelPosition === 'top') {
  130. // Top side
  131. x1 = (points[3][0] + points[0][0]) / 2;
  132. y1 = (points[3][1] + points[0][1]) / 2;
  133. y2 = y1 - labelLineLen;
  134. textY = y2 - 5;
  135. textAlign = 'center';
  136. } else if (labelPosition === 'bottom') {
  137. // Bottom side
  138. x1 = (points[1][0] + points[2][0]) / 2;
  139. y1 = (points[1][1] + points[2][1]) / 2;
  140. y2 = y1 + labelLineLen;
  141. textY = y2 + 5;
  142. textAlign = 'center';
  143. } else if (labelPosition === 'rightTop') {
  144. // RightTop side
  145. x1 = isHorizontal ? points[3][0] : points[1][0];
  146. y1 = isHorizontal ? points[3][1] : points[1][1];
  147. if (isHorizontal) {
  148. y2 = y1 - labelLineLen;
  149. textY = y2 - 5;
  150. textAlign = 'center';
  151. } else {
  152. x2 = x1 + labelLineLen;
  153. textX = x2 + 5;
  154. textAlign = 'top';
  155. }
  156. } else if (labelPosition === 'rightBottom') {
  157. // RightBottom side
  158. x1 = points[2][0];
  159. y1 = points[2][1];
  160. if (isHorizontal) {
  161. y2 = y1 + labelLineLen;
  162. textY = y2 + 5;
  163. textAlign = 'center';
  164. } else {
  165. x2 = x1 + labelLineLen;
  166. textX = x2 + 5;
  167. textAlign = 'bottom';
  168. }
  169. } else if (labelPosition === 'leftTop') {
  170. // LeftTop side
  171. x1 = points[0][0];
  172. y1 = isHorizontal ? points[0][1] : points[1][1];
  173. if (isHorizontal) {
  174. y2 = y1 - labelLineLen;
  175. textY = y2 - 5;
  176. textAlign = 'center';
  177. } else {
  178. x2 = x1 - labelLineLen;
  179. textX = x2 - 5;
  180. textAlign = 'right';
  181. }
  182. } else if (labelPosition === 'leftBottom') {
  183. // LeftBottom side
  184. x1 = isHorizontal ? points[1][0] : points[3][0];
  185. y1 = isHorizontal ? points[1][1] : points[2][1];
  186. if (isHorizontal) {
  187. y2 = y1 + labelLineLen;
  188. textY = y2 + 5;
  189. textAlign = 'center';
  190. } else {
  191. x2 = x1 - labelLineLen;
  192. textX = x2 - 5;
  193. textAlign = 'right';
  194. }
  195. } else {
  196. // Right side or Bottom side
  197. x1 = (points[1][0] + points[2][0]) / 2;
  198. y1 = (points[1][1] + points[2][1]) / 2;
  199. if (isHorizontal) {
  200. y2 = y1 + labelLineLen;
  201. textY = y2 + 5;
  202. textAlign = 'center';
  203. } else {
  204. x2 = x1 + labelLineLen;
  205. textX = x2 + 5;
  206. textAlign = 'left';
  207. }
  208. }
  209. if (isHorizontal) {
  210. x2 = x1;
  211. textX = x2;
  212. } else {
  213. y2 = y1;
  214. textY = y2;
  215. }
  216. linePoints = [[x1, y1], [x2, y2]];
  217. }
  218. layout.label = {
  219. linePoints: linePoints,
  220. x: textX,
  221. y: textY,
  222. verticalAlign: 'middle',
  223. textAlign: textAlign,
  224. inside: isLabelInside
  225. };
  226. });
  227. }
  228. export var funnelLayoutStageHandler = createSimpleOverallStageHandler(SERIES_TYPE_FUNNEL, funnelLayout);
  229. function funnelLayout(ecModel, api) {
  230. ecModel.eachSeriesByType(SERIES_TYPE_FUNNEL, function (seriesModel) {
  231. var data = seriesModel.getData();
  232. var valueDim = data.mapDimension('value');
  233. var sort = seriesModel.get('sort');
  234. var layoutRef = layout.createBoxLayoutReference(seriesModel, api);
  235. var viewRect = layout.getLayoutRect(seriesModel.getBoxLayoutParams(), layoutRef.refContainer);
  236. var isHorizontal = isOrientHorizontal(seriesModel);
  237. var viewWidth = viewRect.width;
  238. var viewHeight = viewRect.height;
  239. var indices = getSortedIndices(data, sort);
  240. var x = viewRect.x;
  241. var y = viewRect.y;
  242. var sizeExtent = isHorizontal ? [parsePercent(seriesModel.get('minSize'), viewHeight), parsePercent(seriesModel.get('maxSize'), viewHeight)] : [parsePercent(seriesModel.get('minSize'), viewWidth), parsePercent(seriesModel.get('maxSize'), viewWidth)];
  243. var dataExtent = data.getDataExtent(valueDim);
  244. var min = seriesModel.get('min');
  245. var max = seriesModel.get('max');
  246. if (min == null) {
  247. min = Math.min(dataExtent[0], 0);
  248. }
  249. if (max == null) {
  250. max = dataExtent[1];
  251. }
  252. var funnelAlign = seriesModel.get('funnelAlign');
  253. var gap = seriesModel.get('gap');
  254. var viewSize = isHorizontal ? viewWidth : viewHeight;
  255. var itemSize = (viewSize - gap * (data.count() - 1)) / data.count();
  256. var getLinePoints = function (idx, offset) {
  257. // End point index is data.count() and we assign it 0
  258. if (isHorizontal) {
  259. var val_1 = data.get(valueDim, idx) || 0;
  260. var itemHeight = linearMap(val_1, [min, max], sizeExtent, true);
  261. var y0 = void 0;
  262. switch (funnelAlign) {
  263. case 'top':
  264. y0 = y;
  265. break;
  266. case 'center':
  267. y0 = y + (viewHeight - itemHeight) / 2;
  268. break;
  269. case 'bottom':
  270. y0 = y + (viewHeight - itemHeight);
  271. break;
  272. }
  273. return [[offset, y0], [offset, y0 + itemHeight]];
  274. }
  275. var val = data.get(valueDim, idx) || 0;
  276. var itemWidth = linearMap(val, [min, max], sizeExtent, true);
  277. var x0;
  278. switch (funnelAlign) {
  279. case 'left':
  280. x0 = x;
  281. break;
  282. case 'center':
  283. x0 = x + (viewWidth - itemWidth) / 2;
  284. break;
  285. case 'right':
  286. x0 = x + viewWidth - itemWidth;
  287. break;
  288. }
  289. return [[x0, offset], [x0 + itemWidth, offset]];
  290. };
  291. if (sort === 'ascending') {
  292. // From bottom to top
  293. itemSize = -itemSize;
  294. gap = -gap;
  295. if (isHorizontal) {
  296. x += viewWidth;
  297. } else {
  298. y += viewHeight;
  299. }
  300. indices = indices.reverse();
  301. }
  302. for (var i = 0; i < indices.length; i++) {
  303. var idx = indices[i];
  304. var nextIdx = indices[i + 1];
  305. var itemModel = data.getItemModel(idx);
  306. if (isHorizontal) {
  307. var width = itemModel.get(['itemStyle', 'width']);
  308. if (width == null) {
  309. width = itemSize;
  310. } else {
  311. width = parsePercent(width, viewWidth);
  312. if (sort === 'ascending') {
  313. width = -width;
  314. }
  315. }
  316. var start = getLinePoints(idx, x);
  317. var end = getLinePoints(nextIdx, x + width);
  318. x += width + gap;
  319. data.setItemLayout(idx, {
  320. points: start.concat(end.slice().reverse())
  321. });
  322. } else {
  323. var height = itemModel.get(['itemStyle', 'height']);
  324. if (height == null) {
  325. height = itemSize;
  326. } else {
  327. height = parsePercent(height, viewHeight);
  328. if (sort === 'ascending') {
  329. height = -height;
  330. }
  331. }
  332. var start = getLinePoints(idx, y);
  333. var end = getLinePoints(nextIdx, y + height);
  334. y += height + gap;
  335. data.setItemLayout(idx, {
  336. points: start.concat(end.slice().reverse())
  337. });
  338. }
  339. }
  340. labelLayout(data);
  341. });
  342. }
  343. function isOrientHorizontal(seriesModel) {
  344. return seriesModel.get('orient') === 'horizontal';
  345. }