funnelLayout.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  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 layout = require("../../util/layout");
  20. var _number = require("../../util/number");
  21. var parsePercent = _number.parsePercent;
  22. var linearMap = _number.linearMap;
  23. /*
  24. * Licensed to the Apache Software Foundation (ASF) under one
  25. * or more contributor license agreements. See the NOTICE file
  26. * distributed with this work for additional information
  27. * regarding copyright ownership. The ASF licenses this file
  28. * to you under the Apache License, Version 2.0 (the
  29. * "License"); you may not use this file except in compliance
  30. * with the License. You may obtain a copy of the License at
  31. *
  32. * http://www.apache.org/licenses/LICENSE-2.0
  33. *
  34. * Unless required by applicable law or agreed to in writing,
  35. * software distributed under the License is distributed on an
  36. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  37. * KIND, either express or implied. See the License for the
  38. * specific language governing permissions and limitations
  39. * under the License.
  40. */
  41. function getViewRect(seriesModel, api) {
  42. return layout.getLayoutRect(seriesModel.getBoxLayoutParams(), {
  43. width: api.getWidth(),
  44. height: api.getHeight()
  45. });
  46. }
  47. function getSortedIndices(data, sort) {
  48. var valueDim = data.mapDimension('value');
  49. var valueArr = data.mapArray(valueDim, function (val) {
  50. return val;
  51. });
  52. var indices = [];
  53. var isAscending = sort === 'ascending';
  54. for (var i = 0, len = data.count(); i < len; i++) {
  55. indices[i] = i;
  56. } // Add custom sortable function & none sortable opetion by "options.sort"
  57. if (typeof sort === 'function') {
  58. indices.sort(sort);
  59. } else if (sort !== 'none') {
  60. indices.sort(function (a, b) {
  61. return isAscending ? valueArr[a] - valueArr[b] : valueArr[b] - valueArr[a];
  62. });
  63. }
  64. return indices;
  65. }
  66. function labelLayout(data) {
  67. data.each(function (idx) {
  68. var itemModel = data.getItemModel(idx);
  69. var labelModel = itemModel.getModel('label');
  70. var labelPosition = labelModel.get('position');
  71. var labelLineModel = itemModel.getModel('labelLine');
  72. var layout = data.getItemLayout(idx);
  73. var points = layout.points;
  74. var isLabelInside = labelPosition === 'inner' || labelPosition === 'inside' || labelPosition === 'center' || labelPosition === 'insideLeft' || labelPosition === 'insideRight';
  75. var textAlign;
  76. var textX;
  77. var textY;
  78. var linePoints;
  79. if (isLabelInside) {
  80. if (labelPosition === 'insideLeft') {
  81. textX = (points[0][0] + points[3][0]) / 2 + 5;
  82. textY = (points[0][1] + points[3][1]) / 2;
  83. textAlign = 'left';
  84. } else if (labelPosition === 'insideRight') {
  85. textX = (points[1][0] + points[2][0]) / 2 - 5;
  86. textY = (points[1][1] + points[2][1]) / 2;
  87. textAlign = 'right';
  88. } else {
  89. textX = (points[0][0] + points[1][0] + points[2][0] + points[3][0]) / 4;
  90. textY = (points[0][1] + points[1][1] + points[2][1] + points[3][1]) / 4;
  91. textAlign = 'center';
  92. }
  93. linePoints = [[textX, textY], [textX, textY]];
  94. } else {
  95. var x1;
  96. var y1;
  97. var x2;
  98. var labelLineLen = labelLineModel.get('length');
  99. if (labelPosition === 'left') {
  100. // Left side
  101. x1 = (points[3][0] + points[0][0]) / 2;
  102. y1 = (points[3][1] + points[0][1]) / 2;
  103. x2 = x1 - labelLineLen;
  104. textX = x2 - 5;
  105. textAlign = 'right';
  106. } else if (labelPosition === 'right') {
  107. // Right side
  108. x1 = (points[1][0] + points[2][0]) / 2;
  109. y1 = (points[1][1] + points[2][1]) / 2;
  110. x2 = x1 + labelLineLen;
  111. textX = x2 + 5;
  112. textAlign = 'left';
  113. } else if (labelPosition === 'rightTop') {
  114. // RightTop side
  115. x1 = points[1][0];
  116. y1 = points[1][1];
  117. x2 = x1 + labelLineLen;
  118. textX = x2 + 5;
  119. textAlign = 'top';
  120. } else if (labelPosition === 'rightBottom') {
  121. // RightBottom side
  122. x1 = points[2][0];
  123. y1 = points[2][1];
  124. x2 = x1 + labelLineLen;
  125. textX = x2 + 5;
  126. textAlign = 'bottom';
  127. } else if (labelPosition === 'leftTop') {
  128. // LeftTop side
  129. x1 = points[0][0];
  130. y1 = points[1][1];
  131. x2 = x1 - labelLineLen;
  132. textX = x2 - 5;
  133. textAlign = 'right';
  134. } else if (labelPosition === 'leftBottom') {
  135. // LeftBottom side
  136. x1 = points[3][0];
  137. y1 = points[2][1];
  138. x2 = x1 - labelLineLen;
  139. textX = x2 - 5;
  140. textAlign = 'right';
  141. } else {
  142. // Right side
  143. x1 = (points[1][0] + points[2][0]) / 2;
  144. y1 = (points[1][1] + points[2][1]) / 2;
  145. x2 = x1 + labelLineLen;
  146. textX = x2 + 5;
  147. textAlign = 'left';
  148. }
  149. var y2 = y1;
  150. linePoints = [[x1, y1], [x2, y2]];
  151. textY = y2;
  152. }
  153. layout.label = {
  154. linePoints: linePoints,
  155. x: textX,
  156. y: textY,
  157. verticalAlign: 'middle',
  158. textAlign: textAlign,
  159. inside: isLabelInside
  160. };
  161. });
  162. }
  163. function _default(ecModel, api, payload) {
  164. ecModel.eachSeriesByType('funnel', function (seriesModel) {
  165. var data = seriesModel.getData();
  166. var valueDim = data.mapDimension('value');
  167. var sort = seriesModel.get('sort');
  168. var viewRect = getViewRect(seriesModel, api);
  169. var indices = getSortedIndices(data, sort);
  170. var sizeExtent = [parsePercent(seriesModel.get('minSize'), viewRect.width), parsePercent(seriesModel.get('maxSize'), viewRect.width)];
  171. var dataExtent = data.getDataExtent(valueDim);
  172. var min = seriesModel.get('min');
  173. var max = seriesModel.get('max');
  174. if (min == null) {
  175. min = Math.min(dataExtent[0], 0);
  176. }
  177. if (max == null) {
  178. max = dataExtent[1];
  179. }
  180. var funnelAlign = seriesModel.get('funnelAlign');
  181. var gap = seriesModel.get('gap');
  182. var itemHeight = (viewRect.height - gap * (data.count() - 1)) / data.count();
  183. var y = viewRect.y;
  184. var getLinePoints = function (idx, offY) {
  185. // End point index is data.count() and we assign it 0
  186. var val = data.get(valueDim, idx) || 0;
  187. var itemWidth = linearMap(val, [min, max], sizeExtent, true);
  188. var x0;
  189. switch (funnelAlign) {
  190. case 'left':
  191. x0 = viewRect.x;
  192. break;
  193. case 'center':
  194. x0 = viewRect.x + (viewRect.width - itemWidth) / 2;
  195. break;
  196. case 'right':
  197. x0 = viewRect.x + viewRect.width - itemWidth;
  198. break;
  199. }
  200. return [[x0, offY], [x0 + itemWidth, offY]];
  201. };
  202. if (sort === 'ascending') {
  203. // From bottom to top
  204. itemHeight = -itemHeight;
  205. gap = -gap;
  206. y += viewRect.height;
  207. indices = indices.reverse();
  208. }
  209. for (var i = 0; i < indices.length; i++) {
  210. var idx = indices[i];
  211. var nextIdx = indices[i + 1];
  212. var itemModel = data.getItemModel(idx);
  213. var height = itemModel.get('itemStyle.height');
  214. if (height == null) {
  215. height = itemHeight;
  216. } else {
  217. height = parsePercent(height, viewRect.height);
  218. if (sort === 'ascending') {
  219. height = -height;
  220. }
  221. }
  222. var start = getLinePoints(idx, y);
  223. var end = getLinePoints(nextIdx, y + height);
  224. y += height + gap;
  225. data.setItemLayout(idx, {
  226. points: start.concat(end.slice().reverse())
  227. });
  228. }
  229. labelLayout(data);
  230. });
  231. }
  232. module.exports = _default;