SGraphItem.ts 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748
  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) 2016-2020. 博锐尚格科技股份有限公司
  21. * ~8888'
  22. * .!88~ All rights reserved.
  23. *
  24. * *********************************************************************************************************************
  25. */
  26. import {
  27. SMouseButton,
  28. SMouseEvent,
  29. SObject,
  30. SMatrix
  31. } from "@persagy-web/base/lib";
  32. import { SPainter, SPoint, SRect } from "@persagy-web/draw/lib";
  33. import { SGraphScene } from "./SGraphScene";
  34. /**
  35. * Graph图形引擎Item类
  36. *
  37. * @author 庞利祥(sybotan@126.com)
  38. */
  39. export class SGraphItem extends SObject {
  40. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  41. // 属性
  42. /** 场景对象 */
  43. private _scene: SGraphScene | null = null;
  44. get scene(): SGraphScene | null {
  45. if (null != this._parent) {
  46. return this._parent.scene;
  47. } else {
  48. return this._scene;
  49. }
  50. } // Get scene
  51. set scene(v: SGraphScene | null) {
  52. this._scene = v;
  53. this.update();
  54. } // Set scene
  55. /** parent属性存值函数 */
  56. private _parent: SGraphItem | null = null;
  57. get parent(): SGraphItem | null {
  58. return this._parent;
  59. } // Get parent
  60. set parent(v: SGraphItem | null) {
  61. // 如果parent未变更
  62. if (this.parent == v) {
  63. return;
  64. }
  65. // 如果原parent不为空
  66. if (this._parent != null) {
  67. // 将节点从原parent节点中摘除
  68. let i = this._parent.children.indexOf(this);
  69. this._parent.children.splice(i, 1);
  70. }
  71. this._parent = v;
  72. this.update();
  73. // 如果新parent不为空
  74. if (this._parent != null) {
  75. // 将节点加入到新parent节点中
  76. this._parent.children.push(this);
  77. this._parent.children.sort(SGraphItem.sortItemZOrder);
  78. }
  79. } // Set parent()
  80. /** 子节点 */
  81. children: SGraphItem[] = [];
  82. /** Z轴顺序 */
  83. private _zOrder: number = 0;
  84. get zOrder(): number {
  85. return this._zOrder;
  86. } // Get zOrder
  87. set zOrder(v: number) {
  88. this._zOrder = v;
  89. if (this._parent != null) {
  90. this._parent.children.sort(SGraphItem.sortItemZOrder);
  91. }
  92. this.update();
  93. } // Set zOrder
  94. /** 位置 */
  95. pos: SPoint = new SPoint(0, 0);
  96. /** X轴坐标 */
  97. get x(): number {
  98. return this.pos.x;
  99. } // Get x
  100. set x(v: number) {
  101. if (this.pos.x == v) {
  102. return;
  103. }
  104. let old = this.pos.x;
  105. this.pos.x = v;
  106. this.update();
  107. } // Set x
  108. /** Y轴坐标 */
  109. get y(): number {
  110. return this.pos.y;
  111. } // Get y
  112. set y(v: number) {
  113. if (this.pos.y == v) {
  114. return;
  115. }
  116. let old = this.pos.y;
  117. this.pos.y = v;
  118. this.update();
  119. } // Set y
  120. /** 缩放比例 */
  121. scale: number = 1;
  122. /** 放缩反比例 */
  123. private _inverseScale: number = 1;
  124. get inverseScale(): number {
  125. return this._inverseScale;
  126. } // Get inverseScale
  127. /** 旋转角度 */
  128. _rotate: number = 0;
  129. get rotate(): number {
  130. return this._rotate;
  131. } // Get rotate
  132. set rotate(v: number) {
  133. this._rotate = v;
  134. this.update();
  135. } // Set rotate
  136. /** 是否可见 */
  137. private _visible: boolean = true;
  138. get visible(): boolean {
  139. return this._visible;
  140. } // Get visible
  141. set visible(v: boolean) {
  142. this._visible = v;
  143. this.update();
  144. } // Set visible
  145. /** 是否可以移动 */
  146. moveable: boolean = false;
  147. /** 是否正在移动 */
  148. private _isMoving = false;
  149. /** 是否可用 */
  150. enabled: boolean = true;
  151. /** 是否可被选中 */
  152. selectable = false;
  153. /** 是否被选中 */
  154. protected _selected = false;
  155. get selected(): boolean {
  156. return this._selected && this.selectable && this.enabled;
  157. } // Get selected
  158. set selected(value: boolean) {
  159. // 如果选择状态未变更
  160. if (this.selected == value) {
  161. return;
  162. }
  163. this._selected = value;
  164. this.update();
  165. } // Set selected
  166. /** 是否进行变形 */
  167. isTransform = true;
  168. /** 鼠标按下时位置 */
  169. private _mouseDownPos = new SPoint();
  170. /** 鼠标样式 */
  171. cursor: string = "default";
  172. /** 保存上一次的grabitem */
  173. private _lastGrab: SGraphItem | null = null;
  174. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  175. // 函数
  176. /**
  177. * 构造函数
  178. *
  179. * @param parent 指向父对象
  180. */
  181. constructor(parent: SGraphItem | null = null) {
  182. super();
  183. if (parent) {
  184. this.parent = parent;
  185. }
  186. } // Function constructor()
  187. /**
  188. * Item绘制框架
  189. *
  190. * @param painter painter对象
  191. * @param rect 绘制区域
  192. */
  193. onPaint(painter: SPainter, rect: SRect): void {
  194. this.onDraw(painter);
  195. for (let item of this.children) {
  196. // 如果item不可见
  197. if (!item.visible) {
  198. continue;
  199. }
  200. // item所占矩阵与要刷新的矩阵相交则刷新,否则不刷
  201. // if (item.boundingRect()--rect){
  202. //
  203. // }
  204. // 保存画布状态
  205. painter.save();
  206. try {
  207. // item位移到指定位置绘制
  208. painter.translate(item.x, item.y);
  209. painter.scale(item.scale, item.scale);
  210. painter.rotate(item.rotate);
  211. // 如果不进行变形处理,则取消painter的变型操作
  212. if (!item.isTransform) {
  213. let matrix = painter.worldTransform;
  214. item._inverseScale = 1.0 / matrix.a;
  215. painter.scale(item._inverseScale, item._inverseScale);
  216. }
  217. // 设置绘制区域
  218. // canvas.clip(item.boundingRect())
  219. // rect 调整 宽度高度取最小值 根据pos位移
  220. // 绘制item
  221. item.onPaint(painter, rect);
  222. } catch (e) {
  223. console.log(e);
  224. }
  225. // 恢复画布状态
  226. painter.restore();
  227. }
  228. } // Function onPaint()
  229. /**
  230. * Item绘制操作
  231. *
  232. * @param painter painter对象
  233. */
  234. onDraw(painter: SPainter): void {} // Function onDraw()
  235. /**
  236. * 隐藏对象
  237. */
  238. hide(): void {
  239. this.visible = false;
  240. } // Function hide()
  241. /**
  242. * 显示对象
  243. */
  244. show(): void {
  245. this.visible = true;
  246. } // Function show()
  247. /**
  248. * 更新Item
  249. */
  250. update(): void {
  251. if (null != this.scene) {
  252. const view = this.scene.view;
  253. if (null != view) {
  254. view.update();
  255. }
  256. }
  257. } // Function update()
  258. /**
  259. * Item对象边界区域
  260. *
  261. * @return 对象边界区域
  262. */
  263. boundingRect(): SRect {
  264. return new SRect(0, 0, 10, 10);
  265. } // Function boundingRect()
  266. /**
  267. * 移动item到指定位置
  268. *
  269. * @param x 新位置的x坐标
  270. * @param y 新位置的y坐标
  271. */
  272. moveTo(x: number, y: number): void {
  273. this.x = x;
  274. this.y = y;
  275. } // moveTo()
  276. /**
  277. * 判断item是否包含点x,y
  278. *
  279. * @param x 横坐标(当前item)
  280. * @param y 纵坐标(当前item)
  281. *
  282. * @return boolean
  283. */
  284. contains(x: number, y: number): boolean {
  285. return this.boundingRect().contains(x, y);
  286. } // Function contains()
  287. /**
  288. * 获得item的路径节点列表。(该节点被加载到场景中,如果未被加载到场景中,计算会出错)
  289. *
  290. * @return *[]
  291. */
  292. itemPath(): SGraphItem[] {
  293. if (this.parent != null) {
  294. let list = this.parent.itemPath();
  295. list.push(this);
  296. return list;
  297. }
  298. return [this];
  299. } // Function itemPath()
  300. /**
  301. * 将场景中的xy坐标转换成item坐标。(该节点被加载到场景中,如果未被加载到场景中,计算会出错)
  302. *
  303. * @param x 场景中的横坐标
  304. * @param y 场景中的纵坐标
  305. *
  306. * @return 在item中的坐标
  307. */
  308. mapFromScene(x: number, y: number): SPoint {
  309. const m = this.scene2itemMattrix();
  310. return new SPoint(x, y).matrixTransform(m.inversed());
  311. } // Function mapFromScene()
  312. /**
  313. * 将item中的xy坐标转换成场景坐标。(该节点被加载到场景中,如果未被加载到场景中,计算会出错)
  314. *
  315. * @param x item中的横坐标
  316. * @param y item中的纵坐标
  317. *
  318. * @return 在场景中的坐标
  319. */
  320. mapToScene(x: number, y: number): SPoint {
  321. if (this.parent == null) {
  322. return new SPoint(x, y);
  323. }
  324. const m = this.scene2itemMattrix();
  325. return new SPoint(x, y).matrixTransform(m);
  326. } // Function mapToScene()
  327. /**
  328. * 场景对象到item对象的转换矩阵
  329. *
  330. * @return 转换矩阵
  331. */
  332. scene2itemMattrix(): SMatrix {
  333. let m = new SMatrix();
  334. let list = this.itemPath();
  335. for (let item of list) {
  336. m.translate(item.x, item.y);
  337. m.scale(item.scale, item.scale);
  338. m.rotate(item.rotate);
  339. // 如果不进行变形处理,则取消painter的变型操作
  340. if (!item.isTransform) {
  341. m.scale(item._inverseScale, item._inverseScale);
  342. }
  343. }
  344. return m;
  345. }
  346. // =================================================================================================================
  347. // 事件
  348. /**
  349. * 鼠标单击事件
  350. *
  351. * @param event 保存事件参数
  352. * @return boolean
  353. */
  354. onClick(event: SMouseEvent): boolean {
  355. for (let i = this.children.length - 1; i >= 0; i--) {
  356. let item = this.children[i];
  357. if (!this.acceptEvent() || !item.visible) {
  358. continue;
  359. }
  360. let ce = SGraphItem.toChildMouseEvent(item, event);
  361. if (item.contains(ce.x, ce.y) && item.onClick(ce)) {
  362. // 如果点在子项目上且子项目处理了事件
  363. return true;
  364. }
  365. }
  366. return false;
  367. } // Function onClick()
  368. /**
  369. * 鼠标双击事件
  370. *
  371. * @param event 保存事件参数
  372. * @return boolean
  373. */
  374. onDoubleClick(event: SMouseEvent): boolean {
  375. for (let i = this.children.length - 1; i >= 0; i--) {
  376. let item = this.children[i];
  377. if (!this.acceptEvent() || !item.visible) {
  378. continue;
  379. }
  380. let ce = SGraphItem.toChildMouseEvent(item, event);
  381. if (item.contains(ce.x, ce.y) && item.onDoubleClick(ce)) {
  382. // 如果点在子项目上且子项目处理了事件
  383. return true;
  384. }
  385. }
  386. return false;
  387. } // Function onDoubleClick()
  388. /**
  389. * 鼠标按下事件
  390. *
  391. * @param event 保存事件参数
  392. * @return boolean
  393. */
  394. onMouseDown(event: SMouseEvent): boolean {
  395. for (let i = this.children.length - 1; i >= 0; i--) {
  396. try {
  397. let item = this.children[i];
  398. if (!this.acceptEvent() || !item.visible) {
  399. continue;
  400. }
  401. let ce = SGraphItem.toChildMouseEvent(item, event);
  402. if (item.contains(ce.x, ce.y) && item.onMouseDown(ce)) {
  403. // 如果点在子项目上且子项目处理了事件
  404. return true;
  405. }
  406. } catch (e) {
  407. console.log(e);
  408. }
  409. }
  410. // 选择
  411. let select = false;
  412. if (this.selectable) {
  413. select = this.clickSelect(event);
  414. }
  415. // 移动
  416. if (this.moveable) {
  417. this._mouseDownPos = new SPoint(event.x, event.y);
  418. this._isMoving = true;
  419. if (this.scene) {
  420. this._lastGrab = this.scene.grabItem;
  421. }
  422. this.grabItem(this);
  423. return true;
  424. }
  425. return select;
  426. } // Function onMouseDown()
  427. /**
  428. * 鼠标移动事件
  429. *
  430. * @param event 保存事件参数
  431. * @return boolean
  432. */
  433. onMouseMove(event: SMouseEvent): boolean {
  434. for (let i = this.children.length - 1; i >= 0; i--) {
  435. let item = this.children[i];
  436. if (!this.acceptEvent() || !item.visible) {
  437. continue;
  438. }
  439. let ce = SGraphItem.toChildMouseEvent(item, event);
  440. if (item.contains(ce.x, ce.y) && item.onMouseMove(ce)) {
  441. // 如果点在子项目上且子项目处理了事件
  442. return true;
  443. }
  444. }
  445. if (this.scene && this.scene.view) {
  446. this.scene.view.cursor = this.cursor;
  447. }
  448. if (
  449. event.buttons & SMouseButton.LeftButton &&
  450. this.moveable &&
  451. this._isMoving
  452. ) {
  453. let old = new SPoint(this.pos.x, this.pos.y);
  454. const mp = this.toParentChange(
  455. event.x - this._mouseDownPos.x,
  456. event.y - this._mouseDownPos.y
  457. );
  458. this.moveTo(this.pos.x + mp.x, this.pos.y + mp.y);
  459. this.$emit("onMove", old, this.pos);
  460. }
  461. // 处理hover
  462. const scene = this.scene;
  463. if (null != scene) {
  464. if (scene.hoverItem == null || scene.hoverItem !== this) {
  465. if (scene.hoverItem != null) {
  466. scene.hoverItem.onMouseLeave(event);
  467. }
  468. this.onMouseEnter(event);
  469. scene.hoverItem = this;
  470. }
  471. }
  472. return true;
  473. } // Function onMouseMove()
  474. /**
  475. * 释放鼠标事件
  476. *
  477. * @param event 保存事件参数
  478. * @return boolean
  479. */
  480. onMouseUp(event: SMouseEvent): boolean {
  481. for (let i = this.children.length - 1; i >= 0; i--) {
  482. let item = this.children[i];
  483. if (!this.acceptEvent() || !item.visible) {
  484. continue;
  485. }
  486. let ce = SGraphItem.toChildMouseEvent(item, event);
  487. if (item.contains(ce.x, ce.y) && item.onMouseUp(ce)) {
  488. // 如果点在子项目上且子项目处理了事件
  489. return true;
  490. }
  491. }
  492. this._isMoving = false;
  493. this.releaseItem();
  494. if (this.scene) {
  495. this.scene.grabItem = this._lastGrab;
  496. }
  497. return false;
  498. } // Function onMouseUp()
  499. /**
  500. * 鼠标进入事件
  501. *
  502. * @param event 保存事件参数
  503. * @return boolean
  504. */
  505. onMouseEnter(event: SMouseEvent): boolean {
  506. return false;
  507. } // Function onMouseEnter()
  508. /**
  509. * 鼠标离开事件
  510. *
  511. * @param event 保存事件参数
  512. * @return boolean
  513. */
  514. onMouseLeave(event: SMouseEvent): boolean {
  515. return false;
  516. } // Function onMouseLeave()
  517. /**
  518. * 上下文菜单事件
  519. *
  520. * @param event 事件参数
  521. */
  522. onContextMenu(event: SMouseEvent): boolean {
  523. for (let i = this.children.length - 1; i >= 0; i--) {
  524. let item = this.children[i];
  525. if (!this.acceptEvent() || !item.visible) {
  526. continue;
  527. }
  528. let ce = SGraphItem.toChildMouseEvent(item, event);
  529. if (item.contains(ce.x, ce.y) && item.onContextMenu(ce)) {
  530. // 如果点在子项目上且子项目处理了事件
  531. return true;
  532. }
  533. }
  534. return false;
  535. } // Function onContextMenu()
  536. /**
  537. * 按键按下事件
  538. *
  539. * @param event 事件参数
  540. */
  541. onKeyDown(event: KeyboardEvent): void {} // Function onKeyDown()
  542. /**
  543. * 按键press事件
  544. *
  545. * @param event 事件参数
  546. */
  547. onKeyPress(event: KeyboardEvent): void {} // Function onKeyPress()
  548. /**
  549. * 按键松开事件
  550. *
  551. * @param event 事件参数
  552. */
  553. onKeyUp(event: KeyboardEvent): void {} // Function onKeyUp()
  554. /**
  555. * 取消操作item事件
  556. *
  557. * */
  558. cancelOperate(): void {} // Function cancelOperate()
  559. /**
  560. * 返回item对应的数据对象
  561. *
  562. * */
  563. toData(): any | null {
  564. return null;
  565. } // Function toData()
  566. /**
  567. * 移动item后的处理,计算其他信息,将原点置为场景原点
  568. *
  569. * @param x x坐标
  570. * @param y y坐标
  571. * */
  572. moveToOrigin(x: number, y: number): void {
  573. if (this.children && this.children.length) {
  574. this.children.forEach(it => {
  575. it.moveToOrigin(x, y);
  576. });
  577. }
  578. } // Function moveToOrigin()
  579. // =================================================================================================================
  580. // 私有方法
  581. /**
  582. * 按ZOrder排序
  583. *
  584. * @param a 比较元素1
  585. * @param b 比较元素2
  586. * @return {number}
  587. */
  588. private static sortItemZOrder(a: SGraphItem, b: SGraphItem): number {
  589. return a.zOrder - b.zOrder;
  590. } // Function sortItemZOrder()
  591. /**
  592. * 鼠标事件转子对象鼠标事件
  593. *
  594. * @param child 子对象
  595. * @param event 事件参数
  596. * @return 子对象鼠标事件
  597. */
  598. private static toChildMouseEvent(
  599. child: SGraphItem,
  600. event: SMouseEvent
  601. ): SMouseEvent {
  602. let ce = new SMouseEvent(event);
  603. ce.matrix.translate(child.x, child.y);
  604. ce.matrix.scale(child.scale, child.scale);
  605. ce.matrix.rotate(0, 0, child.rotate);
  606. if (!child.isTransform) {
  607. ce.matrix.scale(child._inverseScale, child._inverseScale);
  608. }
  609. const mp = new SPoint(event.offsetX, event.offsetY).matrixTransform(
  610. ce.matrix.inversed()
  611. );
  612. ce.x = mp.x;
  613. ce.y = mp.y;
  614. return ce;
  615. } // Function toChildMouseEvent()
  616. /**
  617. * 锁定item
  618. *
  619. * @param item 被锁定的item
  620. */
  621. protected grabItem(item: SGraphItem): void {
  622. if (this.scene != null) {
  623. this.scene.grabItem = item;
  624. }
  625. } // Function grabItem
  626. /**
  627. * 释放被锁定的item
  628. */
  629. protected releaseItem(): void {
  630. if (this.scene != null) {
  631. this.scene.grabItem = null;
  632. }
  633. } // Function grabItem
  634. /**
  635. * 计算点在父节点的位置
  636. *
  637. * @param x X轴
  638. * @param y Y轴
  639. * @return 在父节点的位置
  640. */
  641. private toParentChange(x: number, y: number): SPoint {
  642. const m = new SMatrix();
  643. m.scale(this.scale, this.scale);
  644. if (!this.isTransform) {
  645. m.scale(this._inverseScale, this._inverseScale);
  646. }
  647. m.rotate(this.rotate);
  648. const mp = new SPoint(x, y).matrixTransform(m);
  649. return new SPoint(mp.x, mp.y);
  650. }
  651. /**
  652. * 判断是否处理事件
  653. *
  654. * @return true: 处理事件,否则不处理
  655. */
  656. private acceptEvent(): boolean {
  657. return this.visible && this.enabled;
  658. } // Function acceptEvent()
  659. /**
  660. * 点选item对象
  661. *
  662. * @param event 事件参数
  663. */
  664. private clickSelect(event: SMouseEvent): boolean {
  665. // 如果Item不可被选中,或没有按下鼠标左键,则直接返回
  666. if (!this.selectable || event.buttons != SMouseButton.LeftButton) {
  667. return false;
  668. }
  669. const scene = this.scene;
  670. if (scene == null) {
  671. return false;
  672. }
  673. // 如果按下Ctrl键,只改变当前item的选择标志
  674. if (event.ctrlKey) {
  675. scene.selectContainer.toggleItem(this);
  676. } else {
  677. scene.selectContainer.setItem(this);
  678. }
  679. return true;
  680. } // Function clickSelect()
  681. } // Class SGraphItem