SGraphSelectContainer.ts 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571
  1. import { SGraphItem } from "./SGraphItem";
  2. import { SObject } from "@saga-web/base/lib";
  3. import { SGraphLayoutType } from "./enums/SGraphLayoutType";
  4. import { SOrderSetType } from "./enums/SOrderSetType";
  5. /**
  6. * 基本选择器
  7. *
  8. * @author 郝建龙
  9. */
  10. export class SGraphSelectContainer extends SObject {
  11. /** 选择对象list */
  12. private itemList: SGraphItem[] = [];
  13. /** 统计选中item的数量 */
  14. get count(): number {
  15. return this.itemList.length;
  16. }
  17. /** 置顶zorder增加基数 */
  18. private radix: number = 0.001;
  19. /**
  20. * 构造体
  21. * */
  22. constructor() {
  23. super();
  24. } // Constructor
  25. /**
  26. * 添加item到list
  27. *
  28. * @param item 当前选中的item
  29. * */
  30. addItem(item: SGraphItem): void {
  31. for (let i = 0; i < this.itemList.length; i++) {
  32. if (this.itemList[i] == item) {
  33. return;
  34. }
  35. }
  36. item.selected = true;
  37. this.itemList.push(item);
  38. this.$emit("listChange", this.itemList);
  39. } // Function addItem()
  40. /**
  41. * 清空再添加(事件+复制数组)
  42. *
  43. * @param item 当前选中的item
  44. * */
  45. setItem(item: SGraphItem): void {
  46. this.itemList.forEach((t: SGraphItem): void => {
  47. t.selected = false;
  48. });
  49. this.itemList.length = 0;
  50. item.selected = true;
  51. this.itemList.push(item);
  52. this.$emit("listChange", this.itemList);
  53. } // Function setItem()
  54. /**
  55. * 清空选择对象
  56. *
  57. * */
  58. clear(): void {
  59. this.itemList.forEach((t: SGraphItem): void => {
  60. t.selected = false;
  61. });
  62. this.itemList.length = 0;
  63. this.$emit("listChange", this.itemList);
  64. } // Function clear()
  65. /**
  66. * 切换选择对象
  67. *
  68. * @param item 当前选中的item
  69. * */
  70. toggleItem(item: SGraphItem): void {
  71. for (let i = 0; i < this.itemList.length; i++) {
  72. if (this.itemList[i] == item) {
  73. this.itemList[i].selected = false;
  74. this.itemList.splice(i, 1);
  75. this.$emit("listChange", this.itemList, item);
  76. return;
  77. }
  78. }
  79. // 多选时,父级item需要一致
  80. if (this.itemList.length) {
  81. if (item.parent != this.itemList[0].parent) {
  82. return;
  83. }
  84. }
  85. item.selected = true;
  86. this.itemList.push(item);
  87. this.$emit("listChange", this.itemList);
  88. } // Function toggleItem()
  89. /**
  90. * 对齐
  91. *
  92. * @param type 对齐方式
  93. * */
  94. layout(type: SGraphLayoutType): void {
  95. if (this.itemList.length < 2) {
  96. return;
  97. }
  98. switch (type) {
  99. case SGraphLayoutType.Left:
  100. this.alignLeft();
  101. break;
  102. case SGraphLayoutType.Bottom:
  103. this.alignBottom();
  104. break;
  105. case SGraphLayoutType.Center:
  106. this.alignCenter();
  107. break;
  108. case SGraphLayoutType.Horizontal:
  109. this.alignHorizontal();
  110. break;
  111. case SGraphLayoutType.Middle:
  112. this.alignMiddle();
  113. break;
  114. case SGraphLayoutType.Right:
  115. this.alignRight();
  116. break;
  117. case SGraphLayoutType.Top:
  118. this.alignTop();
  119. break;
  120. case SGraphLayoutType.Vertical:
  121. this.alignVertical();
  122. break;
  123. default:
  124. console.log("对齐类型不存在");
  125. break;
  126. }
  127. } // Function layout()
  128. /**
  129. * 图层操作
  130. *
  131. * @param type 操作类型
  132. * */
  133. setOrder(type: SOrderSetType): void {
  134. if (this.itemList.length < 1) {
  135. return;
  136. }
  137. switch (type) {
  138. case SOrderSetType.Top:
  139. this.setTop();
  140. break;
  141. case SOrderSetType.Bottom:
  142. this.setBottom();
  143. break;
  144. case SOrderSetType.After:
  145. this.setAfter();
  146. break;
  147. case SOrderSetType.Before:
  148. this.setBefore();
  149. break;
  150. default:
  151. console.log("图层操作类型不存在");
  152. break;
  153. }
  154. } // Function setOrder()
  155. /**
  156. * 左对齐
  157. *
  158. * */
  159. private alignLeft(): void {
  160. let standardItem = this.itemList[0];
  161. // 计算第一个外接矩阵左上角坐标在场景中的坐标
  162. let p = standardItem.mapToScene(
  163. standardItem.boundingRect().x,
  164. standardItem.boundingRect().y
  165. );
  166. for (let i = 1; i < this.itemList.length; i++) {
  167. let curItem = this.itemList[i];
  168. if (curItem.moveable) {
  169. // 将p转换为当前item中的坐标
  170. let p1 = curItem.mapFromScene(p.x, p.y);
  171. // 根据等式差值相等 newboundx - oldboundx = newposx - oldposx => newposx = newboundx - oldboundx + oldposx
  172. curItem.x =
  173. (p1.x - curItem.boundingRect().x) * curItem.inverseScale +
  174. curItem.x;
  175. }
  176. }
  177. } // Function alignLeft()
  178. /**
  179. * 顶对齐
  180. *
  181. * */
  182. private alignTop(): void {
  183. let standardItem = this.itemList[0];
  184. let p = standardItem.mapToScene(
  185. standardItem.boundingRect().x,
  186. standardItem.boundingRect().y
  187. );
  188. for (let i = 1; i < this.itemList.length; i++) {
  189. let curItem = this.itemList[i];
  190. if (curItem.moveable) {
  191. let p1 = curItem.mapFromScene(p.x, p.y);
  192. curItem.y =
  193. (p1.y - curItem.boundingRect().y) * curItem.inverseScale +
  194. curItem.y;
  195. }
  196. }
  197. } // Function alignTop()
  198. /**
  199. * 右对齐
  200. *
  201. * */
  202. private alignRight(): void {
  203. let standardItem = this.itemList[0];
  204. let p = standardItem.mapToScene(
  205. standardItem.boundingRect().right,
  206. standardItem.boundingRect().bottom
  207. );
  208. for (let i = 1; i < this.itemList.length; i++) {
  209. let curItem = this.itemList[i];
  210. if (curItem.moveable) {
  211. let p1 = curItem.mapFromScene(p.x, p.y);
  212. curItem.x =
  213. (p1.x - curItem.boundingRect().right) *
  214. curItem.inverseScale +
  215. curItem.x;
  216. }
  217. }
  218. } // Function alignRight()
  219. /**
  220. * 底对齐
  221. *
  222. * */
  223. private alignBottom(): void {
  224. let standardItem = this.itemList[0];
  225. let p = standardItem.mapToScene(
  226. standardItem.boundingRect().right,
  227. standardItem.boundingRect().bottom
  228. );
  229. for (let i = 1; i < this.itemList.length; i++) {
  230. let curItem = this.itemList[i];
  231. if (curItem.moveable) {
  232. let p1 = curItem.mapFromScene(p.x, p.y);
  233. curItem.y =
  234. (p1.y - curItem.boundingRect().bottom) *
  235. curItem.inverseScale +
  236. curItem.y;
  237. }
  238. }
  239. } // Function alignBottom()
  240. /**
  241. * 水平居中对齐
  242. *
  243. * */
  244. private alignCenter(): void {
  245. // 第一个选中的item即为基准对象
  246. let standardItem = this.itemList[0];
  247. // 基准对象的中心点
  248. let center = standardItem.boundingRect().center();
  249. // 中心点转换为场景坐标
  250. let p = standardItem.mapToScene(center.x, center.y);
  251. for (let i = 1; i < this.itemList.length; i++) {
  252. let curItem = this.itemList[i];
  253. if (curItem.moveable) {
  254. let p1 = curItem.mapFromScene(p.x, p.y);
  255. // 根据等式差值相等 newcenterx - oldcenterx = newposx - oldposx => newposx = newcenterx - oldcenterx + oldposx
  256. curItem.x =
  257. (p1.x - curItem.boundingRect().center().x) *
  258. curItem.inverseScale +
  259. curItem.x;
  260. }
  261. }
  262. } // Function alignCenter()
  263. /**
  264. * 垂直居中对齐
  265. *
  266. * */
  267. private alignMiddle(): void {
  268. // 第一个选中的item即为基准对象
  269. let standardItem = this.itemList[0];
  270. // 基准对象的中心点
  271. let center = standardItem.boundingRect().center();
  272. // 中心点转换为场景坐标
  273. let p = standardItem.mapToScene(center.x, center.y);
  274. for (let i = 1; i < this.itemList.length; i++) {
  275. let curItem = this.itemList[i];
  276. if (curItem.moveable) {
  277. let p1 = curItem.mapFromScene(p.x, p.y);
  278. curItem.y =
  279. (p1.y - curItem.boundingRect().center().y) *
  280. curItem.inverseScale +
  281. curItem.y;
  282. }
  283. }
  284. } // Function alignMiddle()
  285. /**
  286. * 垂直分布
  287. *
  288. * */
  289. private alignVertical(): void {
  290. if (this.itemList.length < 3) {
  291. return;
  292. }
  293. for (let i = 0; i < this.itemList.length - 1; i++) {
  294. for (let j = 0; j < this.itemList.length - 1 - i; j++) {
  295. let curItemCenter = this.itemList[j].boundingRect().center();
  296. let nextItemCenter = this.itemList[j + 1]
  297. .boundingRect()
  298. .center();
  299. let curCenterY = this.itemList[j].mapToScene(
  300. curItemCenter.x,
  301. curItemCenter.y
  302. ).y;
  303. let nextCenterY = this.itemList[j + 1].mapToScene(
  304. nextItemCenter.x,
  305. nextItemCenter.y
  306. ).y;
  307. if (curCenterY > nextCenterY) {
  308. let temp = this.itemList[j];
  309. this.itemList[j] = this.itemList[j + 1];
  310. this.itemList[j + 1] = temp;
  311. }
  312. }
  313. }
  314. let top = this.itemList[0];
  315. let bottom = this.itemList[this.itemList.length - 1];
  316. let topCenter = top.boundingRect().center();
  317. let bottomCenter = bottom.boundingRect().center();
  318. let leftToRight =
  319. bottom.mapToScene(bottomCenter.x, bottomCenter.y).y -
  320. top.mapToScene(topCenter.x, topCenter.y).y;
  321. let average = leftToRight / (this.itemList.length - 1);
  322. for (let k = 1; k < this.itemList.length - 1; k++) {
  323. let curItem = this.itemList[k];
  324. if (curItem.moveable) {
  325. let p1 = top.mapToScene(
  326. top.boundingRect().center().x,
  327. top.boundingRect().center().y
  328. );
  329. let p2 = curItem.mapFromScene(p1.x, p1.y + average * k);
  330. curItem.y =
  331. (p2.y - curItem.boundingRect().center().y) *
  332. curItem.inverseScale +
  333. curItem.y;
  334. }
  335. }
  336. } // Function alignVertical()
  337. /**
  338. * 水平分布
  339. *
  340. * */
  341. private alignHorizontal(): void {
  342. if (this.itemList.length < 3) {
  343. return;
  344. }
  345. for (let i = 0; i < this.itemList.length - 1; i++) {
  346. for (let j = 0; j < this.itemList.length - 1 - i; j++) {
  347. let curItemCenter = this.itemList[j].boundingRect().center();
  348. let nextItemCenter = this.itemList[j + 1]
  349. .boundingRect()
  350. .center();
  351. let curCenterX = this.itemList[j].mapToScene(
  352. curItemCenter.x,
  353. curItemCenter.y
  354. ).x;
  355. let nextCenterX = this.itemList[j + 1].mapToScene(
  356. nextItemCenter.x,
  357. nextItemCenter.y
  358. ).x;
  359. if (curCenterX > nextCenterX) {
  360. let temp = this.itemList[j];
  361. this.itemList[j] = this.itemList[j + 1];
  362. this.itemList[j + 1] = temp;
  363. }
  364. }
  365. }
  366. let left = this.itemList[0];
  367. let right = this.itemList[this.itemList.length - 1];
  368. let leftCenter = left.boundingRect().center();
  369. let rightCenter = right.boundingRect().center();
  370. let leftToRight =
  371. right.mapToScene(rightCenter.x, rightCenter.y).x -
  372. left.mapToScene(leftCenter.x, leftCenter.y).x;
  373. let average = leftToRight / (this.itemList.length - 1);
  374. for (let k = 1; k < this.itemList.length - 1; k++) {
  375. let curItem = this.itemList[k];
  376. if (curItem.moveable) {
  377. let p1 = left.mapToScene(
  378. left.boundingRect().center().x,
  379. left.boundingRect().center().y
  380. );
  381. let p2 = curItem.mapFromScene(p1.x + average * k, p1.y);
  382. // 根据等式 newposx - oldposx = newcenterx - oldcenterx
  383. curItem.x =
  384. (p2.x - curItem.boundingRect().center().x) *
  385. curItem.inverseScale +
  386. curItem.x;
  387. }
  388. }
  389. } // Function alignHorizontal()
  390. // 修改层级操作步骤:
  391. // 排序(保持相对层级不变)
  392. // children里边要取 整数部分相同的 点:children是已经排好序的
  393. // 然后再取最大值
  394. /**
  395. * 置顶
  396. *
  397. * */
  398. private setTop(): void {
  399. const arr: SGraphItem[] = this.sortItemList(this.itemList, true);
  400. // 按顺序zOrder增加
  401. arr.forEach(it => {
  402. let max = it.zOrder;
  403. if (it.parent) {
  404. it.parent.children.forEach(item => {
  405. if (
  406. Number(max.toFixed()) == Number(item.zOrder.toFixed())
  407. ) {
  408. if (item.zOrder > max) {
  409. max = item.zOrder;
  410. }
  411. }
  412. });
  413. it.zOrder = max + this.radix;
  414. }
  415. });
  416. } // Function setTop()
  417. /**
  418. * 置底
  419. *
  420. * */
  421. private setBottom(): void {
  422. const arr: SGraphItem[] = this.sortItemList(this.itemList, false);
  423. // 按顺序zOrder增加
  424. arr.forEach(it => {
  425. let min = it.zOrder;
  426. if (it.parent) {
  427. it.parent.children.forEach(item => {
  428. if (
  429. Number(min.toFixed()) == Number(item.zOrder.toFixed())
  430. ) {
  431. if (item.zOrder < min) {
  432. min = item.zOrder;
  433. }
  434. }
  435. });
  436. it.zOrder = min - this.radix;
  437. }
  438. });
  439. } // Function setBottom()
  440. /**
  441. * 下移一层zorder减小
  442. *
  443. * */
  444. private setBefore(): void {
  445. const arr: SGraphItem[] = this.sortItemList(this.itemList, false);
  446. arr.forEach(it => {
  447. if (it.parent) {
  448. const min = it.zOrder;
  449. let temp = it.parent.children
  450. .map(child => {
  451. if (
  452. Number(child.zOrder.toFixed()) ==
  453. Number(min.toFixed())
  454. ) {
  455. return child.zOrder;
  456. }
  457. })
  458. .filter(c => c);
  459. temp = [...new Set(temp)];
  460. // 当前层有多个
  461. const index = temp.indexOf(it.zOrder);
  462. if (index <= 1) {
  463. // 当前item 索引为1时 将当前item放至第一个前边
  464. // @ts-ignore
  465. it.zOrder = temp[0] - this.radix;
  466. } else if (index > 1) {
  467. // 当前item 索引大于等于2,将当前item放至前两个中间
  468. // @ts-ignore
  469. it.zOrder = (temp[index - 1] + temp[index - 2]) / 2;
  470. }
  471. }
  472. });
  473. } // Function setAfter()
  474. /**
  475. * 上移一层zorder增加
  476. *
  477. * */
  478. private setAfter(): void {
  479. const arr: SGraphItem[] = this.sortItemList(this.itemList, false);
  480. arr.forEach(it => {
  481. if (it.parent) {
  482. const max = it.zOrder;
  483. let temp = it.parent.children
  484. .map(child => {
  485. if (
  486. Number(child.zOrder.toFixed()) ==
  487. Number(max.toFixed())
  488. ) {
  489. return child.zOrder;
  490. }
  491. })
  492. .filter(c => c);
  493. temp = [...new Set(temp)].reverse();
  494. // 当前层有多个
  495. const index = temp.indexOf(it.zOrder);
  496. if (index <= 1) {
  497. // 当前item 索引为1时 将当前item放至第一个前边
  498. // @ts-ignore
  499. it.zOrder = temp[0] + this.radix;
  500. } else if (index > 1) {
  501. // 当前item 索引大于等于2,将当前item放至前两个中间
  502. // @ts-ignore
  503. it.zOrder = (temp[index - 1] + temp[index - 2]) / 2;
  504. }
  505. }
  506. });
  507. } // Function setBefore()
  508. /**
  509. * 数组排序
  510. *
  511. * @param arr 排序前的数组
  512. * @param flag 正序or逆序
  513. * @return list 排序后的数组
  514. * */
  515. sortItemList(arr: SGraphItem[], flag: boolean = true): SGraphItem[] {
  516. const list: SGraphItem[] = arr.map(t => t);
  517. list.sort(this.compare("zOrder", flag));
  518. return list;
  519. } // Function sortItemList()
  520. /**
  521. * 按照某个属性排序
  522. *
  523. * @param prop 属性
  524. * @param flag 默认正序
  525. * */
  526. private compare(prop: string, flag: boolean) {
  527. const f = flag ? 1 : -1;
  528. // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
  529. return (a: any, b: any) => {
  530. a = a[prop];
  531. b = b[prop];
  532. if (a < b) {
  533. return f * -1;
  534. }
  535. if (a > b) {
  536. return f * 1;
  537. }
  538. return 0;
  539. };
  540. } // Function compare()
  541. } // Class SGraphSelectContainer