SGraphItem.ts 19 KB

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