SGraphSelectContainer.ts 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037
  1. /*
  2. * *********************************************************************************************************************
  3. *
  4. * !!
  5. * .F88X
  6. * X8888Y
  7. * .}888888N;
  8. * i888888N; .:! .I$WI:
  9. * R888888I .'N88~ i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8&
  10. * .R888888I .;N8888~ .X8' "8I.!,/8" !%NY8`"8I8~~8>,88I
  11. * +888888N; .8888888Y "&&8Y.}8,
  12. * ./888888N; .R888888Y .'}~ .>}'.`+> i}! "i' +/' .'i~ !11,.:">, .~]! .i}i
  13. * ~888888%: .I888888l .]88~`1/iY88Ii+1'.R$8$8]"888888888> Y8$ W8E X8E W8888'188Il}Y88$*
  14. * 18888888 E8888881 .]W%8$`R8X'&8%++N8i,8N%N8+l8%` .}8N:.R$RE%N88N%N$K$R 188,FE$8%~Y88I
  15. * .E888888I .i8888888' .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~
  16. * 8888888I .,N888888~ ~88i"8W,!N8*.I88.}888%F,i$88"F88" 888:E8X.>88!i88>`888*.}Fl1]*}1YKi'
  17. * i888888N' I888Y ]88;/EX*IFKFK88X K8R .l8W 88Y ~88}'88E&%8W.X8N``]88!.$8K .:W8I
  18. * .i888888N; I8Y .&8$ .X88! i881.:%888>I88 ;88] +88+.';;;;:.Y88X 18N.,88l .+88/
  19. * .:R888888I
  20. * .&888888I Copyright (c) 2009-2020. 博锐尚格科技股份有限公司
  21. * ~8888'
  22. * .!88~ All rights reserved.
  23. *
  24. * *********************************************************************************************************************
  25. */
  26. import { SGraphItem } from "./SGraphItem";
  27. import { SGraphLayoutType } from "./enums/SGraphLayoutType";
  28. import { SOrderSetType } from "./enums/SOrderSetType";
  29. import { SColor, SPainter, SPoint, SRect, SSize } from "@persagy-web/draw";
  30. import { SMathUtil } from "./utils/SMathUtil";
  31. import { SMouseEvent } from "@persagy-web/base";
  32. /**
  33. * 基本选择器
  34. *
  35. * @author 郝建龙
  36. */
  37. export class SGraphSelectContainer extends SGraphItem {
  38. /** 选择对象list */
  39. private _itemList: SGraphItem[] = [];
  40. get itemList(): SGraphItem[] {
  41. return this._itemList;
  42. } // Get itemList
  43. /** 外接点的list */
  44. private pointList: SPoint[] = [];
  45. /** item宽 */
  46. private _width: number = 0;
  47. get width(): number {
  48. return this._width;
  49. } // Get width
  50. set width(v: number) {
  51. if (v == this._width) {
  52. return;
  53. }
  54. this._width = v;
  55. this.update();
  56. } // Set width
  57. /** item高 */
  58. private _height: number = 0;
  59. get height(): number {
  60. return this._height;
  61. } // Get height
  62. set height(v: number) {
  63. if (v == this._height) {
  64. return;
  65. }
  66. this._height = v;
  67. this.update();
  68. } // Set height
  69. /** 修改之前的宽 */
  70. private oldWidth: number = 0;
  71. /** 修改之前的高 */
  72. private oldHeight: number = 0;
  73. /** 最小宽 */
  74. minWidth: number = 0;
  75. /** 最大宽 */
  76. maxWidth: number = Number.MAX_SAFE_INTEGER;
  77. /** 最小高 */
  78. minHeight: number = 0;
  79. /** 最小高 */
  80. maxHeight: number = Number.MAX_SAFE_INTEGER;
  81. /** 边框颜色 */
  82. _strokeColor: SColor = new SColor("#82C7FC");
  83. /** 画笔颜色 */
  84. get strokeColor(): SColor {
  85. return this._strokeColor;
  86. } // Get strokeColor
  87. set strokeColor(v: SColor) {
  88. this._strokeColor = v;
  89. this.update();
  90. } // Set strokeColor
  91. /** 填充颜色 */
  92. _fillColor: SColor = new SColor("#ffffff");
  93. get fillColor(): SColor {
  94. return this._fillColor;
  95. } // Get fillColor
  96. set fillColor(v: SColor) {
  97. this._fillColor = v;
  98. this.update();
  99. } // Set fillColor
  100. /** 绘制矩形的圆角半径 */
  101. /** 统计选中item的数量 */
  102. get count(): number {
  103. return this.itemList.length;
  104. }
  105. /** 置顶zorder增加基数 */
  106. private radix: number = 0.001;
  107. /** 定时器 */
  108. private timer: number | undefined;
  109. /** 半径 */
  110. radius: number = 5;
  111. /** 全局灵敏度 */
  112. dis: number = 10;
  113. /** 灵敏度转换为场景长度 */
  114. private sceneDis: number = 10;
  115. /** 当前点索引 */
  116. private _curIndex: number = -1;
  117. get curIndex(): number {
  118. return this._curIndex;
  119. } // get curIndex
  120. set curIndex(v: number) {
  121. if (v === this._curIndex) {
  122. return;
  123. }
  124. this._curIndex = v;
  125. this.update();
  126. } // set curIndex
  127. /** 当前点索引 */
  128. private curPoint: SPoint | undefined;
  129. /** 鼠标样式 */
  130. static cursorList: string[] = [
  131. "nw-resize",
  132. "n-resize",
  133. "ne-resize",
  134. "e-resize",
  135. "se-resize",
  136. "s-resize",
  137. "sw-resize",
  138. "w-resize",
  139. "move",
  140. "default"
  141. ];
  142. /**
  143. * 构造体
  144. * */
  145. constructor() {
  146. super();
  147. this.zOrder = 9999999;
  148. this.visible = false;
  149. this.moveable = true;
  150. } // Constructor
  151. /**
  152. * 添加item到list
  153. *
  154. * @param item 当前选中的item
  155. * */
  156. addItem(item: SGraphItem): void {
  157. for (let i = 0; i < this.itemList.length; i++) {
  158. if (this.itemList[i] == item) {
  159. return;
  160. }
  161. }
  162. item.selected = true;
  163. this.itemList.push(item);
  164. this.throttle(this.selectChange, 80);
  165. } // Function addItem()
  166. /**
  167. * 清空再添加(事件+复制数组)
  168. *
  169. * @param item 当前选中的item
  170. * */
  171. setItem(item: SGraphItem): void {
  172. this.itemList.forEach((t: SGraphItem): void => {
  173. t.selected = false;
  174. });
  175. this.itemList.length = 0;
  176. item.selected = true;
  177. this.itemList.push(item);
  178. this.throttle(this.selectChange, 80);
  179. } // Function setItem()
  180. /**
  181. * 清空选择对象
  182. *
  183. * */
  184. clear(): void {
  185. this.itemList.forEach((t: SGraphItem): void => {
  186. t.selected = false;
  187. });
  188. this.itemList.length = 0;
  189. this.throttle(this.selectChange, 80);
  190. } // Function clear()
  191. /**
  192. * 切换选择对象
  193. *
  194. * @param item 当前选中的item
  195. * */
  196. toggleItem(item: SGraphItem): void {
  197. for (let i = 0; i < this.itemList.length; i++) {
  198. if (this.itemList[i] == item) {
  199. this.itemList[i].selected = false;
  200. this.itemList.splice(i, 1);
  201. this.throttle(this.selectChange, 80);
  202. return;
  203. }
  204. }
  205. // 多选时,父级item需要一致
  206. if (this.itemList.length) {
  207. if (item.parent != this.itemList[0].parent) {
  208. return;
  209. }
  210. }
  211. item.selected = true;
  212. this.itemList.push(item);
  213. this.throttle(this.selectChange, 80);
  214. } // Function toggleItem()
  215. /**
  216. * 对齐
  217. *
  218. * @param type 对齐方式
  219. * */
  220. layout(type: SGraphLayoutType): void {
  221. if (this.itemList.length < 2) {
  222. return;
  223. }
  224. switch (type) {
  225. case SGraphLayoutType.Left:
  226. this.alignLeft();
  227. break;
  228. case SGraphLayoutType.Bottom:
  229. this.alignBottom();
  230. break;
  231. case SGraphLayoutType.Center:
  232. this.alignCenter();
  233. break;
  234. case SGraphLayoutType.Horizontal:
  235. this.alignHorizontal();
  236. break;
  237. case SGraphLayoutType.Middle:
  238. this.alignMiddle();
  239. break;
  240. case SGraphLayoutType.Right:
  241. this.alignRight();
  242. break;
  243. case SGraphLayoutType.Top:
  244. this.alignTop();
  245. break;
  246. case SGraphLayoutType.Vertical:
  247. this.alignVertical();
  248. break;
  249. default:
  250. console.log("对齐类型不存在");
  251. break;
  252. }
  253. } // Function layout()
  254. /**
  255. * 图层操作
  256. *
  257. * @param type 操作类型
  258. * */
  259. setOrder(type: SOrderSetType): void {
  260. if (this.itemList.length < 1) {
  261. return;
  262. }
  263. switch (type) {
  264. case SOrderSetType.Top:
  265. this.setTop();
  266. break;
  267. case SOrderSetType.Bottom:
  268. this.setBottom();
  269. break;
  270. case SOrderSetType.After:
  271. this.setAfter();
  272. break;
  273. case SOrderSetType.Before:
  274. this.setBefore();
  275. break;
  276. default:
  277. console.log("图层操作类型不存在");
  278. break;
  279. }
  280. } // Function setOrder()
  281. /**
  282. * 左对齐
  283. *
  284. * */
  285. private alignLeft(): void {
  286. let standardItem = this.itemList[0];
  287. // 计算第一个外接矩阵左上角坐标在场景中的坐标
  288. let p = standardItem.mapToScene(
  289. standardItem.boundingRect().x,
  290. standardItem.boundingRect().y
  291. );
  292. for (let i = 1; i < this.itemList.length; i++) {
  293. let curItem = this.itemList[i];
  294. if (curItem.moveable) {
  295. // 将p转换为当前item中的坐标
  296. let p1 = curItem.mapFromScene(p.x, p.y);
  297. // 根据等式差值相等 newboundx - oldboundx = newposx - oldposx => newposx = newboundx - oldboundx + oldposx
  298. curItem.x =
  299. (p1.x - curItem.boundingRect().x) * curItem.inverseScale +
  300. curItem.x;
  301. }
  302. }
  303. } // Function alignLeft()
  304. /**
  305. * 顶对齐
  306. *
  307. * */
  308. private alignTop(): void {
  309. let standardItem = this.itemList[0];
  310. let p = standardItem.mapToScene(
  311. standardItem.boundingRect().x,
  312. standardItem.boundingRect().y
  313. );
  314. for (let i = 1; i < this.itemList.length; i++) {
  315. let curItem = this.itemList[i];
  316. if (curItem.moveable) {
  317. let p1 = curItem.mapFromScene(p.x, p.y);
  318. curItem.y =
  319. (p1.y - curItem.boundingRect().y) * curItem.inverseScale +
  320. curItem.y;
  321. }
  322. }
  323. } // Function alignTop()
  324. /**
  325. * 右对齐
  326. *
  327. * */
  328. private alignRight(): void {
  329. let standardItem = this.itemList[0];
  330. let p = standardItem.mapToScene(
  331. standardItem.boundingRect().right,
  332. standardItem.boundingRect().bottom
  333. );
  334. for (let i = 1; i < this.itemList.length; i++) {
  335. let curItem = this.itemList[i];
  336. if (curItem.moveable) {
  337. let p1 = curItem.mapFromScene(p.x, p.y);
  338. curItem.x =
  339. (p1.x - curItem.boundingRect().right) *
  340. curItem.inverseScale +
  341. curItem.x;
  342. }
  343. }
  344. } // Function alignRight()
  345. /**
  346. * 底对齐
  347. *
  348. * */
  349. private alignBottom(): void {
  350. let standardItem = this.itemList[0];
  351. let p = standardItem.mapToScene(
  352. standardItem.boundingRect().right,
  353. standardItem.boundingRect().bottom
  354. );
  355. for (let i = 1; i < this.itemList.length; i++) {
  356. let curItem = this.itemList[i];
  357. if (curItem.moveable) {
  358. let p1 = curItem.mapFromScene(p.x, p.y);
  359. curItem.y =
  360. (p1.y - curItem.boundingRect().bottom) *
  361. curItem.inverseScale +
  362. curItem.y;
  363. }
  364. }
  365. } // Function alignBottom()
  366. /**
  367. * 水平居中对齐
  368. *
  369. * */
  370. private alignCenter(): void {
  371. // 第一个选中的item即为基准对象
  372. let standardItem = this.itemList[0];
  373. // 基准对象的中心点
  374. let center = standardItem.boundingRect().center();
  375. // 中心点转换为场景坐标
  376. let p = standardItem.mapToScene(center.x, center.y);
  377. for (let i = 1; i < this.itemList.length; i++) {
  378. let curItem = this.itemList[i];
  379. if (curItem.moveable) {
  380. let p1 = curItem.mapFromScene(p.x, p.y);
  381. // 根据等式差值相等 newcenterx - oldcenterx = newposx - oldposx => newposx = newcenterx - oldcenterx + oldposx
  382. curItem.x =
  383. (p1.x - curItem.boundingRect().center().x) *
  384. curItem.inverseScale +
  385. curItem.x;
  386. }
  387. }
  388. } // Function alignCenter()
  389. /**
  390. * 垂直居中对齐
  391. *
  392. * */
  393. private alignMiddle(): void {
  394. // 第一个选中的item即为基准对象
  395. let standardItem = this.itemList[0];
  396. // 基准对象的中心点
  397. let center = standardItem.boundingRect().center();
  398. // 中心点转换为场景坐标
  399. let p = standardItem.mapToScene(center.x, center.y);
  400. for (let i = 1; i < this.itemList.length; i++) {
  401. let curItem = this.itemList[i];
  402. if (curItem.moveable) {
  403. let p1 = curItem.mapFromScene(p.x, p.y);
  404. curItem.y =
  405. (p1.y - curItem.boundingRect().center().y) *
  406. curItem.inverseScale +
  407. curItem.y;
  408. }
  409. }
  410. } // Function alignMiddle()
  411. /**
  412. * 垂直分布
  413. *
  414. * */
  415. private alignVertical(): void {
  416. if (this.itemList.length < 3) {
  417. return;
  418. }
  419. for (let i = 0; i < this.itemList.length - 1; i++) {
  420. for (let j = 0; j < this.itemList.length - 1 - i; j++) {
  421. let curItemCenter = this.itemList[j].boundingRect().center();
  422. let nextItemCenter = this.itemList[j + 1]
  423. .boundingRect()
  424. .center();
  425. let curCenterY = this.itemList[j].mapToScene(
  426. curItemCenter.x,
  427. curItemCenter.y
  428. ).y;
  429. let nextCenterY = this.itemList[j + 1].mapToScene(
  430. nextItemCenter.x,
  431. nextItemCenter.y
  432. ).y;
  433. if (curCenterY > nextCenterY) {
  434. let temp = this.itemList[j];
  435. this.itemList[j] = this.itemList[j + 1];
  436. this.itemList[j + 1] = temp;
  437. }
  438. }
  439. }
  440. let top = this.itemList[0];
  441. let bottom = this.itemList[this.itemList.length - 1];
  442. let topCenter = top.boundingRect().center();
  443. let bottomCenter = bottom.boundingRect().center();
  444. let leftToRight =
  445. bottom.mapToScene(bottomCenter.x, bottomCenter.y).y -
  446. top.mapToScene(topCenter.x, topCenter.y).y;
  447. let average = leftToRight / (this.itemList.length - 1);
  448. for (let k = 1; k < this.itemList.length - 1; k++) {
  449. let curItem = this.itemList[k];
  450. if (curItem.moveable) {
  451. let p1 = top.mapToScene(
  452. top.boundingRect().center().x,
  453. top.boundingRect().center().y
  454. );
  455. let p2 = curItem.mapFromScene(p1.x, p1.y + average * k);
  456. curItem.y =
  457. (p2.y - curItem.boundingRect().center().y) *
  458. curItem.inverseScale +
  459. curItem.y;
  460. }
  461. }
  462. } // Function alignVertical()
  463. /**
  464. * 水平分布
  465. *
  466. * */
  467. private alignHorizontal(): void {
  468. if (this.itemList.length < 3) {
  469. return;
  470. }
  471. for (let i = 0; i < this.itemList.length - 1; i++) {
  472. for (let j = 0; j < this.itemList.length - 1 - i; j++) {
  473. let curItemCenter = this.itemList[j].boundingRect().center();
  474. let nextItemCenter = this.itemList[j + 1]
  475. .boundingRect()
  476. .center();
  477. let curCenterX = this.itemList[j].mapToScene(
  478. curItemCenter.x,
  479. curItemCenter.y
  480. ).x;
  481. let nextCenterX = this.itemList[j + 1].mapToScene(
  482. nextItemCenter.x,
  483. nextItemCenter.y
  484. ).x;
  485. if (curCenterX > nextCenterX) {
  486. let temp = this.itemList[j];
  487. this.itemList[j] = this.itemList[j + 1];
  488. this.itemList[j + 1] = temp;
  489. }
  490. }
  491. }
  492. let left = this.itemList[0];
  493. let right = this.itemList[this.itemList.length - 1];
  494. let leftCenter = left.boundingRect().center();
  495. let rightCenter = right.boundingRect().center();
  496. let leftToRight =
  497. right.mapToScene(rightCenter.x, rightCenter.y).x -
  498. left.mapToScene(leftCenter.x, leftCenter.y).x;
  499. let average = leftToRight / (this.itemList.length - 1);
  500. for (let k = 1; k < this.itemList.length - 1; k++) {
  501. let curItem = this.itemList[k];
  502. if (curItem.moveable) {
  503. let p1 = left.mapToScene(
  504. left.boundingRect().center().x,
  505. left.boundingRect().center().y
  506. );
  507. let p2 = curItem.mapFromScene(p1.x + average * k, p1.y);
  508. // 根据等式 newposx - oldposx = newcenterx - oldcenterx
  509. curItem.x =
  510. (p2.x - curItem.boundingRect().center().x) *
  511. curItem.inverseScale +
  512. curItem.x;
  513. }
  514. }
  515. } // Function alignHorizontal()
  516. // 修改层级操作步骤:
  517. // 排序(保持相对层级不变)
  518. // children里边要取 整数部分相同的 点:children是已经排好序的
  519. // 然后再取最大值
  520. /**
  521. * 置顶
  522. *
  523. * */
  524. private setTop(): void {
  525. const arr: SGraphItem[] = this.sortItemList(this.itemList, true);
  526. // 按顺序zOrder增加
  527. arr.forEach(it => {
  528. let max = it.zOrder;
  529. if (it.parent) {
  530. it.parent.children.forEach(item => {
  531. if (
  532. Number(max.toFixed()) == Number(item.zOrder.toFixed())
  533. ) {
  534. if (item.zOrder > max) {
  535. max = item.zOrder;
  536. }
  537. }
  538. });
  539. it.zOrder = max + this.radix;
  540. }
  541. });
  542. } // Function setTop()
  543. /**
  544. * 置底
  545. *
  546. * */
  547. private setBottom(): void {
  548. const arr: SGraphItem[] = this.sortItemList(this.itemList, false);
  549. // 按顺序zOrder增加
  550. arr.forEach(it => {
  551. let min = it.zOrder;
  552. if (it.parent) {
  553. it.parent.children.forEach(item => {
  554. if (
  555. Number(min.toFixed()) == Number(item.zOrder.toFixed())
  556. ) {
  557. if (item.zOrder < min) {
  558. min = item.zOrder;
  559. }
  560. }
  561. });
  562. it.zOrder = min - this.radix;
  563. }
  564. });
  565. } // Function setBottom()
  566. /**
  567. * 下移一层zorder减小
  568. *
  569. * */
  570. private setBefore(): void {
  571. const arr: SGraphItem[] = this.sortItemList(this.itemList, false);
  572. arr.forEach(it => {
  573. if (it.parent) {
  574. const min = it.zOrder;
  575. let temp = it.parent.children
  576. .map(child => {
  577. if (
  578. Number(child.zOrder.toFixed()) ==
  579. Number(min.toFixed())
  580. ) {
  581. return child.zOrder;
  582. }
  583. })
  584. .filter(c => c);
  585. temp = [...new Set(temp)];
  586. // 当前层有多个
  587. const index = temp.indexOf(it.zOrder);
  588. if (index <= 1) {
  589. // 当前item 索引为1时 将当前item放至第一个前边
  590. // @ts-ignore
  591. it.zOrder = temp[0] - this.radix;
  592. } else if (index > 1) {
  593. // 当前item 索引大于等于2,将当前item放至前两个中间
  594. // @ts-ignore
  595. it.zOrder = (temp[index - 1] + temp[index - 2]) / 2;
  596. }
  597. }
  598. });
  599. } // Function setAfter()
  600. /**
  601. * 上移一层zorder增加
  602. *
  603. * */
  604. private setAfter(): void {
  605. const arr: SGraphItem[] = this.sortItemList(this.itemList, false);
  606. arr.forEach(it => {
  607. if (it.parent) {
  608. const max = it.zOrder;
  609. let temp = it.parent.children
  610. .map(child => {
  611. if (
  612. Number(child.zOrder.toFixed()) ==
  613. Number(max.toFixed())
  614. ) {
  615. return child.zOrder;
  616. }
  617. })
  618. .filter(c => c);
  619. temp = [...new Set(temp)].reverse();
  620. // 当前层有多个
  621. const index = temp.indexOf(it.zOrder);
  622. if (index <= 1) {
  623. // 当前item 索引为1时 将当前item放至第一个前边
  624. // @ts-ignore
  625. it.zOrder = temp[0] + this.radix;
  626. } else if (index > 1) {
  627. // 当前item 索引大于等于2,将当前item放至前两个中间
  628. // @ts-ignore
  629. it.zOrder = (temp[index - 1] + temp[index - 2]) / 2;
  630. }
  631. }
  632. });
  633. } // Function setBefore()
  634. /**
  635. * 数组排序
  636. *
  637. * @param arr 排序前的数组
  638. * @param flag 正序or逆序
  639. * @return list 排序后的数组
  640. * */
  641. sortItemList(arr: SGraphItem[], flag: boolean = true): SGraphItem[] {
  642. const list: SGraphItem[] = arr.map(t => t);
  643. list.sort(this.compare("zOrder", flag));
  644. return list;
  645. } // Function sortItemList()
  646. /**
  647. * 按照某个属性排序
  648. *
  649. * @param prop 属性
  650. * @param flag 默认正序
  651. * */
  652. private compare(prop: string, flag: boolean): any {
  653. const f = flag ? 1 : -1;
  654. // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
  655. return (a: any, b: any) => {
  656. a = a[prop];
  657. b = b[prop];
  658. if (a < b) {
  659. return f * -1;
  660. }
  661. if (a > b) {
  662. return f * 1;
  663. }
  664. return 0;
  665. };
  666. } // Function compare()
  667. /**
  668. * 防抖-防止快速多次触发 itemChange;只执行最后一次
  669. * */
  670. throttle(fn: Function, wait: number): void {
  671. if (this.timer) clearTimeout(this.timer);
  672. this.timer = setTimeout((): void => {
  673. this.selectChange();
  674. }, wait);
  675. } // Function throttle()
  676. /**
  677. * 选中的item更新时更新自身边界
  678. * */
  679. selectChange(): void {
  680. // 抛出事件
  681. this.$emit("listChange", this.itemList);
  682. if (this.count > 0) {
  683. // 重新计算边界
  684. this.getSize();
  685. // 计算边界8个点
  686. this.calExtreme();
  687. if (
  688. this.count > 1 ||
  689. (this.count == 1 && this.itemList[0].showSelect)
  690. ) {
  691. this.visible = true;
  692. }
  693. } else {
  694. this.visible = false;
  695. this.curIndex = -1;
  696. }
  697. } // Function selectChange()
  698. /**
  699. * 计算选中item的外接矩形和
  700. *
  701. * @return 外接矩形的和
  702. * */
  703. calItemBounding(): SRect {
  704. if (this.itemList.length) {
  705. const fir = this.itemList[0];
  706. let rect = fir.boundingRect().adjusted(fir.pos.x, fir.pos.y, 0, 0);
  707. for (let i = 1; i < this.itemList.length; i++) {
  708. const cur = this.itemList[i];
  709. rect = rect.unioned(
  710. cur.boundingRect().adjusted(cur.pos.x, cur.pos.y, 0, 0)
  711. );
  712. }
  713. return rect;
  714. } else {
  715. return new SRect();
  716. }
  717. } // Function calItemBounding()
  718. /**
  719. * 根据矩阵得到宽高,并将自己移动至矩阵左上角
  720. */
  721. getSize(): void {
  722. const r = this.calItemBounding();
  723. this.width = r.width;
  724. this.height = r.height;
  725. this.moveTo(r.left, r.top);
  726. } // getSize()
  727. /**
  728. * 计算外接矩形 8 个端点
  729. * */
  730. calExtreme(): void {
  731. this.pointList = [];
  732. this.pointList.push(new SPoint(0, 0));
  733. this.pointList.push(new SPoint(this.width / 2, 0));
  734. this.pointList.push(new SPoint(this.width, 0));
  735. this.pointList.push(new SPoint(this.width, this.height / 2));
  736. this.pointList.push(new SPoint(this.width, this.height));
  737. this.pointList.push(new SPoint(this.width / 2, this.height));
  738. this.pointList.push(new SPoint(0, this.height));
  739. this.pointList.push(new SPoint(0, this.height / 2));
  740. } // Function calExtreme()
  741. /**
  742. * 计算点到哪个点
  743. */
  744. getNearestPoint(p: SPoint): void {
  745. let len = this.sceneDis;
  746. this.curIndex = -1;
  747. for (let i = 0; i < this.pointList.length; i++) {
  748. let dis = SMathUtil.pointDistance(
  749. p.x,
  750. p.y,
  751. this.pointList[i].x,
  752. this.pointList[i].y
  753. );
  754. if (dis < len) {
  755. len = dis;
  756. this.curIndex = i;
  757. }
  758. }
  759. } // Function getNearestPoint()
  760. /**
  761. * Item对象边界区域
  762. *
  763. * @return 对象边界区域
  764. */
  765. boundingRect(): SRect {
  766. if (this.visible) {
  767. return new SRect(
  768. -this.sceneDis,
  769. -this.sceneDis,
  770. this.width + this.sceneDis + this.sceneDis,
  771. this.height + this.sceneDis + this.sceneDis
  772. );
  773. } else {
  774. return new SRect();
  775. }
  776. } // Function boundingRect()
  777. /**
  778. * 宽高发发生变化
  779. *
  780. * @param oldSize 改之前的大小
  781. * @param newSize 改之后大小
  782. * */
  783. protected resize(oldSize: SSize, newSize: SSize): void {
  784. for (let v of this.itemList) {
  785. // @ts-ignore
  786. v.resize && v.resize(oldSize, newSize);
  787. const p = v.mapToScene(this.pos.x, this.pos.y);
  788. const p2 = v.mapFromScene(p.x, p.y);
  789. v.moveTo(p2.x - v.boundingRect().x, p2.y - v.boundingRect().y);
  790. }
  791. } // Function resize()
  792. /**
  793. * 鼠标按下事件
  794. *
  795. * @param event 保存事件参数
  796. * @return boolean
  797. */
  798. onMouseDown(event: SMouseEvent): boolean {
  799. if (event.buttons == 1) {
  800. this.getNearestPoint(new SPoint(event.x, event.y));
  801. if (this.curIndex < 0) {
  802. this.curPoint = undefined;
  803. return super.onMouseDown(event);
  804. } else {
  805. const cur = this.pointList[this.curIndex];
  806. this.curPoint = new SPoint(cur.x, cur.y);
  807. this.oldWidth = this.width;
  808. this.oldHeight = this.height;
  809. this.grabItem(this);
  810. }
  811. this.update();
  812. return true;
  813. }
  814. return super.onMouseDown(event);
  815. } // Function onMouseDown()
  816. /**
  817. * 鼠标按下事件
  818. *
  819. * @param event 保存事件参数
  820. * @return boolean
  821. */
  822. onMouseMove(event: SMouseEvent): boolean {
  823. this.setCursor(9);
  824. if (
  825. this.curIndex > -1 &&
  826. this.count == 1 &&
  827. this.itemList[0]._resizeable
  828. ) {
  829. this.setCursor(this.curIndex);
  830. // @ts-ignore
  831. const difX = event.x - this.curPoint.x;
  832. // @ts-ignore
  833. const difY = event.y - this.curPoint.y;
  834. const mp = this.toParentChange(difX, difY);
  835. const oldSize = new SSize(this.width, this.height);
  836. let resultX, resultY;
  837. switch (this.curIndex) {
  838. case 0:
  839. resultX = this.width - difX;
  840. resultY = this.height - difY;
  841. if (resultX > this.minWidth && resultX < this.maxWidth) {
  842. this.width = resultX;
  843. this.x = this.x + mp.x;
  844. }
  845. if (resultY > this.minHeight && resultY < this.maxHeight) {
  846. this.height = resultY;
  847. this.y = this.y + mp.y;
  848. }
  849. break;
  850. case 1:
  851. resultY = this.height - difY;
  852. if (resultY > this.minHeight && resultY < this.maxHeight) {
  853. this.height = resultY;
  854. this.y = this.y + mp.y;
  855. }
  856. break;
  857. case 2:
  858. resultX = this.oldWidth + difX;
  859. resultY = this.height - difY;
  860. if (resultX > this.minWidth && resultX < this.maxWidth) {
  861. this.width = resultX;
  862. }
  863. if (resultY > this.minHeight && resultY < this.maxHeight) {
  864. this.height = resultY;
  865. this.y = this.y + mp.y;
  866. }
  867. break;
  868. case 3:
  869. resultX = this.oldWidth + difX;
  870. if (resultX > this.minWidth && resultX < this.maxWidth) {
  871. this.width = resultX;
  872. }
  873. break;
  874. case 4:
  875. resultX = this.oldWidth + difX;
  876. resultY = this.oldHeight + difY;
  877. if (resultX > this.minWidth && resultX < this.maxWidth) {
  878. this.width = resultX;
  879. }
  880. if (resultY > this.minHeight && resultY < this.maxHeight) {
  881. this.height = resultY;
  882. }
  883. break;
  884. case 5:
  885. resultY = this.oldHeight + difY;
  886. if (resultY > this.minHeight && resultY < this.maxHeight) {
  887. this.height = resultY;
  888. }
  889. break;
  890. case 6:
  891. resultX = this.width - difX;
  892. resultY = this.oldHeight + difY;
  893. if (resultX > this.minWidth && resultX < this.maxWidth) {
  894. this.width = resultX;
  895. this.x = this.x + mp.x;
  896. }
  897. if (resultY > this.minHeight && resultY < this.maxHeight) {
  898. this.height = resultY;
  899. }
  900. break;
  901. case 7:
  902. resultX = this.width - difX;
  903. if (resultX > this.minWidth && resultX < this.maxWidth) {
  904. this.width = resultX;
  905. this.x = this.x + mp.x;
  906. }
  907. break;
  908. default:
  909. break;
  910. }
  911. const newSize = new SSize(this.width, this.height);
  912. this.resize(oldSize, newSize);
  913. this.calExtreme();
  914. } else {
  915. let flag = true;
  916. for (let v of this.itemList) {
  917. if (!v.moveable) {
  918. flag = false;
  919. }
  920. }
  921. if (flag) {
  922. super.onMouseMove(event);
  923. this.setCursor(8);
  924. if (this._isMoving) {
  925. const mp = this.toParentChange(
  926. event.x - this._mouseDownPos.x,
  927. event.y - this._mouseDownPos.y
  928. );
  929. this.itemList.forEach(t => {
  930. t.moveTo(t.pos.x + mp.x, t.pos.y + mp.y);
  931. });
  932. }
  933. }
  934. }
  935. return true;
  936. } // Function onMouseMove()
  937. /**
  938. * 设置鼠标样式
  939. *
  940. * @param n 样式的索引
  941. * */
  942. private setCursor(n: number): void {
  943. this.cursor = SGraphSelectContainer.cursorList[n] || "default";
  944. if (this.scene && this.scene.view) {
  945. this.scene.view.cursor = this.cursor;
  946. }
  947. } // Function setCursor()
  948. /**
  949. * 鼠标按下事件
  950. *
  951. * @param event 保存事件参数
  952. * @return boolean
  953. */
  954. onMouseUp(event: SMouseEvent): boolean {
  955. this.curIndex = -1;
  956. super.onMouseUp(event);
  957. return true;
  958. } // Function onMouseUp()
  959. /**
  960. * Item绘制操作
  961. *
  962. * @param painter painter对象
  963. */
  964. onDraw(painter: SPainter): void {
  965. // 缓存场景长度
  966. this.sceneDis = painter.toPx(this.dis);
  967. // 多选时 或者 只选择一个并且是需要显示选择器时 绘制
  968. if (
  969. this.count > 1 ||
  970. (this.count == 1 && this.itemList[0].showSelect)
  971. ) {
  972. painter.pen.lineWidth = painter.toPx(1);
  973. painter.pen.color = SColor.White;
  974. painter.brush.color = SColor.Transparent;
  975. painter.drawRect(0, 0, this.width, this.height);
  976. painter.pen.color = this.strokeColor;
  977. painter.pen.lineDash = [painter.toPx(4), painter.toPx(4)];
  978. painter.drawRect(0, 0, this.width, this.height);
  979. const r = painter.toPx(this.radius);
  980. painter.pen.lineDash = [];
  981. this.pointList.forEach((t, i) => {
  982. if (i == this.curIndex) {
  983. painter.brush.color = this.fillColor;
  984. } else {
  985. painter.brush.color = SColor.White;
  986. }
  987. painter.drawCircle(t.x, t.y, r);
  988. });
  989. }
  990. } // Function onDraw()
  991. } // Class SGraphSelectContainer