SGraphyView.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. import { SMouseEvent, SNetUtil } from "@saga-web/base/lib";
  2. import {
  3. SCanvasView,
  4. SPainter,
  5. SPoint,
  6. SRect,
  7. SSvgPaintEngine
  8. } from "@saga-web/draw/lib";
  9. import { SGraphyScene } from "./SGraphyScene";
  10. import { SGraphyItem } from "./SGraphyItem";
  11. /**
  12. * Graphy图形引擎视图类
  13. *
  14. * @author 庞利祥(sybotan@126.com)
  15. */
  16. export class SGraphyView extends SCanvasView {
  17. /** 场景对象 */
  18. private _scene: SGraphyScene | null = null;
  19. get scene(): SGraphyScene | null {
  20. return this._scene;
  21. } // Get scene
  22. set scene(v: SGraphyScene | null) {
  23. if (this._scene != null) {
  24. this._scene.view = null;
  25. }
  26. this._scene = v;
  27. if (this._scene != null) {
  28. this._scene.view = this;
  29. }
  30. } // Set scene
  31. /**
  32. * 构造函数
  33. *
  34. * @param id 画布对象ID
  35. */
  36. constructor(id: string) {
  37. super(id);
  38. } // Function constructor()
  39. /**
  40. * 保存场景SVG文件
  41. *
  42. * @param name 文件名
  43. * @param width svg宽度
  44. * @param height svg高度
  45. */
  46. saveSceneSvg(name: string, width: number, height: number): void {
  47. let url = URL.createObjectURL(
  48. new Blob([this.sceneSvgData(width, height)], {
  49. type: "text/xml,charset=UTF-8"
  50. })
  51. );
  52. SNetUtil.downLoad(name, url);
  53. } // Function saveSceneSvg()
  54. /**
  55. * 场景SVG图形的数据
  56. *
  57. * @param width svg宽度
  58. * @param height svg高度
  59. * @return URL地址
  60. */
  61. sceneSvgData(width: number, height: number): string {
  62. if (null == this.scene) {
  63. return "";
  64. }
  65. let engine = new SSvgPaintEngine(width, height);
  66. let painter = new SPainter(engine);
  67. // 保存视图缩放比例与原点位置
  68. let s0 = this.scale;
  69. let x0 = this.origin.x;
  70. let y0 = this.origin.y;
  71. // 场景中无对象
  72. let rect = this.scene.allItemRect();
  73. this.fitRectToSize(width, height, rect);
  74. this.onDraw(painter);
  75. // 恢复视图缩放比例与原点位置
  76. this.scale = s0;
  77. this.origin.x = x0;
  78. this.origin.y = y0;
  79. return engine.toSvg();
  80. } // Function saveSvg()
  81. /**
  82. * 适配视图到视图
  83. */
  84. fitSceneToView(): void {
  85. if (null == this.scene) {
  86. return;
  87. }
  88. // 场景中无对象
  89. let rect = this.scene.allItemRect();
  90. this.fitRectToSize(this.width, this.height, rect);
  91. } // Function FitView()
  92. /**
  93. * 适配选中的对象在视图中可见
  94. */
  95. fitSelectedToView(): void {
  96. if (null == this.scene) {
  97. return;
  98. }
  99. // 场景中无对象
  100. let rect = this.scene.selectedItemRect();
  101. this.fitRectToSize(this.width, this.height, rect);
  102. } // Function fitSelectedToSize()
  103. /**
  104. * 适配任意对象在视图中可见
  105. */
  106. fitItemToView(itemList: SGraphyItem[]): void {
  107. if (null == this.scene) {
  108. return;
  109. }
  110. let rect: SRect | null = null;
  111. // 依次取item列中的所有item。将所有item的边界做并焦处理。
  112. for (let item of itemList) {
  113. if (rect == null) {
  114. rect = item.boundingRect().translated(item.pos.x, item.pos.y);
  115. } else {
  116. rect.union(
  117. item.boundingRect().translated(item.pos.x, item.pos.y)
  118. );
  119. }
  120. }
  121. // 场景中无对象
  122. this.fitRectToSize(this.width, this.height, rect);
  123. } // Function fitItemToView()
  124. /**
  125. * 将场景中的xy坐标转换成视图坐标。
  126. *
  127. * @param x 场景中的横坐标
  128. * @param y 场景中的纵坐标
  129. * @return 视图坐标
  130. */
  131. mapFromScene(x: number, y: number): SPoint;
  132. /**
  133. * 将场景中的xy坐标转换成视图坐标。
  134. *
  135. * @param pos 场景中的坐标
  136. * @return 视图坐标
  137. */
  138. mapFromScene(pos: SPoint): SPoint;
  139. /**
  140. * 将场景中的xy坐标转换成视图坐标(重载实现)。
  141. *
  142. * @param x 场景中的横坐标
  143. * @param y 场景中的纵坐标
  144. * @return 视图坐标
  145. */
  146. mapFromScene(x: number | SPoint, y?: number): SPoint {
  147. if (x instanceof SPoint) {
  148. // 如果传入的是SPoint对象
  149. return new SPoint(
  150. x.x * this.scale + this.origin.x,
  151. x.y * this.scale + this.origin.y
  152. );
  153. }
  154. // @ts-ignore
  155. return new SPoint(
  156. x * this.scale + this.origin.x,
  157. (y == undefined ? 0 : y) * this.scale + this.origin.y
  158. );
  159. } // Function mapFromScene()
  160. /**
  161. * 将i视图的xy坐标转换成场景坐标。
  162. *
  163. * @param x 视图横坐标
  164. * @param y 视图纵坐标
  165. * @return 场景坐标
  166. */
  167. mapToScene(x: number, y: number): SPoint;
  168. /**
  169. * 将i视图的xy坐标转换成场景坐标。
  170. *
  171. * @param pos 视图坐标
  172. * @return 场景坐标
  173. */
  174. mapToScene(pos: SPoint): SPoint;
  175. /**
  176. * 将i视图的xy坐标转换成场景坐标。(不推荐在外部调用)
  177. *
  178. * @param x 视图的横坐标/或SPoint对象
  179. * @param y 视图的纵坐标
  180. * @return 场景坐标
  181. */
  182. mapToScene(x: number | SPoint, y?: number): SPoint {
  183. if (x instanceof SPoint) {
  184. // 如果传入的是SPoint对象
  185. return new SPoint(
  186. (x.x - this.origin.x) / this.scale,
  187. (x.y - this.origin.y) / this.scale
  188. );
  189. }
  190. return new SPoint(
  191. (x - this.origin.x) / this.scale,
  192. ((y == undefined ? 0 : y) - this.origin.y) / this.scale
  193. );
  194. } // Function mapToScene()
  195. /**
  196. * 绘制视图
  197. *
  198. * @param painter painter对象
  199. */
  200. protected onDraw(painter: SPainter): void {
  201. painter.save();
  202. painter.clearRect(0, 0, this.width, this.height);
  203. painter.restore();
  204. // 如果未设备场景
  205. if (this.scene == null) {
  206. return;
  207. }
  208. // 绘制背景
  209. painter.save();
  210. this.drawBackground(painter);
  211. painter.restore();
  212. // 绘制场景
  213. painter.save();
  214. painter.translate(this.origin.x, this.origin.y);
  215. painter.scale(this.scale, this.scale);
  216. this.scene.drawScene(painter, new SRect());
  217. painter.restore();
  218. // 绘制前景
  219. painter.save();
  220. this.drawForeground(painter);
  221. painter.restore();
  222. } // Function onDraw();
  223. /**
  224. * 绘制场景背景
  225. *
  226. * @param painter painter对象
  227. */
  228. protected drawBackground(painter: SPainter): void {
  229. // DO NOTHING
  230. } // Function drawBackground()
  231. /**
  232. * 绘制场景前景
  233. *
  234. * @param painter painter对象
  235. */
  236. protected drawForeground(painter: SPainter): void {
  237. // DO NOTHING
  238. } // Function drawForeground()
  239. /**
  240. * 鼠标单击事件
  241. *
  242. * @param event 事件参数
  243. */
  244. protected onClick(event: MouseEvent): void {
  245. if (this.scene != null) {
  246. let ce = this.toSceneMotionEvent(event);
  247. this.scene.onClick(ce);
  248. }
  249. } // Function onClick()
  250. /**
  251. * 鼠标双击事件
  252. *
  253. * @param event 事件参数
  254. */
  255. protected onDoubleClick(event: MouseEvent): void {
  256. if (this.scene != null) {
  257. let ce = this.toSceneMotionEvent(event);
  258. this.scene.onDoubleClick(ce);
  259. }
  260. } // Function onClick()
  261. /**
  262. * 鼠标按下事件
  263. *
  264. * @param event 事件参数
  265. */
  266. protected onMouseDown(event: MouseEvent): void {
  267. super.onMouseDown(event);
  268. if (this.scene != null) {
  269. let ce = this.toSceneMotionEvent(event);
  270. this.scene.onMouseDown(ce);
  271. }
  272. } // Function onClick()
  273. /**
  274. * 鼠标移动事件
  275. *
  276. * @param event 事件参数
  277. */
  278. protected onMouseMove(event: MouseEvent): void {
  279. super.onMouseMove(event);
  280. if (this.scene != null) {
  281. let ce = this.toSceneMotionEvent(event);
  282. this.scene.onMouseMove(ce);
  283. }
  284. } // Function onClick()
  285. /**
  286. * 鼠标松开事件
  287. *
  288. * @param event 事件参数
  289. */
  290. protected onMouseUp(event: MouseEvent): void {
  291. super.onMouseUp(event);
  292. if (this.scene != null) {
  293. let ce = this.toSceneMotionEvent(event);
  294. this.scene.onMouseUp(ce);
  295. }
  296. } // Function onClick()
  297. /**
  298. * 上下文菜单事件
  299. *
  300. * @param event 事件参数
  301. */
  302. protected onContextMenu(event: MouseEvent): void {
  303. if (this.scene != null) {
  304. let ce = this.toSceneMotionEvent(event);
  305. this.scene.onContextMenu(ce);
  306. }
  307. } // Function onContextMenu()
  308. /**
  309. * 按键按下事件
  310. *
  311. * @param event 事件参数
  312. */
  313. protected onKeyDown(event: KeyboardEvent): void {
  314. if (this.scene != null) {
  315. this.scene.onKeyDown(event);
  316. }
  317. } // Function onKeyDown()
  318. // /**
  319. // * 按键press事件
  320. // *
  321. // * @param event 事件参数
  322. // */
  323. // protected onKeyPress(event: KeyboardEvent): void {
  324. // if (this.scene != null) {
  325. // this.scene.onKeyPress(event);
  326. // }
  327. // } // Function onKeyPress()
  328. /**
  329. * 按键松开事件
  330. *
  331. * @param event 事件参数
  332. */
  333. protected onKeyUp(event: KeyboardEvent): void {
  334. if (this.scene != null) {
  335. this.scene.onKeyUp(event);
  336. }
  337. } // Function onKeyUp()
  338. /**
  339. * 适配场景在视图中可见
  340. *
  341. * @param width 宽度
  342. * @param height 高度
  343. * @param rect 对象的矩阵大小
  344. */
  345. private fitRectToSize(
  346. width: number,
  347. height: number,
  348. rect: SRect | null
  349. ): void {
  350. // 未设置场景
  351. if (null == rect || !rect.isValid()) {
  352. return;
  353. }
  354. this.scale = Math.min(width / rect.width, height / rect.height) * 0.8;
  355. // 计算场景中心点
  356. let center = rect.center();
  357. this.origin.x = width / 2.0 - center.x * this.scale;
  358. this.origin.y = height / 2.0 - center.y * this.scale;
  359. } // Function fitRectToSize()
  360. /**
  361. * MouseEvent事件转换成场景SMouseEvent事件
  362. *
  363. * @param event 事件参数
  364. */
  365. private toSceneMotionEvent(event: MouseEvent): SMouseEvent {
  366. let se = new SMouseEvent(event);
  367. se.x = (se.x - this.origin.x) / this.scale;
  368. se.y = (se.y - this.origin.y) / this.scale;
  369. return se;
  370. } // Function toSceneMotionEvent()
  371. } // Class SGraphyView