SEditPolygon.vue 14 KB

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