Draw.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800
  1. import { dataService } from "../Service/DataService.js";
  2. import { stateService } from "../Service/StateService.js";
  3. import { coordinate } from "../Coordinate.js";
  4. import Style from "@/graphic/CanvasStyle/index.js";
  5. import VectorType from "../enum/VectorType.js";
  6. import { mathUtil } from "../Util/MathUtil.js";
  7. import ElementEvents from "../enum/ElementEvents.js";
  8. import { elementService } from "../Service/ElementService.js";
  9. import UIEvents from "@/graphic/enum/UIEvents.js";
  10. import VectorCategory from "@/graphic/enum/VectorCategory.js";
  11. const imgCache = {};
  12. const help = {
  13. getVectorStyle(vector, geoType = vector.geoType) {
  14. const geoId = vector?.vectorId;
  15. if (!geoId) {
  16. return [Style[geoType], undefined];
  17. }
  18. const itemsEntry = [
  19. [stateService.getSelectItem(), "Select"],
  20. [stateService.getDraggingItem(), "Dragging"],
  21. [stateService.getFocusItem(), "Focus"],
  22. ];
  23. let currentAttr;
  24. return [
  25. itemsEntry.reduce((prev, [item, attr]) => {
  26. if (
  27. item &&
  28. // item.type === VectorType[geoType] &&
  29. geoId === item.vectorId
  30. ) {
  31. if (Style[attr]) {
  32. const style = Style[attr][geoType] || Style[attr][item.category]
  33. if (style) {
  34. currentAttr = attr;
  35. return style;
  36. }
  37. }
  38. }
  39. return prev;
  40. }, Style[geoType]),
  41. currentAttr,
  42. ];
  43. },
  44. setStyle(ctx, styles) {
  45. for (const style in styles) {
  46. if (typeof styles[style] === "function") {
  47. styles[style](ctx, vector);
  48. } else {
  49. ctx[style] = styles[style];
  50. }
  51. }
  52. },
  53. setVectorStyle(ctx, vector, geoType = vector.geoType) {
  54. let styles, attr
  55. if (Array.isArray(geoType)) {
  56. for (const type of geoType) {
  57. [styles, attr] = help.getVectorStyle(vector, type);
  58. if (styles) {
  59. break;
  60. }
  61. }
  62. } else {
  63. [styles, attr] = help.getVectorStyle(vector, geoType);
  64. }
  65. help.setStyle(ctx, styles)
  66. return [styles, attr];
  67. },
  68. transformCoves(lines) {
  69. return lines.map((line) =>
  70. line.map((line) => ({
  71. start: coordinate.getScreenXY(line.start),
  72. end: coordinate.getScreenXY(line.end),
  73. controls: line.controls.map(coordinate.getScreenXY.bind(coordinate)),
  74. }))
  75. );
  76. },
  77. drawCoves(ctx, coves) {
  78. for (const curve of coves) {
  79. ctx.beginPath();
  80. ctx.moveTo(curve.start.x, curve.start.y);
  81. if (curve.controls.length === 1) {
  82. ctx.quadraticCurveTo(
  83. curve.controls[0].x,
  84. curve.controls[0].y,
  85. curve.end.x,
  86. curve.end.y
  87. );
  88. } else {
  89. ctx.bezierCurveTo(
  90. curve.controls[0].x,
  91. curve.controls[0].y,
  92. curve.controls[1].x,
  93. curve.controls[1].y,
  94. curve.end.x,
  95. curve.end.y
  96. );
  97. }
  98. ctx.stroke();
  99. }
  100. },
  101. getReal(data) {
  102. return (data * coordinate.ratio * coordinate.zoom) / coordinate.defaultZoom;
  103. },
  104. getImage(src) {
  105. if (imgCache[src]) {
  106. return imgCache[src];
  107. }
  108. const img = new Image();
  109. img.src = src;
  110. return (imgCache[src] = new Promise((resolve) => {
  111. img.onload = () => {
  112. resolve(img);
  113. };
  114. }));
  115. },
  116. getTextCenter(ctx, txt) {
  117. const text = ctx.measureText(txt);
  118. const height = text.actualBoundingBoxAscent + text.actualBoundingBoxDescent
  119. return {
  120. width: text.width,
  121. height,
  122. x: text.width / 2,
  123. y: -height / 2
  124. }
  125. },
  126. // 绘制圆角矩形
  127. roundRect(ctx, x, y, width, height, radius) {
  128. ctx.beginPath();
  129. ctx.moveTo(x + radius, y);
  130. ctx.lineTo(x + width - radius, y);
  131. ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
  132. ctx.lineTo(x + width, y + height - radius);
  133. ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
  134. ctx.lineTo(x + radius, y + height);
  135. ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
  136. ctx.lineTo(x, y + radius);
  137. ctx.quadraticCurveTo(x, y, x + radius, y);
  138. ctx.closePath();
  139. },
  140. getRealDistance(p1, p2) {
  141. return Math.round(mathUtil.getDistance(p1, p2) * 100) / 100
  142. },
  143. getPerpendicularPoint(p1, p2, p3, d) {
  144. if (p1.x === p2.x) {
  145. return {x: p3.x + d, y: p3.y}
  146. } else if (p1.y === p2.y) {
  147. return {x: p3.x, y: p3.y + d}
  148. }
  149. // 计算通过 p1 和 p2 的直线的斜率和截距
  150. const slope = (p2.y - p1.y) / (p2.x - p1.x);
  151. const intercept = p1.y - slope * p1.x;
  152. // 计算垂直线的斜率和截距
  153. const perpendicularSlope = -1 / slope;
  154. const perpendicularIntercept = p3.y - perpendicularSlope * p3.x;
  155. // 计算垂足点 p0
  156. const x = (perpendicularIntercept - intercept) / (slope - perpendicularSlope);
  157. const y = slope * x + intercept;
  158. const p0 = { x, y };
  159. // 计算点 p4
  160. const distance = d; // 指定距离
  161. const dx = distance / Math.sqrt(1 + perpendicularSlope ** 2);
  162. const dy = perpendicularSlope * dx;
  163. return { x: p0.x + dx, y: p0.y + dy };
  164. },
  165. drawLineText(ctx, start, end, text, style) {
  166. if (start.x > end.x) {
  167. [start, end] = [end, start]
  168. }
  169. const angle = Math.atan2(end.y - start.y, end.x - start.x) * 180 / Math.PI;
  170. const center = mathUtil.lineCenter(start, end)
  171. ctx.save();
  172. ctx.translate(center.x, center.y);
  173. ctx.rotate(angle * Math.PI / 180);
  174. const textCenter = help.getTextCenter(ctx, text)
  175. const padding = style.padding;
  176. help.roundRect(
  177. ctx,
  178. -textCenter.x - padding,
  179. textCenter.y - padding,
  180. textCenter.width + 2 * padding,
  181. textCenter.height + 2 * padding,
  182. (textCenter.height / 2) + padding
  183. )
  184. ctx.fillStyle = style.backColor
  185. ctx.fill()
  186. ctx.fillStyle = style.fillColor
  187. ctx.fillText(text, -textCenter.x, -textCenter.y);
  188. ctx.restore();
  189. }
  190. };
  191. export default class Draw {
  192. constructor() {
  193. this.canvas = null;
  194. this.context = null;
  195. }
  196. initContext(canvas) {
  197. if (canvas) {
  198. this.canvas = canvas;
  199. this.context = canvas.getContext("2d");
  200. } else {
  201. this.context = null;
  202. this.canvas = null;
  203. }
  204. }
  205. clear() {
  206. this.context.clearRect(
  207. 0,
  208. 0,
  209. this.context.canvas.width,
  210. this.context.canvas.height
  211. );
  212. }
  213. drawBackGroundImg(vector) {
  214. if (!vector.display) {
  215. return;
  216. }
  217. const img = vector.imageData;
  218. const width = help.getReal(img.width);
  219. const height = help.getReal(img.height);
  220. const center = coordinate.getScreenXY(vector.center);
  221. this.context.save();
  222. this.context.drawImage(
  223. img,
  224. center.x - width / 2,
  225. center.y - height / 2,
  226. width,
  227. height
  228. );
  229. this.context.restore();
  230. }
  231. drawGrid(startX, startY, w, h, step1, step2) {
  232. this.context.save();
  233. this.context.beginPath();
  234. for (var x = startX; x <= w; x += step1) {
  235. this.context.moveTo(x, 0);
  236. this.context.lineTo(x, h);
  237. }
  238. for (var y = startY; y <= h; y += step1) {
  239. this.context.moveTo(0, y);
  240. this.context.lineTo(w, y);
  241. }
  242. this.context.strokeStyle = "rgba(0,0,0,0.1)";
  243. this.context.lineWidth = 0.5;
  244. this.context.stroke();
  245. this.context.beginPath();
  246. for (var x = startX; x <= w; x += step2) {
  247. this.context.moveTo(x, 0);
  248. this.context.lineTo(x, h);
  249. }
  250. for (var y = startY; y <= h; y += step2) {
  251. this.context.moveTo(0, y);
  252. this.context.lineTo(w, y);
  253. }
  254. this.context.strokeStyle = "rgba(0,0,0,0.2)";
  255. this.context.lineWidth = 1;
  256. this.context.stroke();
  257. this.context.restore();
  258. }
  259. drawRoad(vector, isTemp) {
  260. if (!isTemp && vector.display && vector.way !== "oneWay") {
  261. const ctx = this.context;
  262. const draw = (midDivide) => {
  263. const startScreen = coordinate.getScreenXY(midDivide.start);
  264. const endScreen = coordinate.getScreenXY(midDivide.end);
  265. ctx.beginPath();
  266. ctx.moveTo(startScreen.x, startScreen.y);
  267. ctx.lineTo(endScreen.x, endScreen.y);
  268. ctx.stroke();
  269. };
  270. ctx.save();
  271. help.setVectorStyle(ctx, vector);
  272. vector.midDivide.leftMidDivide && draw(vector.midDivide.leftMidDivide);
  273. vector.midDivide.rightMidDivide && draw(vector.midDivide.rightMidDivide);
  274. ctx.restore();
  275. }
  276. if (import.meta.env.DEV && !isTemp) {
  277. const startReal = isTemp
  278. ? vector.start
  279. : dataService.getRoadPoint(vector.startId);
  280. const endReal = isTemp
  281. ? vector.end
  282. : dataService.getRoadPoint(vector.endId);
  283. this.drawTextByInfo(
  284. { x: (startReal.x + endReal.x) / 2, y: (startReal.y + endReal.y) / 2 },
  285. vector.vectorId
  286. );
  287. }
  288. this.drawRoadEdge(vector, isTemp);
  289. vector.leftLanes && vector.leftLanes.forEach(this.drawLan.bind(this));
  290. vector.rightLanes && vector.rightLanes.forEach(this.drawLan.bind(this));
  291. }
  292. drawLan(lan) {
  293. const ctx = this.context;
  294. const start = coordinate.getScreenXY(lan.start);
  295. const end = coordinate.getScreenXY(lan.end);
  296. ctx.save();
  297. ctx.beginPath();
  298. help.setVectorStyle(ctx, null, "Lane");
  299. ctx.setLineDash(Style.Lane.dash);
  300. ctx.moveTo(start.x, start.y);
  301. ctx.lineTo(end.x, end.y);
  302. ctx.stroke();
  303. ctx.restore();
  304. if (import.meta.env.DEV) {
  305. this.drawPoint(lan.start);
  306. this.drawPoint(lan.end);
  307. }
  308. }
  309. drawRoadEdge(vector, isTemp) {
  310. //判断是否与road方向一致。角度足够小,路足够宽,有可能向量方向不一致
  311. const start = isTemp
  312. ? vector.start
  313. : dataService.getRoadPoint(vector.startId);
  314. const end = isTemp ? vector.end : dataService.getRoadPoint(vector.endId);
  315. const drawRoadEdgeChild = (edgeVector) => {
  316. const flag = mathUtil.isSameDirForVector(
  317. start,
  318. end,
  319. edgeVector.start,
  320. edgeVector.end
  321. );
  322. if (flag) {
  323. ctx.beginPath();
  324. const point1 = coordinate.getScreenXY(edgeVector.start);
  325. const point2 = coordinate.getScreenXY(edgeVector.end);
  326. ctx.moveTo(point1.x, point1.y);
  327. ctx.lineTo(point2.x, point2.y);
  328. ctx.stroke();
  329. }
  330. this.drawTextByInfo(
  331. {
  332. x: (edgeVector.start.x + edgeVector.end.x) / 2,
  333. y: (edgeVector.start.y + edgeVector.end.y) / 2,
  334. },
  335. edgeVector.vectorId
  336. );
  337. };
  338. const leftEdge = isTemp
  339. ? vector.leftEdge
  340. : dataService.getRoadEdge(vector.leftEdgeId);
  341. const rightEdge = isTemp
  342. ? vector.rightEdge
  343. : dataService.getRoadEdge(vector.rightEdgeId);
  344. const ctx = this.context;
  345. ctx.save();
  346. isTemp && (ctx.globalAlpha = 0.3);
  347. help.setVectorStyle(ctx, leftEdge);
  348. drawRoadEdgeChild(leftEdge);
  349. help.setVectorStyle(ctx, rightEdge);
  350. drawRoadEdgeChild(rightEdge);
  351. ctx.restore();
  352. if (import.meta.env.DEV) {
  353. this.drawPoint(leftEdge.start);
  354. this.drawPoint(leftEdge.end);
  355. this.drawPoint(rightEdge.start);
  356. this.drawPoint(rightEdge.end);
  357. }
  358. }
  359. drawCrossPoint(vector) {
  360. const start = coordinate.getScreenXY(
  361. dataService
  362. .getRoadEdge(vector.edgeInfo1.id)
  363. .getPosition(vector.edgeInfo1.dir)
  364. );
  365. const end = coordinate.getScreenXY(
  366. dataService
  367. .getRoadEdge(vector.edgeInfo2.id)
  368. .getPosition(vector.edgeInfo2.dir)
  369. );
  370. const pt2 = mathUtil.twoOrderBezier(
  371. 0.5,
  372. start,
  373. coordinate.getScreenXY({ x: vector.x, y: vector.y }),
  374. end
  375. );
  376. const pt = mathUtil.twoOrderBezier2(0.5, start, pt2, end);
  377. const extremePoint = coordinate.getScreenXY(vector.extremePoint);
  378. const ctx = this.context;
  379. ctx.save();
  380. ctx.strokeStyle = "red";
  381. ctx.beginPath();
  382. ctx.arc(
  383. // pt.x,
  384. // pt.y,
  385. extremePoint.x,
  386. extremePoint.y,
  387. Style.CrossPoint.radius * coordinate.ratio,
  388. 0,
  389. Math.PI * 2,
  390. true
  391. );
  392. ctx.stroke();
  393. ctx.fill();
  394. ctx.restore();
  395. ctx.save();
  396. ctx.beginPath();
  397. help.setVectorStyle(ctx, null, "RoadEdge");
  398. //曲线
  399. // ctx.moveTo(start.x, start.y);
  400. // ctx.quadraticCurveTo(pt.x, pt.y, end.x, end.y);
  401. const [coves] = help.transformCoves([vector.curves]);
  402. help.drawCoves(ctx, coves);
  403. ctx.restore();
  404. }
  405. drawCurveRoad(vector) {
  406. if (vector.display && vector.midDivide) {
  407. const covesArray = help.transformCoves([
  408. vector.midDivide.leftMidDivideCurves,
  409. vector.midDivide.rightMidDivideCurves,
  410. ]);
  411. const ctx = this.context;
  412. ctx.save();
  413. help.setVectorStyle(ctx, vector);
  414. for (let coves of covesArray) {
  415. help.drawCoves(ctx, coves);
  416. }
  417. ctx.restore();
  418. }
  419. this.drawCurveRoadEdge(dataService.getCurveRoadEdge(vector.rightEdgeId));
  420. this.drawCurveRoadEdge(dataService.getCurveRoadEdge(vector.leftEdgeId));
  421. vector.leftLanesCurves &&
  422. vector.leftLanesCurves.forEach(this.drawCurveLan.bind(this));
  423. vector.rightLanesCurves &&
  424. vector.rightLanesCurves.forEach(this.drawCurveLan.bind(this));
  425. if (import.meta.env.DEV) {
  426. vector.points.forEach(this.drawPoint.bind(this));
  427. }
  428. }
  429. drawCurveRoadEdge(vector, isTemp) {
  430. const [coves] = help.transformCoves([vector.curves]);
  431. const ctx = this.context;
  432. ctx.save();
  433. help.setVectorStyle(ctx, vector);
  434. help.drawCoves(ctx, coves);
  435. ctx.restore();
  436. if (import.meta.env.DEV) {
  437. vector.points.forEach(this.drawPoint.bind(this));
  438. }
  439. }
  440. drawCurveLan(lines) {
  441. const [coves] = help.transformCoves([lines]);
  442. const ctx = this.context;
  443. ctx.save();
  444. help.setVectorStyle(ctx, null, "CurveLan");
  445. ctx.setLineDash(Style.Lane.dash);
  446. help.drawCoves(ctx, coves);
  447. ctx.restore();
  448. if (import.meta.env.DEV) {
  449. lines.map((line) => {
  450. this.drawPoint(line.start);
  451. this.drawPoint(line.end);
  452. });
  453. }
  454. }
  455. drawRoadPoint(vector) {
  456. this.drawPoint(vector);
  457. }
  458. drawArrow(vector) {
  459. const startReal = dataService.getPoint(vector.startId);
  460. const start = coordinate.getScreenXY(startReal);
  461. const endReal = dataService.getPoint(vector.endId);
  462. const end = coordinate.getScreenXY(endReal);
  463. const ctx = this.context;
  464. ctx.save();
  465. const [style] = help.setVectorStyle(this.context, vector);
  466. if (vector.arrowColor) {
  467. ctx.strokeStyle = vector.arrowColor;
  468. }
  469. const dires =
  470. vector.category === UIEvents.MeasureLine
  471. ? [
  472. [start, end],
  473. [end, start],
  474. ]
  475. : [[start, end]];
  476. for (let [start, end] of dires) {
  477. const lines = mathUtil.getArrow(start, end);
  478. ctx.moveTo(lines[0].x, lines[0].y);
  479. ctx.lineTo(lines[1].x, lines[1].y);
  480. ctx.lineTo(lines[2].x, lines[2].y);
  481. }
  482. ctx.stroke();
  483. ctx.restore();
  484. }
  485. drawMagnifier(vector) {
  486. const ctx = this.context;
  487. this.drawPoint({
  488. ...vector,
  489. ...vector.position,
  490. radius: Style.Magnifier.radius,
  491. });
  492. const pt = coordinate.getScreenXY(vector.position);
  493. const target = coordinate.getScreenXY(vector.popPosition);
  494. const [style] = help.setVectorStyle(ctx, vector);
  495. const radius = help.getReal(vector.radius || style.radius);
  496. const offset = radius / 2;
  497. const targetPts =
  498. style === Style.Focus.Magnifier
  499. ? [mathUtil.translate(pt, target, pt, radius), target]
  500. : null;
  501. ctx.save();
  502. ctx.beginPath();
  503. ctx.moveTo(pt.x - offset, pt.y);
  504. ctx.lineTo(pt.x + offset, pt.y);
  505. ctx.stroke();
  506. ctx.beginPath();
  507. ctx.moveTo(pt.x, pt.y - offset);
  508. ctx.lineTo(pt.x, pt.y + offset);
  509. ctx.stroke();
  510. if (targetPts) {
  511. ctx.beginPath();
  512. ctx.moveTo(targetPts[0].x, targetPts[0].y);
  513. ctx.lineTo(targetPts[1].x, targetPts[1].y);
  514. ctx.stroke();
  515. let img, imgBound;
  516. if (vector.photoImage) {
  517. img = vector.photoImage;
  518. imgBound = [0, 0, img.width, img.height];
  519. } else {
  520. const size = help.getReal(style.target.realRadius);
  521. const backImg = dataService.getBackgroundImg();
  522. img = backImg.imageData;
  523. const imgCenter = coordinate.getScreenXY(backImg.center);
  524. const start = {
  525. x: imgCenter.x - help.getReal(img.width) / 2,
  526. y: imgCenter.y - help.getReal(img.height) / 2,
  527. };
  528. const ro = img.width / help.getReal(img.width);
  529. imgBound = [
  530. (pt.x - start.x - size) * ro,
  531. (pt.y - start.y - size) * ro,
  532. size * 2 * ro,
  533. size * 2 * ro,
  534. ];
  535. }
  536. const size = help.getReal(style.target.radius);
  537. ctx.beginPath();
  538. ctx.arc(target.x, target.y, size, 0, 2 * Math.PI);
  539. ctx.clip();
  540. ctx.drawImage(
  541. img,
  542. ...imgBound,
  543. target.x - size,
  544. target.y - size,
  545. size * 2,
  546. size * 2
  547. );
  548. ctx.strokeStyle = style.target.strokeStyle;
  549. ctx.lineWidth = style.target.lineWidth;
  550. ctx.stroke();
  551. }
  552. ctx.restore();
  553. }
  554. drawCircle(element) {
  555. this.drawPoint({
  556. ...element,
  557. geoType: "Circle",
  558. ...element.center,
  559. });
  560. element.points.forEach((point) => this.drawPoint(point));
  561. }
  562. drawPoint(vector) {
  563. const pt = coordinate.getScreenXY({ x: vector.x, y: vector.y });
  564. const ctx = this.context;
  565. const [style] = help.setVectorStyle(
  566. ctx,
  567. vector,
  568. [vector.category, vector.geoType, "Point"]
  569. );
  570. if (vector.color) {
  571. ctx.strokeStyle = vector.color;
  572. }
  573. const draw = (style) => {
  574. const radius = help.getReal(vector.radius || style.radius);
  575. ctx.save();
  576. ctx.beginPath();
  577. ctx.arc(pt.x, pt.y, radius, 0, Math.PI * 2, true);
  578. help.setStyle(ctx, style)
  579. ctx.stroke();
  580. ctx.fill();
  581. ctx.restore();
  582. }
  583. draw(style)
  584. if (style.out) {
  585. draw(style.out)
  586. }
  587. if (import.meta.env.DEV) {
  588. if (vector.vectorId) {
  589. this.drawTextByInfo(vector, vector.vectorId);
  590. }
  591. }
  592. }
  593. drawTextByInfo(position, txt, angle, setStyle = true) {
  594. const ctx = this.context;
  595. ctx.save();
  596. setStyle && help.setVectorStyle(ctx, null, "Text");
  597. const pt = coordinate.getScreenXY(position);
  598. const textCenter = help.getTextCenter(ctx, txt);
  599. pt.x -= textCenter.x;
  600. pt.y -= textCenter.y;
  601. if (angle) {
  602. ctx.translate(pt.x, pt.y);
  603. ctx.rotate(angle);
  604. ctx.fillText(txt, 0, 0);
  605. } else {
  606. ctx.fillText(txt, pt.x, pt.y);
  607. }
  608. ctx.restore();
  609. }
  610. // 文字
  611. drawText(vector) {
  612. help.setVectorStyle(this.context, vector);
  613. this.context.fillStyle = vector.color;
  614. const oldFont = this.context.font;
  615. this.context.font = `${vector.fontSize}px Microsoft YaHei`;
  616. this.drawTextByInfo(vector.center, vector.value, 0, false);
  617. const ctx = this.context;
  618. const pt = coordinate.getScreenXY(vector.center);
  619. const text = ctx.measureText(vector.value);
  620. pt.x -= text.width / 2;
  621. pt.y += (text.actualBoundingBoxAscent + text.actualBoundingBoxDescent) / 2;
  622. this.context.font = oldFont;
  623. }
  624. drawLineText(vector, style) {
  625. const startReal = dataService.getPoint(vector.startId);
  626. const endReal = dataService.getPoint(vector.endId);
  627. help.drawLineText(
  628. this.context,
  629. coordinate.getScreenXY(startReal),
  630. coordinate.getScreenXY(endReal),
  631. help.getRealDistance(startReal, endReal) + "m",
  632. style
  633. )
  634. }
  635. drawBaseLineLabel(vector) {
  636. const startReal = dataService.getPoint(vector.startId);
  637. const start = coordinate.getScreenXY(startReal);
  638. const endReal = dataService.getPoint(vector.endId);
  639. const end = coordinate.getScreenXY(endReal);
  640. const point = mathUtil.translate(
  641. end, start, end, mathUtil.getDistance(start, end) / 3
  642. )
  643. const p4 = help.getPerpendicularPoint(start, end, point, 30)
  644. const ctx = this.context
  645. ctx.beginPath();
  646. const [style] = help.setVectorStyle(
  647. this.context,
  648. vector,
  649. vector.category || vector.geoType
  650. );
  651. ctx.moveTo(point.x, point.y);
  652. ctx.lineTo(p4.x, p4.y);
  653. ctx.stroke();
  654. const p5 = help.getPerpendicularPoint(start, end, point, 35)
  655. help.drawLineText(
  656. this.context,
  657. help.getPerpendicularPoint(point, p5, p5, 10),
  658. help.getPerpendicularPoint(point, p5, p5, -10),
  659. "基准线",
  660. {
  661. padding: 6,
  662. backColor: "rgba(0,0,0,0)",
  663. fillColor: style.strokeStyle
  664. }
  665. )
  666. }
  667. drawLine(vector) {
  668. const startReal = dataService.getPoint(vector.startId);
  669. const start = coordinate.getScreenXY(startReal);
  670. const endReal = dataService.getPoint(vector.endId);
  671. const end = coordinate.getScreenXY(endReal);
  672. this.context.save();
  673. const [style, attr] = help.setVectorStyle(
  674. this.context,
  675. vector,
  676. [vector.category, vector.geoType, 'BaseLine']
  677. );
  678. if (style.dash) {
  679. this.context.setLineDash(style.dash);
  680. }
  681. this.context.beginPath();
  682. this.context.moveTo(start.x, start.y);
  683. this.context.lineTo(end.x, end.y);
  684. this.context.stroke();
  685. this.context.restore();
  686. const drawPoints = () => {
  687. // if (attr) {
  688. this.drawPoint(dataService.getPoint(vector.startId))
  689. this.drawPoint(dataService.getPoint(vector.endId))
  690. // }
  691. }
  692. switch (vector.category) {
  693. case VectorCategory.Line.ArrowLine:
  694. this.drawArrow(vector);
  695. drawPoints()
  696. break
  697. case VectorCategory.Line.BaseLine:
  698. this.drawBaseLineLabel(vector)
  699. drawPoints()
  700. break;
  701. case VectorCategory.Line.MeasureLine:
  702. this.drawLineText(vector, style.text)
  703. drawPoints()
  704. break;
  705. }
  706. }
  707. drawElementLine(element) {
  708. let start = elementService.getPoint(element.startId);
  709. start = coordinate.getScreenXY(start);
  710. let end = elementService.getPoint(element.endId);
  711. end = coordinate.getScreenXY(end);
  712. this.context.save();
  713. const [style] = help.setVectorStyle(
  714. this.context,
  715. element,
  716. element.category || element.geoType
  717. );
  718. if (style.dash) {
  719. this.context.setLineDash(style.dash);
  720. }
  721. this.context.beginPath();
  722. this.context.moveTo(start.x, start.y);
  723. this.context.lineTo(end.x, end.y);
  724. this.context.stroke();
  725. this.context.restore();
  726. }
  727. }
  728. const draw = new Draw();
  729. export { draw };