funnelLayout.js 12 KB

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