SGraphItem.ts 21 KB

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