SEditPolygon.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. <template>
  2. <div>
  3. <el-button @click="show">展示</el-button>
  4. <el-button @click="create">创建</el-button>
  5. <el-button @click="edit">编辑</el-button>
  6. <canvas id="editPolygon" width="800" height="400" tabindex="0"/>
  7. </div>
  8. </template>
  9. <script lang='ts'>
  10. import { SGraphItem, SGraphScene, SGraphView } from "@persagy-web/graph/";
  11. import { SMouseEvent } from "@persagy-web/base/";
  12. import { SColor, SLine, SLineCapStyle, SPainter, SPoint, SRect } from "@persagy-web/draw";
  13. import { SRelationState } from "@persagy-web/big/lib/enums/SRelationState";
  14. import { SMathUtil } from "@persagy-web/big/lib/utils/SMathUtil";
  15. /**
  16. * 可编辑多边形示例
  17. *
  18. * @author 郝洁 <haojie@persagy.com>
  19. */
  20. export default {
  21. data() {
  22. return {
  23. scene: null,
  24. view: null
  25. };
  26. },
  27. /**
  28. * 页面挂载
  29. */
  30. mounted(): void {
  31. this.view = new SGraphView("editPolygon");
  32. },
  33. methods: {
  34. show() {
  35. const scene = new SGraphScene();
  36. this.view.scene = scene;
  37. const spolygonItem = new SPolygonItem(null);
  38. spolygonItem.setStatus = SRelationState.Normal;
  39. const PointList: SPoint[] = [
  40. new SPoint(0, 0),
  41. new SPoint(50, 0),
  42. new SPoint(50, 50),
  43. new SPoint(0, 50)
  44. ];
  45. spolygonItem.setPointList = PointList;
  46. scene.addItem(spolygonItem);
  47. this.view.fitSceneToView();
  48. },
  49. create() {
  50. const scene = new SGraphScene();
  51. this.view.scene = scene;
  52. const spolygonItem = new SPolygonItem(null);
  53. spolygonItem.setStatus = SRelationState.Create;
  54. scene.addItem(spolygonItem);
  55. scene.grabItem = spolygonItem;
  56. this.view.fitSceneToView();
  57. },
  58. edit() {
  59. const scene = new SGraphScene();
  60. this.view.scene = scene;
  61. const spolygonItem = new SPolygonItem(null);
  62. spolygonItem.setStatus = SRelationState.Edit;
  63. const PointList: SPoint[] = [
  64. new SPoint(0, 0),
  65. new SPoint(50, 0),
  66. new SPoint(50, 60),
  67. new SPoint(0, 50)
  68. ];
  69. spolygonItem.setPointList = PointList;
  70. scene.addItem(spolygonItem);
  71. scene.grabItem = spolygonItem;
  72. this.view.fitSceneToView();
  73. }
  74. }
  75. };
  76. /**
  77. * @author hanyaolong
  78. */
  79. class SPolygonItem extends SGraphItem {
  80. /** X坐标最小值 */
  81. private minX = Number.MAX_SAFE_INTEGER;
  82. /** X坐标最大值 */
  83. private maxX = Number.MIN_SAFE_INTEGER;
  84. /** Y坐标最小值 */
  85. private minY = Number.MAX_SAFE_INTEGER;
  86. /** Y坐标最大值 */
  87. private maxY = Number.MIN_SAFE_INTEGER;
  88. /** 轮廓线坐标 */
  89. private pointList: SPoint[] = [];
  90. // 获取当前状态
  91. get getPointList(): SPoint[] {
  92. return this.pointList;
  93. }
  94. // 编辑当前状态
  95. set setPointList(arr: SPoint[]) {
  96. this.pointList = arr;
  97. if (arr) {
  98. this._xyzListToSPointList(arr);
  99. }
  100. this.update();
  101. }
  102. /** 是否闭合 */
  103. closeFlag: boolean = false;
  104. // 当前状态 1:show 2 创建中,3 编辑中
  105. _status: number = SRelationState.Normal;
  106. // 获取当前状态
  107. get getStatus(): number {
  108. return this._status;
  109. }
  110. // 编辑当前状态
  111. set setStatus(value: number) {
  112. this._status = value;
  113. this.update();
  114. }
  115. /** 边框颜色 */
  116. borderColor: SColor = new SColor("#0091FF");
  117. /** 填充颜色 */
  118. brushColor: SColor = new SColor("#1EE887");
  119. /** border宽 只可输入像素宽*/
  120. borderLine: number = 4;
  121. /** 鼠标移动点 */
  122. private lastPoint = new SPoint();
  123. /** 当前鼠标获取顶点对应索引 */
  124. private curIndex: number = -1;
  125. /** 灵敏像素 */
  126. private len: number = 15;
  127. /** 场景像素 */
  128. private scenceLen: number = 15;
  129. /** 场景像素 */
  130. private isAlt: boolean = false;
  131. /**
  132. * 构造函数
  133. *
  134. * @param parent 指向父对象
  135. */
  136. constructor(parent: SGraphItem | null) {
  137. super(parent);
  138. }
  139. ///////////////以下为对pointList 数组的操作方法
  140. /**
  141. * 储存新的多边形顶点
  142. * @param x 点位得x坐标
  143. * @param y 点位得y坐标
  144. * @param i 储存所在索引
  145. * @return SPoint
  146. */
  147. insertPoint(x: number, y: number, i: number | null = null): SPoint {
  148. const point = new SPoint(x, y);
  149. if (i == null) {
  150. this.pointList.push(point);
  151. } else {
  152. this.pointList.splice(i, 0, point);
  153. }
  154. this.update();
  155. return point;
  156. }
  157. /**
  158. * 删除点位
  159. * @param i 删除点所在的索引
  160. * @return SPoint
  161. */
  162. deletePoint(i: number | null = null): SPoint | null | undefined {
  163. let point = null;
  164. if (i != null) {
  165. if (this.pointList.length - 1 >= i) {
  166. point = this.pointList[i];
  167. this.pointList.splice(i, 1);
  168. } else {
  169. point = null;
  170. }
  171. } else {
  172. point = this.pointList.pop();
  173. }
  174. this.update();
  175. return point;
  176. }
  177. /**
  178. * 多边形顶点的移动位置
  179. * @param x 点位得x坐标
  180. * @param y 点位得y坐标
  181. * @param i 点位得i坐标
  182. * @return SPoint
  183. */
  184. movePoint(x: number, y: number, i: number): SPoint | null | undefined {
  185. let point = null;
  186. if (this.pointList.length) {
  187. this.pointList[i].x = x;
  188. this.pointList[i].y = y;
  189. }
  190. point = this.pointList[i];
  191. return point;
  192. }
  193. /**
  194. * 打印出多边形数组
  195. * @return SPoint[]
  196. */
  197. PrintPointList(): SPoint[] {
  198. return this.pointList;
  199. }
  200. ///////////////////////////
  201. /**
  202. * xyz数据转换为SPoint格式函数;获取外接矩阵参数
  203. *
  204. * @param arr 外层传入的数据PointList
  205. */
  206. protected _xyzListToSPointList(arr: SPoint[]): void {
  207. if (arr.length) {
  208. this.pointList = arr.map(it => {
  209. let x = it.x,
  210. y = it.y;
  211. if (x < this.minX) {
  212. this.minX = x;
  213. }
  214. if (y < this.minY) {
  215. this.minY = y;
  216. }
  217. if (x > this.maxX) {
  218. this.maxX = x;
  219. }
  220. if (y > this.maxY) {
  221. this.maxY = y;
  222. }
  223. return new SPoint(x, y);
  224. });
  225. } else {
  226. this.pointList = [];
  227. }
  228. }
  229. ////////////以下为三种状态下的绘制多边形方法
  230. /**
  231. * 展示状态 --绘制多边形数组
  232. * @param painter 绘制类
  233. * @param pointList 绘制多边形数组
  234. *
  235. */
  236. protected drawShowPolygon(painter: SPainter, pointList: SPoint[]): void {
  237. painter.pen.lineCapStyle = SLineCapStyle.Square;
  238. painter.pen.color = this.borderColor;
  239. painter.pen.lineWidth = painter.toPx(this.borderLine);
  240. painter.brush.color = this.brushColor;
  241. painter.drawPolygon([...pointList]);
  242. }
  243. /**
  244. *
  245. * 创建状态 --绘制多边形数组
  246. * @param painter 绘制类
  247. * @param pointList 绘制多边形数组
  248. *
  249. */
  250. protected drawCreatePolygon(painter: SPainter, pointList: SPoint[]): void {
  251. painter.pen.lineCapStyle = SLineCapStyle.Square;
  252. painter.pen.color = this.borderColor;
  253. painter.pen.lineWidth = painter.toPx(4);
  254. painter.drawPolyline(pointList);
  255. if (this.lastPoint) {
  256. painter.drawLine(pointList[pointList.length - 1], this.lastPoint);
  257. }
  258. painter.pen.color = SColor.Transparent;
  259. painter.brush.color = this.brushColor;
  260. painter.pen.lineWidth = painter.toPx(4);
  261. painter.drawPolygon([...pointList, this.lastPoint]);
  262. }
  263. /**
  264. *
  265. * 编辑状态 --绘制多边形数组
  266. *
  267. * @param painter 绘制类
  268. * @param pointList 绘制多边形数组
  269. *
  270. */
  271. protected drawEditPolygon(painter: SPainter, pointList: SPoint[]): void {
  272. // 展示多边形
  273. this.drawShowPolygon(painter, pointList);
  274. // 绘制顶点块
  275. pointList.forEach((item, index) => {
  276. painter.drawCircle(item.x, item.y, painter.toPx(8));
  277. });
  278. }
  279. ////////////////////////////////
  280. /**
  281. * 编辑状态操作多边形数组
  282. *
  283. * @param event 鼠标事件
  284. *
  285. *
  286. */
  287. protected editPolygonPoint(event: SMouseEvent): void {
  288. // 判断是否为删除状态 isAlt = true为删除状态
  289. if (this.isAlt) {
  290. // 1 判断是否点击在多边形顶点
  291. let lenIndex = -1; // 当前点击到的点位索引;
  292. let curenLen = this.scenceLen; // 当前的灵敏度
  293. this.pointList.forEach((item, index) => {
  294. let dis = SMathUtil.pointDistance(event.x, event.y, item.x, item.y);
  295. if (dis < curenLen) {
  296. curenLen = dis;
  297. lenIndex = index;
  298. }
  299. });
  300. // 若点击到,对该索引对应的点做删除
  301. if (lenIndex != -1) {
  302. if (this.pointList.length <= 3) {
  303. return;
  304. }
  305. this.deletePoint(lenIndex);
  306. }
  307. } else {
  308. // 1 判断是否点击在多边形顶点
  309. this.curIndex = -1;
  310. let lenIndex = -1; // 当前点击到的点位索引;
  311. let curenLen = this.scenceLen; // 当前的灵敏度
  312. this.pointList.forEach((item, index) => {
  313. let dis = SMathUtil.pointDistance(event.x, event.y, item.x, item.y);
  314. if (dis < curenLen) {
  315. curenLen = dis;
  316. lenIndex = index;
  317. }
  318. });
  319. this.curIndex = lenIndex;
  320. // 2判断是否点击在多边形得边上
  321. if (-1 == lenIndex) {
  322. let len = SMathUtil.pointToLine(
  323. new SPoint(event.x, event.y),
  324. new SLine(this.pointList[0], this.pointList[1])
  325. ),
  326. index = 0;
  327. if (this.pointList.length > 2) {
  328. for (let i = 1; i < this.pointList.length; i++) {
  329. let dis = SMathUtil.pointToLine(
  330. new SPoint(event.x, event.y),
  331. new SLine(this.pointList[i], this.pointList[i + 1])
  332. );
  333. if (i + 1 == this.pointList.length) {
  334. dis = SMathUtil.pointToLine(
  335. new SPoint(event.x, event.y),
  336. new SLine(this.pointList[i], this.pointList[0])
  337. );
  338. }
  339. if (dis.MinDis < len.MinDis) {
  340. len = dis;
  341. index = i;
  342. }
  343. }
  344. }
  345. // 如果再线上则为新增点
  346. if (len.Point) {
  347. if (len.MinDis <= this.scenceLen) {
  348. this.pointList.splice(index + 1, 0, len.Point);
  349. }
  350. }
  351. }
  352. // 刷新视图
  353. this.update();
  354. }
  355. }
  356. ///////////////////////////////以下为鼠标事件
  357. /**
  358. * 鼠标双击事件
  359. *
  360. * @param event 事件参数
  361. * @return boolean
  362. */
  363. onDoubleClick(event: SMouseEvent): boolean {
  364. // 如果位show状态 双击改对象则需改为编辑状态
  365. if (SRelationState.Normal == this._status) {
  366. this._status = SRelationState.Edit;
  367. } else if (SRelationState.Edit == this._status) {
  368. this._status = SRelationState.Normal;
  369. }
  370. this.update();
  371. return true;
  372. } // Function onDoubleClick()
  373. /**
  374. * 键盘事件
  375. *
  376. * @param event 事件参数
  377. * @return boolean
  378. */
  379. onKeyDown(event: KeyboardEvent): boolean {
  380. console.log(event);
  381. if (this._status == SRelationState.Normal) {
  382. return false;
  383. } else if (this._status == SRelationState.Create) {
  384. if (event.code == "Enter") {
  385. this._status = SRelationState.Normal;
  386. }
  387. } else if (this._status == SRelationState.Edit) {
  388. if (event.key == "Alt") {
  389. this.isAlt = true;
  390. }
  391. } else {
  392. }
  393. this.update();
  394. return true;
  395. } // Function onKeyDown()
  396. /**
  397. * 键盘键抬起事件
  398. *
  399. * @param event 事件参数
  400. * @return boolean
  401. */
  402. onKeyUp(event: KeyboardEvent): boolean {
  403. if (this._status == SRelationState.Edit) {
  404. if (event.key == "Alt") {
  405. this.isAlt = false;
  406. }
  407. }
  408. this.update();
  409. return true;
  410. } // Function onKeyUp()
  411. /**
  412. * 鼠标按下事件
  413. *
  414. * @param event 事件参数
  415. * @return boolean
  416. */
  417. onMouseDown(event: SMouseEvent): boolean {
  418. console.log("aaaaaa");
  419. // 如果状态为编辑状态则添加点
  420. if (this._status == SRelationState.Create) {
  421. // 新增顶点
  422. this.insertPoint(event.x, event.y);
  423. } else if (this._status == SRelationState.Edit) {
  424. // 对多边形数组做编辑操作
  425. this.editPolygonPoint(event);
  426. }
  427. return true;
  428. } // Function onMouseDown()
  429. /**
  430. * 鼠标移入事件
  431. *
  432. * @param event 事件参数
  433. * @return boolean
  434. */
  435. onMouseEnter(event: SMouseEvent): boolean {
  436. return true;
  437. } // Function onMouseEnter()
  438. /**
  439. * 鼠标移出事件
  440. *
  441. * @param event 事件参数
  442. * @return boolean
  443. */
  444. onMouseLeave(event: SMouseEvent): boolean {
  445. return true;
  446. } // Function onMouseLeave()
  447. /**
  448. * 鼠标移动事件
  449. *
  450. * @param event 事件参数
  451. * @return boolean
  452. */
  453. onMouseMove(event: SMouseEvent): boolean {
  454. if (this._status == SRelationState.Create) {
  455. this.lastPoint.x = event.x;
  456. this.lastPoint.y = event.y;
  457. } else if (this._status == SRelationState.Edit) {
  458. if (-1 != this.curIndex) {
  459. this.pointList[this.curIndex].x = event.x;
  460. this.pointList[this.curIndex].y = event.y;
  461. }
  462. }
  463. this.update();
  464. return true;
  465. } // Function onMouseMove()
  466. /**
  467. * 鼠标抬起事件
  468. *
  469. * @param event 事件参数
  470. * @return boolean
  471. */
  472. onMouseUp(event: SMouseEvent): boolean {
  473. if (this._status == SRelationState.Edit) {
  474. this.curIndex = -1;
  475. }
  476. return true;
  477. } // Function onMouseUp()
  478. /**
  479. * 适配事件
  480. *
  481. * @param event 事件参数
  482. * @return boolean
  483. */
  484. onResize(event: SMouseEvent): boolean {
  485. return true;
  486. } // Function onResize()
  487. /**
  488. * Item对象边界区域
  489. *
  490. * @return SRect
  491. */
  492. boundingRect(): SRect {
  493. return new SRect(
  494. this.minX,
  495. this.minY,
  496. this.maxX - this.minX,
  497. this.maxY - this.minY
  498. );
  499. } // Function boundingRect()
  500. /**
  501. * Item绘制操作
  502. *
  503. * @param painter painter对象
  504. */
  505. onDraw(painter: SPainter): void {
  506. this.scenceLen = painter.toPx(this.len);
  507. // 当状态为展示状态
  508. if (this._status == SRelationState.Normal) {
  509. // 闭合多边形
  510. this.drawShowPolygon(painter, this.pointList);
  511. } else if (this._status == SRelationState.Create) {
  512. // 创建状态
  513. this.drawCreatePolygon(painter, this.pointList);
  514. } else {
  515. // 编辑状态
  516. this.drawEditPolygon(painter, this.pointList);
  517. }
  518. } // Function onDraw()
  519. }
  520. </script>