SEditPolygon.vue 18 KB

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