funnelLayout.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  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 { isFunction } from 'zrender/lib/core/util.js';
  43. function getSortedIndices(data, sort) {
  44. var valueDim = data.mapDimension('value');
  45. var valueArr = data.mapArray(valueDim, function (val) {
  46. return val;
  47. });
  48. var indices = [];
  49. var isAscending = sort === 'ascending';
  50. for (var i = 0, len = data.count(); i < len; i++) {
  51. indices[i] = i;
  52. }
  53. // Add custom sortable function & none sortable opetion by "options.sort"
  54. if (isFunction(sort)) {
  55. indices.sort(sort);
  56. } else if (sort !== 'none') {
  57. indices.sort(function (a, b) {
  58. return isAscending ? valueArr[a] - valueArr[b] : valueArr[b] - valueArr[a];
  59. });
  60. }
  61. return indices;
  62. }
  63. function labelLayout(data) {
  64. var seriesModel = data.hostModel;
  65. var orient = seriesModel.get('orient');
  66. data.each(function (idx) {
  67. var itemModel = data.getItemModel(idx);
  68. var labelModel = itemModel.getModel('label');
  69. var labelPosition = labelModel.get('position');
  70. var labelLineModel = itemModel.getModel('labelLine');
  71. var layout = data.getItemLayout(idx);
  72. var points = layout.points;
  73. var isLabelInside = labelPosition === 'inner' || labelPosition === 'inside' || labelPosition === 'center' || labelPosition === 'insideLeft' || labelPosition === 'insideRight';
  74. var textAlign;
  75. var textX;
  76. var textY;
  77. var linePoints;
  78. if (isLabelInside) {
  79. if (labelPosition === 'insideLeft') {
  80. textX = (points[0][0] + points[3][0]) / 2 + 5;
  81. textY = (points[0][1] + points[3][1]) / 2;
  82. textAlign = 'left';
  83. } else if (labelPosition === 'insideRight') {
  84. textX = (points[1][0] + points[2][0]) / 2 - 5;
  85. textY = (points[1][1] + points[2][1]) / 2;
  86. textAlign = 'right';
  87. } else {
  88. textX = (points[0][0] + points[1][0] + points[2][0] + points[3][0]) / 4;
  89. textY = (points[0][1] + points[1][1] + points[2][1] + points[3][1]) / 4;
  90. textAlign = 'center';
  91. }
  92. linePoints = [[textX, textY], [textX, textY]];
  93. } else {
  94. var x1 = void 0;
  95. var y1 = void 0;
  96. var x2 = void 0;
  97. var y2 = void 0;
  98. var labelLineLen = labelLineModel.get('length');
  99. if (process.env.NODE_ENV !== 'production') {
  100. if (orient === 'vertical' && ['top', 'bottom'].indexOf(labelPosition) > -1) {
  101. labelPosition = 'left';
  102. console.warn('Position error: Funnel chart on vertical orient dose not support top and bottom.');
  103. }
  104. if (orient === 'horizontal' && ['left', 'right'].indexOf(labelPosition) > -1) {
  105. labelPosition = 'bottom';
  106. console.warn('Position error: Funnel chart on horizontal orient dose not support left and right.');
  107. }
  108. }
  109. if (labelPosition === 'left') {
  110. // Left side
  111. x1 = (points[3][0] + points[0][0]) / 2;
  112. y1 = (points[3][1] + points[0][1]) / 2;
  113. x2 = x1 - labelLineLen;
  114. textX = x2 - 5;
  115. textAlign = 'right';
  116. } else if (labelPosition === 'right') {
  117. // Right side
  118. x1 = (points[1][0] + points[2][0]) / 2;
  119. y1 = (points[1][1] + points[2][1]) / 2;
  120. x2 = x1 + labelLineLen;
  121. textX = x2 + 5;
  122. textAlign = 'left';
  123. } else if (labelPosition === 'top') {
  124. // Top side
  125. x1 = (points[3][0] + points[0][0]) / 2;
  126. y1 = (points[3][1] + points[0][1]) / 2;
  127. y2 = y1 - labelLineLen;
  128. textY = y2 - 5;
  129. textAlign = 'center';
  130. } else if (labelPosition === 'bottom') {
  131. // Bottom side
  132. x1 = (points[1][0] + points[2][0]) / 2;
  133. y1 = (points[1][1] + points[2][1]) / 2;
  134. y2 = y1 + labelLineLen;
  135. textY = y2 + 5;
  136. textAlign = 'center';
  137. } else if (labelPosition === 'rightTop') {
  138. // RightTop side
  139. x1 = orient === 'horizontal' ? points[3][0] : points[1][0];
  140. y1 = orient === 'horizontal' ? points[3][1] : points[1][1];
  141. if (orient === 'horizontal') {
  142. y2 = y1 - labelLineLen;
  143. textY = y2 - 5;
  144. textAlign = 'center';
  145. } else {
  146. x2 = x1 + labelLineLen;
  147. textX = x2 + 5;
  148. textAlign = 'top';
  149. }
  150. } else if (labelPosition === 'rightBottom') {
  151. // RightBottom side
  152. x1 = points[2][0];
  153. y1 = points[2][1];
  154. if (orient === 'horizontal') {
  155. y2 = y1 + labelLineLen;
  156. textY = y2 + 5;
  157. textAlign = 'center';
  158. } else {
  159. x2 = x1 + labelLineLen;
  160. textX = x2 + 5;
  161. textAlign = 'bottom';
  162. }
  163. } else if (labelPosition === 'leftTop') {
  164. // LeftTop side
  165. x1 = points[0][0];
  166. y1 = orient === 'horizontal' ? points[0][1] : points[1][1];
  167. if (orient === 'horizontal') {
  168. y2 = y1 - labelLineLen;
  169. textY = y2 - 5;
  170. textAlign = 'center';
  171. } else {
  172. x2 = x1 - labelLineLen;
  173. textX = x2 - 5;
  174. textAlign = 'right';
  175. }
  176. } else if (labelPosition === 'leftBottom') {
  177. // LeftBottom side
  178. x1 = orient === 'horizontal' ? points[1][0] : points[3][0];
  179. y1 = orient === 'horizontal' ? points[1][1] : points[2][1];
  180. if (orient === 'horizontal') {
  181. y2 = y1 + labelLineLen;
  182. textY = y2 + 5;
  183. textAlign = 'center';
  184. } else {
  185. x2 = x1 - labelLineLen;
  186. textX = x2 - 5;
  187. textAlign = 'right';
  188. }
  189. } else {
  190. // Right side or Bottom side
  191. x1 = (points[1][0] + points[2][0]) / 2;
  192. y1 = (points[1][1] + points[2][1]) / 2;
  193. if (orient === 'horizontal') {
  194. y2 = y1 + labelLineLen;
  195. textY = y2 + 5;
  196. textAlign = 'center';
  197. } else {
  198. x2 = x1 + labelLineLen;
  199. textX = x2 + 5;
  200. textAlign = 'left';
  201. }
  202. }
  203. if (orient === 'horizontal') {
  204. x2 = x1;
  205. textX = x2;
  206. } else {
  207. y2 = y1;
  208. textY = y2;
  209. }
  210. linePoints = [[x1, y1], [x2, y2]];
  211. }
  212. layout.label = {
  213. linePoints: linePoints,
  214. x: textX,
  215. y: textY,
  216. verticalAlign: 'middle',
  217. textAlign: textAlign,
  218. inside: isLabelInside
  219. };
  220. });
  221. }
  222. export default function funnelLayout(ecModel, api) {
  223. ecModel.eachSeriesByType('funnel', function (seriesModel) {
  224. var data = seriesModel.getData();
  225. var valueDim = data.mapDimension('value');
  226. var sort = seriesModel.get('sort');
  227. var layoutRef = layout.createBoxLayoutReference(seriesModel, api);
  228. var viewRect = layout.getLayoutRect(seriesModel.getBoxLayoutParams(), layoutRef.refContainer);
  229. var orient = seriesModel.get('orient');
  230. var viewWidth = viewRect.width;
  231. var viewHeight = viewRect.height;
  232. var indices = getSortedIndices(data, sort);
  233. var x = viewRect.x;
  234. var y = viewRect.y;
  235. var sizeExtent = orient === 'horizontal' ? [parsePercent(seriesModel.get('minSize'), viewHeight), parsePercent(seriesModel.get('maxSize'), viewHeight)] : [parsePercent(seriesModel.get('minSize'), viewWidth), parsePercent(seriesModel.get('maxSize'), viewWidth)];
  236. var dataExtent = data.getDataExtent(valueDim);
  237. var min = seriesModel.get('min');
  238. var max = seriesModel.get('max');
  239. if (min == null) {
  240. min = Math.min(dataExtent[0], 0);
  241. }
  242. if (max == null) {
  243. max = dataExtent[1];
  244. }
  245. var funnelAlign = seriesModel.get('funnelAlign');
  246. var gap = seriesModel.get('gap');
  247. var viewSize = orient === 'horizontal' ? viewWidth : viewHeight;
  248. var itemSize = (viewSize - gap * (data.count() - 1)) / data.count();
  249. var getLinePoints = function (idx, offset) {
  250. // End point index is data.count() and we assign it 0
  251. if (orient === 'horizontal') {
  252. var val_1 = data.get(valueDim, idx) || 0;
  253. var itemHeight = linearMap(val_1, [min, max], sizeExtent, true);
  254. var y0 = void 0;
  255. switch (funnelAlign) {
  256. case 'top':
  257. y0 = y;
  258. break;
  259. case 'center':
  260. y0 = y + (viewHeight - itemHeight) / 2;
  261. break;
  262. case 'bottom':
  263. y0 = y + (viewHeight - itemHeight);
  264. break;
  265. }
  266. return [[offset, y0], [offset, y0 + itemHeight]];
  267. }
  268. var val = data.get(valueDim, idx) || 0;
  269. var itemWidth = linearMap(val, [min, max], sizeExtent, true);
  270. var x0;
  271. switch (funnelAlign) {
  272. case 'left':
  273. x0 = x;
  274. break;
  275. case 'center':
  276. x0 = x + (viewWidth - itemWidth) / 2;
  277. break;
  278. case 'right':
  279. x0 = x + viewWidth - itemWidth;
  280. break;
  281. }
  282. return [[x0, offset], [x0 + itemWidth, offset]];
  283. };
  284. if (sort === 'ascending') {
  285. // From bottom to top
  286. itemSize = -itemSize;
  287. gap = -gap;
  288. if (orient === 'horizontal') {
  289. x += viewWidth;
  290. } else {
  291. y += viewHeight;
  292. }
  293. indices = indices.reverse();
  294. }
  295. for (var i = 0; i < indices.length; i++) {
  296. var idx = indices[i];
  297. var nextIdx = indices[i + 1];
  298. var itemModel = data.getItemModel(idx);
  299. if (orient === 'horizontal') {
  300. var width = itemModel.get(['itemStyle', 'width']);
  301. if (width == null) {
  302. width = itemSize;
  303. } else {
  304. width = parsePercent(width, viewWidth);
  305. if (sort === 'ascending') {
  306. width = -width;
  307. }
  308. }
  309. var start = getLinePoints(idx, x);
  310. var end = getLinePoints(nextIdx, x + width);
  311. x += width + gap;
  312. data.setItemLayout(idx, {
  313. points: start.concat(end.slice().reverse())
  314. });
  315. } else {
  316. var height = itemModel.get(['itemStyle', 'height']);
  317. if (height == null) {
  318. height = itemSize;
  319. } else {
  320. height = parsePercent(height, viewHeight);
  321. if (sort === 'ascending') {
  322. height = -height;
  323. }
  324. }
  325. var start = getLinePoints(idx, y);
  326. var end = getLinePoints(nextIdx, y + height);
  327. y += height + gap;
  328. data.setItemLayout(idx, {
  329. points: start.concat(end.slice().reverse())
  330. });
  331. }
  332. }
  333. labelLayout(data);
  334. });
  335. }