funnelLayout.js 12 KB

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