category.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762
  1. import { __assign, __extends, __rest } from "tslib";
  2. import { clamp, deepMix, each, filter, get, mix, isNumber, isFunction } from '@antv/util';
  3. import { ellipsisLabel } from '../util/label';
  4. import { getMatrixByAngle, getMatrixByTranslate } from '../util/matrix';
  5. import { getStatesStyle } from '../util/state';
  6. import Theme from '../util/theme';
  7. import LegendBase from './base';
  8. /**
  9. * 分页器 默认配置
  10. */
  11. var DEFAULT_PAGE_NAVIGATOR = {
  12. marker: {
  13. style: {
  14. inactiveFill: '#000',
  15. inactiveOpacity: 0.45,
  16. fill: '#000',
  17. opacity: 1,
  18. size: 12,
  19. },
  20. },
  21. text: {
  22. style: {
  23. fill: '#ccc',
  24. fontSize: 12,
  25. },
  26. },
  27. };
  28. // 默认 文本style
  29. var textStyle = {
  30. fill: Theme.textColor,
  31. fontSize: 12,
  32. textAlign: 'start',
  33. textBaseline: 'middle',
  34. fontFamily: Theme.fontFamily,
  35. fontWeight: 'normal',
  36. lineHeight: 12,
  37. };
  38. var RIGHT_ARROW_NAME = 'navigation-arrow-right';
  39. var LEFT_ARROW_NAME = 'navigation-arrow-left';
  40. var ROTATE_MAP = {
  41. right: (90 * Math.PI) / 180,
  42. left: ((360 - 90) * Math.PI) / 180,
  43. up: 0,
  44. down: (180 * Math.PI) / 180,
  45. };
  46. var Category = /** @class */ (function (_super) {
  47. __extends(Category, _super);
  48. function Category() {
  49. var _this = _super !== null && _super.apply(this, arguments) || this;
  50. _this.currentPageIndex = 1;
  51. _this.totalPagesCnt = 1;
  52. _this.pageWidth = 0;
  53. _this.pageHeight = 0;
  54. _this.startX = 0;
  55. _this.startY = 0;
  56. _this.onNavigationBack = function () {
  57. var itemGroup = _this.getElementByLocalId('item-group');
  58. if (_this.currentPageIndex > 1) {
  59. _this.currentPageIndex -= 1;
  60. _this.updateNavigation();
  61. var matrix = _this.getCurrentNavigationMatrix();
  62. if (_this.get('animate')) {
  63. itemGroup.animate({
  64. matrix: matrix,
  65. }, 100);
  66. }
  67. else {
  68. itemGroup.attr({ matrix: matrix });
  69. }
  70. }
  71. };
  72. _this.onNavigationAfter = function () {
  73. var itemGroup = _this.getElementByLocalId('item-group');
  74. if (_this.currentPageIndex < _this.totalPagesCnt) {
  75. _this.currentPageIndex += 1;
  76. _this.updateNavigation();
  77. var matrix = _this.getCurrentNavigationMatrix();
  78. if (_this.get('animate')) {
  79. itemGroup.animate({
  80. matrix: matrix,
  81. }, 100);
  82. }
  83. else {
  84. itemGroup.attr({ matrix: matrix });
  85. }
  86. }
  87. };
  88. return _this;
  89. }
  90. Category.prototype.getDefaultCfg = function () {
  91. var cfg = _super.prototype.getDefaultCfg.call(this);
  92. return __assign(__assign({}, cfg), { name: 'legend', type: 'category', itemSpacing: 24, itemMarginBottom: 8, maxItemWidth: null, itemWidth: null, itemHeight: null, itemName: {}, itemValue: null, maxWidth: null, maxHeight: null, marker: {}, radio: null, items: [], itemStates: {}, itemBackground: {}, pageNavigator: {}, defaultCfg: {
  93. title: {
  94. spacing: 5,
  95. style: {
  96. fill: Theme.textColor,
  97. fontSize: 12,
  98. textAlign: 'start',
  99. textBaseline: 'top',
  100. },
  101. },
  102. background: {
  103. padding: 5,
  104. style: {
  105. stroke: Theme.lineColor,
  106. },
  107. },
  108. itemBackground: {
  109. style: {
  110. opacity: 0,
  111. fill: '#fff',
  112. },
  113. },
  114. pageNavigator: DEFAULT_PAGE_NAVIGATOR,
  115. itemName: {
  116. spacing: 16,
  117. style: textStyle,
  118. },
  119. marker: {
  120. spacing: 8,
  121. style: {
  122. r: 6,
  123. symbol: 'circle',
  124. },
  125. },
  126. itemValue: {
  127. alignRight: false,
  128. formatter: null,
  129. style: textStyle,
  130. spacing: 6,
  131. },
  132. itemStates: {
  133. active: {
  134. nameStyle: {
  135. opacity: 0.8,
  136. },
  137. },
  138. unchecked: {
  139. nameStyle: {
  140. fill: Theme.uncheckedColor,
  141. },
  142. markerStyle: {
  143. fill: Theme.uncheckedColor,
  144. stroke: Theme.uncheckedColor,
  145. },
  146. },
  147. inactive: {
  148. nameStyle: {
  149. fill: Theme.uncheckedColor,
  150. },
  151. markerStyle: {
  152. opacity: 0.2,
  153. },
  154. },
  155. },
  156. } });
  157. };
  158. // 实现 IList 接口
  159. Category.prototype.isList = function () {
  160. return true;
  161. };
  162. /**
  163. * 获取图例项
  164. * @return {ListItem[]} 列表项集合
  165. */
  166. Category.prototype.getItems = function () {
  167. return this.get('items');
  168. };
  169. /**
  170. * 设置列表项
  171. * @param {ListItem[]} items 列表项集合
  172. */
  173. Category.prototype.setItems = function (items) {
  174. this.update({
  175. items: items,
  176. });
  177. };
  178. /**
  179. * 更新列表项
  180. * @param {ListItem} item 列表项
  181. * @param {object} cfg 列表项
  182. */
  183. Category.prototype.updateItem = function (item, cfg) {
  184. mix(item, cfg);
  185. this.clear(); // 由于单个图例项变化,会引起全局变化,所以全部更新
  186. this.render();
  187. };
  188. /**
  189. * 清空列表
  190. */
  191. Category.prototype.clearItems = function () {
  192. var itemGroup = this.getElementByLocalId('item-group');
  193. itemGroup && itemGroup.clear();
  194. };
  195. /**
  196. * 设置列表项的状态
  197. * @param {ListItem} item 列表项
  198. * @param {string} state 状态名
  199. * @param {boolean} value 状态值, true, false
  200. */
  201. Category.prototype.setItemState = function (item, state, value) {
  202. item[state] = value;
  203. var itemElement = this.getElementByLocalId("item-" + item.id);
  204. if (itemElement) {
  205. var items = this.getItems();
  206. var index = items.indexOf(item);
  207. var offsetGroup = this.createOffScreenGroup(); // 离屏的 group
  208. var newElement = this.drawItem(item, index, this.getItemHeight(), offsetGroup);
  209. this.updateElements(newElement, itemElement); // 更新整个分组
  210. this.clearUpdateStatus(itemElement); // 清理更新状态,防止出现 bug
  211. }
  212. };
  213. /**
  214. * 是否存在指定的状态
  215. * @param {ListItem} item 列表项
  216. * @param {boolean} state 状态名
  217. */
  218. Category.prototype.hasState = function (item, state) {
  219. return !!item[state];
  220. };
  221. Category.prototype.getItemStates = function (item) {
  222. var itemStates = this.get('itemStates');
  223. var rst = [];
  224. each(itemStates, function (v, k) {
  225. if (item[k]) {
  226. // item.selected
  227. rst.push(k);
  228. }
  229. });
  230. return rst;
  231. };
  232. /**
  233. * 清楚所有列表项的状态
  234. * @param {string} state 状态值
  235. */
  236. Category.prototype.clearItemsState = function (state) {
  237. var _this = this;
  238. var items = this.getItemsByState(state);
  239. each(items, function (item) {
  240. _this.setItemState(item, state, false);
  241. });
  242. };
  243. /**
  244. * 根据状态获取图例项
  245. * @param {string} state [description]
  246. * @return {ListItem[]} [description]
  247. */
  248. Category.prototype.getItemsByState = function (state) {
  249. var _this = this;
  250. var items = this.getItems();
  251. return filter(items, function (item) {
  252. return _this.hasState(item, state);
  253. });
  254. };
  255. // 绘制 legend 的选项
  256. Category.prototype.drawLegendContent = function (group) {
  257. this.processItems();
  258. this.drawItems(group);
  259. };
  260. // 防止未设置 id
  261. Category.prototype.processItems = function () {
  262. var items = this.get('items');
  263. each(items, function (item) {
  264. if (!item.id) {
  265. // 如果没有设置 id,默认使用 name
  266. item.id = item.name;
  267. }
  268. });
  269. };
  270. // 绘制所有的图例选项
  271. Category.prototype.drawItems = function (group) {
  272. var _this = this;
  273. var itemContainerGroup = this.addGroup(group, {
  274. id: this.getElementId('item-container-group'),
  275. name: 'legend-item-container-group',
  276. });
  277. var itemGroup = this.addGroup(itemContainerGroup, {
  278. id: this.getElementId('item-group'),
  279. name: 'legend-item-group',
  280. });
  281. var itemHeight = this.getItemHeight();
  282. var itemWidth = this.get('itemWidth');
  283. var itemSpacing = this.get('itemSpacing');
  284. var itemMarginBottom = this.get('itemMarginBottom');
  285. var currentPoint = this.get('currentPoint');
  286. var startX = currentPoint.x;
  287. var startY = currentPoint.y;
  288. var layout = this.get('layout');
  289. var items = this.get('items');
  290. var wrapped = false;
  291. var pageWidth = 0;
  292. var maxWidth = this.get('maxWidth'); // 最大宽度,会导致 layout : 'horizontal' 时自动换行
  293. var maxHeight = this.get('maxHeight'); // 最大高度,会导致出现分页
  294. // 暂时不考虑分页
  295. each(items, function (item, index) {
  296. var subGroup = _this.drawItem(item, index, itemHeight, itemGroup);
  297. var bbox = subGroup.getBBox();
  298. var width = itemWidth || bbox.width;
  299. if (width > pageWidth) {
  300. pageWidth = width;
  301. }
  302. if (layout === 'horizontal') {
  303. // 如果水平布局
  304. if (maxWidth && maxWidth < currentPoint.x + width - startX) {
  305. // 检测是否换行
  306. wrapped = true;
  307. currentPoint.x = startX;
  308. currentPoint.y += itemHeight + itemMarginBottom;
  309. }
  310. _this.moveElementTo(subGroup, currentPoint);
  311. currentPoint.x += width + itemSpacing;
  312. }
  313. else {
  314. // 如果垂直布局
  315. if (maxHeight && maxHeight < currentPoint.y + itemHeight + itemMarginBottom - startY) {
  316. // 换行
  317. wrapped = true;
  318. currentPoint.x += pageWidth + itemSpacing;
  319. currentPoint.y = startY;
  320. pageWidth = 0;
  321. }
  322. _this.moveElementTo(subGroup, currentPoint);
  323. currentPoint.y += itemHeight + itemMarginBottom; // itemSpacing 仅影响水平间距
  324. }
  325. });
  326. if (wrapped && this.get('flipPage')) {
  327. this.pageHeight = 0;
  328. this.pageWidth = 0;
  329. this.totalPagesCnt = 1;
  330. this.startX = startX;
  331. this.startY = startY;
  332. this.adjustNavigation(group, itemGroup);
  333. }
  334. };
  335. // 获取图例项的高度,如果未定义,则按照 name 的高度计算
  336. Category.prototype.getItemHeight = function () {
  337. var itemHeight = this.get('itemHeight');
  338. if (!itemHeight) {
  339. var style_1 = (this.get('itemName') || {}).style;
  340. if (isFunction(style_1)) {
  341. var items_1 = this.getItems();
  342. items_1.forEach(function (item, index) {
  343. var fontSize = __assign(__assign({}, textStyle), style_1(item, index, items_1)).fontSize;
  344. if (itemHeight < fontSize) {
  345. itemHeight = fontSize;
  346. }
  347. });
  348. }
  349. else if (style_1) {
  350. itemHeight = style_1.fontSize;
  351. }
  352. }
  353. return itemHeight;
  354. };
  355. // 绘制 marker
  356. Category.prototype.drawMarker = function (container, markerCfg, item, itemHeight) {
  357. var markerAttrs = __assign(__assign(__assign({ x: 0, y: itemHeight / 2 }, markerCfg.style), { symbol: get(item.marker, 'symbol', 'circle') }), get(item.marker, 'style', {}));
  358. var shape = this.addShape(container, {
  359. type: 'marker',
  360. id: this.getElementId("item-" + item.id + "-marker"),
  361. name: 'legend-item-marker',
  362. attrs: markerAttrs,
  363. });
  364. var bbox = shape.getBBox();
  365. shape.attr('x', bbox.width / 2); // marker 需要左对齐,所以不能占用左侧的空间
  366. var _a = shape.attr(), stroke = _a.stroke, fill = _a.fill;
  367. if (stroke) {
  368. shape.set('isStroke', true);
  369. }
  370. if (fill) {
  371. shape.set('isFill', true);
  372. }
  373. return shape;
  374. };
  375. // 绘制文本
  376. Category.prototype.drawItemText = function (container, textName, cfg, item, itemHeight, xPosition, index) {
  377. var formatter = cfg.formatter;
  378. var style = cfg.style;
  379. var attrs = __assign(__assign({ x: xPosition, y: itemHeight / 2, text: formatter ? formatter(item[textName], item, index) : item[textName] }, textStyle), (isFunction(style) ? style(item, index, this.getItems()) : style));
  380. return this.addShape(container, {
  381. type: 'text',
  382. id: this.getElementId("item-" + item.id + "-" + textName),
  383. name: "legend-item-" + textName,
  384. attrs: attrs,
  385. });
  386. };
  387. Category.prototype.drawRadio = function (container, radioCfg, item, itemHeight, x) {
  388. var _a, _b;
  389. var style = radioCfg.style || {};
  390. // 以用户设置的 r 为主
  391. var r = (_a = style.r) !== null && _a !== void 0 ? _a : itemHeight / 2;
  392. var lineWidth = (r * 3.6) / 8;
  393. var _c = [x + r, itemHeight / 2 - r], x0 = _c[0], y0 = _c[1];
  394. var _d = [x0 + r, y0 + r], x1 = _d[0], y1 = _d[1];
  395. var _e = [x0, y1 + r], x2 = _e[0], y2 = _e[1];
  396. var _f = [x, y0 + r], x3 = _f[0], y3 = _f[1];
  397. var showRadio = item.showRadio;
  398. var attrs = __assign(__assign({ path: [
  399. ['M', x0, y0],
  400. ['A', r, r, 0, 0, 1, x1, y1],
  401. ['L', x1 - lineWidth, y1],
  402. ['L', x1, y1],
  403. ['A', r, r, 0, 0, 1, x2, y2],
  404. ['L', x2, y2 - lineWidth],
  405. ['L', x2, y2],
  406. ['A', r, r, 0, 0, 1, x3, y3],
  407. ['L', x3 + lineWidth, y3],
  408. ['L', x3, y3],
  409. ['A', r, r, 0, 0, 1, x0, y0],
  410. ['L', x0, y0 + lineWidth],
  411. ], stroke: '#000000', fill: '#ffffff' }, style), { opacity: showRadio ? ((_b = style === null || style === void 0 ? void 0 : style.opacity) !== null && _b !== void 0 ? _b : 0.45) : 0 });
  412. var radioShape = this.addShape(container, {
  413. type: 'path',
  414. id: this.getElementId("item-" + item.id + "-radio"),
  415. name: 'legend-item-radio',
  416. attrs: attrs,
  417. });
  418. radioShape.set('tip', radioCfg.tip);
  419. return radioShape;
  420. };
  421. // 绘制图例项
  422. Category.prototype.drawItem = function (item, index, itemHeight, itemGroup) {
  423. var groupId = "item-" + item.id;
  424. // 设置单独的 Group 用于 setClip
  425. var subContainer = this.addGroup(itemGroup, {
  426. name: 'legend-item-container',
  427. id: this.getElementId("item-container-" + groupId),
  428. delegateObject: {
  429. item: item,
  430. index: index,
  431. },
  432. });
  433. var subGroup = this.addGroup(subContainer, {
  434. name: 'legend-item',
  435. id: this.getElementId(groupId),
  436. delegateObject: {
  437. item: item,
  438. index: index,
  439. },
  440. });
  441. var marker = this.get('marker');
  442. var itemName = this.get('itemName');
  443. var itemValue = this.get('itemValue');
  444. var itemBackground = this.get('itemBackground');
  445. var radio = this.get('radio');
  446. var itemWidth = this.getLimitItemWidth();
  447. var curX = 0; // 记录当前 x 的位置
  448. if (marker) {
  449. var markerShape = this.drawMarker(subGroup, marker, item, itemHeight);
  450. var spacing = marker.spacing;
  451. var itemMarkerSpacing = get(item, ['marker', 'spacing']);
  452. if (isNumber(itemMarkerSpacing)) {
  453. // 如果 item 有配置 marker.spacing,采用 item 的配置
  454. spacing = itemMarkerSpacing;
  455. }
  456. curX = markerShape.getBBox().maxX + spacing;
  457. }
  458. if (itemName) {
  459. var nameShape = this.drawItemText(subGroup, 'name', itemName, item, itemHeight, curX, index);
  460. if (itemWidth) {
  461. // 设置了 item 的最大宽度限制,并且超出了,进行省略处理
  462. ellipsisLabel(true, nameShape, clamp(itemWidth - curX, 0, itemWidth));
  463. }
  464. curX = nameShape.getBBox().maxX + itemName.spacing;
  465. }
  466. if (itemValue) {
  467. var valueShape = this.drawItemText(subGroup, 'value', itemValue, item, itemHeight, curX, index);
  468. if (itemWidth) {
  469. if (itemValue.alignRight) {
  470. valueShape.attr({
  471. textAlign: 'right',
  472. x: itemWidth,
  473. });
  474. ellipsisLabel(true, valueShape, clamp(itemWidth - curX, 0, itemWidth), 'head');
  475. }
  476. else {
  477. ellipsisLabel(true, valueShape, clamp(itemWidth - curX, 0, itemWidth));
  478. }
  479. }
  480. curX = valueShape.getBBox().maxX + itemValue.spacing;
  481. }
  482. if (radio) {
  483. this.drawRadio(subGroup, radio, item, itemHeight, curX);
  484. }
  485. // 添加透明的背景,便于拾取和包围盒计算
  486. if (itemBackground) {
  487. var bbox = subGroup.getBBox();
  488. var backShape = this.addShape(subGroup, {
  489. type: 'rect',
  490. name: 'legend-item-background',
  491. id: this.getElementId(groupId + "-background"),
  492. attrs: __assign({ x: 0, y: 0, width: bbox.width, height: itemHeight }, itemBackground.style),
  493. });
  494. backShape.toBack();
  495. }
  496. this.applyItemStates(item, subGroup);
  497. return subGroup;
  498. };
  499. // 加上分页器并重新排序 items
  500. Category.prototype.adjustNavigation = function (container, itemGroup) {
  501. var _this = this;
  502. var startX = this.startX;
  503. var startY = this.startY;
  504. var layout = this.get('layout');
  505. var subGroups = itemGroup.findAll(function (item) { return item.get('name') === 'legend-item'; });
  506. var maxWidth = this.get('maxWidth');
  507. var maxHeight = this.get('maxHeight');
  508. var itemWidth = this.get('itemWidth');
  509. var itemSpacing = this.get('itemSpacing');
  510. var itemHeight = this.getItemHeight();
  511. var pageNavigator = deepMix({}, DEFAULT_PAGE_NAVIGATOR, this.get('pageNavigator'));
  512. var navigation = this.drawNavigation(container, layout, '00/00', pageNavigator);
  513. var navigationBBox = navigation.getBBox();
  514. var currentPoint = { x: startX, y: startY };
  515. var pages = 1;
  516. var widthLimit = 0;
  517. var pageWidth = 0;
  518. var maxItemWidth = 0;
  519. var itemMarginBottom = this.get('itemMarginBottom');
  520. /** 判断当前 item 是否溢出当前页。是的话,需要换行 */
  521. function shouldWrap(item, currentPoint) {
  522. var bbox = item.getBBox();
  523. var width = itemWidth || bbox.width;
  524. var newItemXPos = currentPoint.x + width + itemSpacing + navigationBBox.width;
  525. return newItemXPos > maxWidth;
  526. }
  527. if (layout === 'horizontal') {
  528. var maxRow = this.get('maxRow') || 1;
  529. var maxRowHeight_1 = itemHeight + (maxRow === 1 ? 0 : itemMarginBottom);
  530. // 分页器一直靠右上角
  531. var navigationX_1 = maxWidth - itemSpacing - navigationBBox.width - navigationBBox.minX; // 理论上不需要减 navigationBBox.minX
  532. this.pageHeight = maxRowHeight_1 * maxRow;
  533. this.pageWidth = navigationX_1;
  534. each(subGroups, function (item) {
  535. var bbox = item.getBBox();
  536. var width = itemWidth || bbox.width;
  537. if ((widthLimit && widthLimit < currentPoint.x + width + itemSpacing) ||
  538. shouldWrap(item, currentPoint)) {
  539. if (pages === 1) {
  540. widthLimit = currentPoint.x + itemSpacing;
  541. _this.moveElementTo(navigation, {
  542. x: navigationX_1,
  543. y: currentPoint.y + itemHeight / 2 - navigationBBox.height / 2 - navigationBBox.minY,
  544. });
  545. }
  546. pages += 1;
  547. currentPoint.x = startX;
  548. currentPoint.y += maxRowHeight_1;
  549. }
  550. _this.moveElementTo(item, currentPoint);
  551. item.getParent().setClip({
  552. type: 'rect',
  553. attrs: {
  554. x: currentPoint.x,
  555. y: currentPoint.y,
  556. width: width + itemSpacing,
  557. height: itemHeight,
  558. },
  559. });
  560. currentPoint.x += width + itemSpacing;
  561. });
  562. }
  563. else {
  564. each(subGroups, function (item) {
  565. var bbox = item.getBBox();
  566. if (bbox.width > pageWidth) {
  567. pageWidth = bbox.width;
  568. }
  569. });
  570. maxItemWidth = pageWidth;
  571. pageWidth += itemSpacing;
  572. if (maxWidth) {
  573. // maxWidth 限制加上
  574. pageWidth = Math.min(maxWidth, pageWidth);
  575. maxItemWidth = Math.min(maxWidth, maxItemWidth);
  576. }
  577. this.pageWidth = pageWidth;
  578. this.pageHeight = maxHeight - Math.max(navigationBBox.height, itemHeight + itemMarginBottom);
  579. var cntPerPage_1 = Math.floor(this.pageHeight / (itemHeight + itemMarginBottom));
  580. each(subGroups, function (item, index) {
  581. if (index !== 0 && index % cntPerPage_1 === 0) {
  582. pages += 1;
  583. currentPoint.x += pageWidth;
  584. currentPoint.y = startY;
  585. }
  586. _this.moveElementTo(item, currentPoint);
  587. item.getParent().setClip({
  588. type: 'rect',
  589. attrs: {
  590. x: currentPoint.x,
  591. y: currentPoint.y,
  592. width: pageWidth,
  593. height: itemHeight,
  594. },
  595. });
  596. currentPoint.y += itemHeight + itemMarginBottom;
  597. });
  598. this.totalPagesCnt = pages;
  599. this.moveElementTo(navigation, {
  600. x: startX + maxItemWidth / 2 - navigationBBox.width / 2 - navigationBBox.minX,
  601. y: maxHeight - navigationBBox.height - navigationBBox.minY,
  602. });
  603. }
  604. if (this.pageHeight && this.pageWidth) {
  605. // 为了使固定的 clip 生效,clip 设置在 itemContainerGroup 上,itemGroup 需要在翻页时会设置 matrix
  606. itemGroup.getParent().setClip({
  607. type: 'rect',
  608. attrs: {
  609. x: this.startX,
  610. y: this.startY,
  611. width: this.pageWidth,
  612. height: this.pageHeight,
  613. },
  614. });
  615. }
  616. // 重新计算 totalPagesCnt
  617. if (layout === 'horizontal' && this.get('maxRow')) {
  618. this.totalPagesCnt = Math.ceil(pages / this.get('maxRow'));
  619. }
  620. else {
  621. this.totalPagesCnt = pages;
  622. }
  623. if (this.currentPageIndex > this.totalPagesCnt) {
  624. this.currentPageIndex = 1;
  625. }
  626. this.updateNavigation(navigation);
  627. // update initial matrix
  628. itemGroup.attr('matrix', this.getCurrentNavigationMatrix());
  629. };
  630. /**
  631. * 绘制分页器
  632. */
  633. Category.prototype.drawNavigation = function (group, layout, text, styleCfg) {
  634. var currentPoint = { x: 0, y: 0 };
  635. var subGroup = this.addGroup(group, {
  636. id: this.getElementId('navigation-group'),
  637. name: 'legend-navigation',
  638. });
  639. var _a = get(styleCfg.marker, 'style', {}), _b = _a.size, size = _b === void 0 ? 12 : _b, arrowStyle = __rest(_a, ["size"]);
  640. var leftArrow = this.drawArrow(subGroup, currentPoint, LEFT_ARROW_NAME, layout === 'horizontal' ? 'up' : 'left', size, arrowStyle);
  641. leftArrow.on('click', this.onNavigationBack);
  642. var leftArrowBBox = leftArrow.getBBox();
  643. currentPoint.x += leftArrowBBox.width + 2;
  644. var textShape = this.addShape(subGroup, {
  645. type: 'text',
  646. id: this.getElementId('navigation-text'),
  647. name: 'navigation-text',
  648. attrs: __assign({ x: currentPoint.x, y: currentPoint.y + size / 2, text: text, textBaseline: 'middle' }, get(styleCfg.text, 'style')),
  649. });
  650. var textBBox = textShape.getBBox();
  651. currentPoint.x += textBBox.width + 2;
  652. var rightArrow = this.drawArrow(subGroup, currentPoint, RIGHT_ARROW_NAME, layout === 'horizontal' ? 'down' : 'right', size, arrowStyle);
  653. rightArrow.on('click', this.onNavigationAfter);
  654. return subGroup;
  655. };
  656. Category.prototype.updateNavigation = function (navigation) {
  657. var pageNavigator = deepMix({}, DEFAULT_PAGE_NAVIGATOR, this.get('pageNavigator'));
  658. var _a = pageNavigator.marker.style, fill = _a.fill, opacity = _a.opacity, inactiveFill = _a.inactiveFill, inactiveOpacity = _a.inactiveOpacity;
  659. var text = this.currentPageIndex + "/" + this.totalPagesCnt;
  660. var textShape = navigation ? navigation.getChildren()[1] : this.getElementByLocalId('navigation-text');
  661. var leftArrow = navigation
  662. ? navigation.findById(this.getElementId(LEFT_ARROW_NAME))
  663. : this.getElementByLocalId(LEFT_ARROW_NAME);
  664. var rightArrow = navigation
  665. ? navigation.findById(this.getElementId(RIGHT_ARROW_NAME))
  666. : this.getElementByLocalId(RIGHT_ARROW_NAME);
  667. textShape.attr('text', text);
  668. // 更新 left-arrow marker
  669. leftArrow.attr('opacity', this.currentPageIndex === 1 ? inactiveOpacity : opacity);
  670. leftArrow.attr('fill', this.currentPageIndex === 1 ? inactiveFill : fill);
  671. leftArrow.attr('cursor', this.currentPageIndex === 1 ? 'not-allowed' : 'pointer');
  672. // 更新 right-arrow marker
  673. rightArrow.attr('opacity', this.currentPageIndex === this.totalPagesCnt ? inactiveOpacity : opacity);
  674. rightArrow.attr('fill', this.currentPageIndex === this.totalPagesCnt ? inactiveFill : fill);
  675. rightArrow.attr('cursor', this.currentPageIndex === this.totalPagesCnt ? 'not-allowed' : 'pointer');
  676. // 更新位置
  677. var cursorX = leftArrow.getBBox().maxX + 2;
  678. textShape.attr('x', cursorX);
  679. cursorX += textShape.getBBox().width + 2;
  680. this.updateArrowPath(rightArrow, { x: cursorX, y: 0 });
  681. };
  682. Category.prototype.drawArrow = function (group, currentPoint, name, direction, size, style) {
  683. var x = currentPoint.x, y = currentPoint.y;
  684. var shape = this.addShape(group, {
  685. type: 'path',
  686. id: this.getElementId(name),
  687. name: name,
  688. attrs: __assign({ size: size,
  689. direction: direction, path: [['M', x + size / 2, y], ['L', x, y + size], ['L', x + size, y + size], ['Z']], cursor: 'pointer' }, style),
  690. });
  691. shape.attr('matrix', getMatrixByAngle({ x: x + size / 2, y: y + size / 2 }, ROTATE_MAP[direction]));
  692. return shape;
  693. };
  694. /**
  695. * 更新分页器 arrow 组件
  696. */
  697. Category.prototype.updateArrowPath = function (arrow, point) {
  698. var x = point.x, y = point.y;
  699. var _a = arrow.attr(), size = _a.size, direction = _a.direction;
  700. var matrix = getMatrixByAngle({ x: x + size / 2, y: y + size / 2 }, ROTATE_MAP[direction]);
  701. arrow.attr('path', [['M', x + size / 2, y], ['L', x, y + size], ['L', x + size, y + size], ['Z']]);
  702. arrow.attr('matrix', matrix);
  703. };
  704. Category.prototype.getCurrentNavigationMatrix = function () {
  705. var _a = this, currentPageIndex = _a.currentPageIndex, pageWidth = _a.pageWidth, pageHeight = _a.pageHeight;
  706. var layout = this.get('layout');
  707. var translate = layout === 'horizontal'
  708. ? {
  709. x: 0,
  710. y: pageHeight * (1 - currentPageIndex),
  711. }
  712. : {
  713. x: pageWidth * (1 - currentPageIndex),
  714. y: 0,
  715. };
  716. return getMatrixByTranslate(translate);
  717. };
  718. // 附加状态对应的样式
  719. Category.prototype.applyItemStates = function (item, subGroup) {
  720. var states = this.getItemStates(item);
  721. var hasStates = states.length > 0;
  722. if (hasStates) {
  723. var children = subGroup.getChildren();
  724. var itemStates_1 = this.get('itemStates');
  725. each(children, function (element) {
  726. var name = element.get('name');
  727. var elName = name.split('-')[2]; // marker, name, value
  728. var statesStyle = getStatesStyle(item, elName, itemStates_1);
  729. if (statesStyle) {
  730. element.attr(statesStyle);
  731. if (elName === 'marker' && !(element.get('isStroke') && element.get('isFill'))) {
  732. // 如果 marker 是单填充或者单描边的话,就不要额外添加 stroke 或这 fill 属性,否则会影响 unchecked 后的显示
  733. if (element.get('isStroke')) {
  734. element.attr('fill', null);
  735. }
  736. if (element.get('isFill')) {
  737. element.attr('stroke', null);
  738. }
  739. }
  740. }
  741. });
  742. }
  743. };
  744. // 获取 itemWidth 的最终设置
  745. Category.prototype.getLimitItemWidth = function () {
  746. var itemWidth = this.get('itemWidth');
  747. var maxItemWidth = this.get('maxItemWidth');
  748. if (maxItemWidth) {
  749. // 设置了最大宽度
  750. if (itemWidth) {
  751. maxItemWidth = itemWidth <= maxItemWidth ? itemWidth : maxItemWidth;
  752. }
  753. }
  754. else if (itemWidth) {
  755. maxItemWidth = itemWidth;
  756. }
  757. return maxItemWidth;
  758. };
  759. return Category;
  760. }(LegendBase));
  761. export default Category;
  762. //# sourceMappingURL=category.js.map