@meta2d/core 1.0.56 → 1.0.58

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (299) hide show
  1. package/{index.ts → index.d.ts} +9 -9
  2. package/index.js +10 -0
  3. package/index.js.map +1 -0
  4. package/package.json +38 -39
  5. package/src/canvas/canvas.d.ts +456 -0
  6. package/src/canvas/canvas.js +7584 -0
  7. package/src/canvas/canvas.js.map +1 -0
  8. package/src/canvas/canvasImage.d.ts +27 -0
  9. package/src/canvas/canvasImage.js +441 -0
  10. package/src/canvas/canvasImage.js.map +1 -0
  11. package/src/canvas/canvasTemplate.d.ts +18 -0
  12. package/src/canvas/canvasTemplate.js +209 -0
  13. package/src/canvas/canvasTemplate.js.map +1 -0
  14. package/src/canvas/{index.ts → index.d.ts} +2 -2
  15. package/src/canvas/index.js +3 -0
  16. package/src/canvas/index.js.map +1 -0
  17. package/src/canvas/magnifierCanvas.d.ts +19 -0
  18. package/src/canvas/magnifierCanvas.js +102 -0
  19. package/src/canvas/magnifierCanvas.js.map +1 -0
  20. package/src/canvas/offscreen.d.ts +1 -0
  21. package/src/canvas/offscreen.js +14 -0
  22. package/src/canvas/offscreen.js.map +1 -0
  23. package/src/core.d.ts +479 -0
  24. package/src/core.js +4609 -0
  25. package/src/core.js.map +1 -0
  26. package/src/data.d.ts +34 -0
  27. package/src/data.js +85 -0
  28. package/src/data.js.map +1 -0
  29. package/src/diagrams/arrow.d.ts +4 -0
  30. package/src/diagrams/arrow.js +47 -0
  31. package/src/diagrams/arrow.js.map +1 -0
  32. package/src/diagrams/circle.d.ts +2 -0
  33. package/src/diagrams/circle.js +9 -0
  34. package/src/diagrams/circle.js.map +1 -0
  35. package/src/diagrams/cloud.d.ts +2 -0
  36. package/src/diagrams/cloud.js +12 -0
  37. package/src/diagrams/cloud.js.map +1 -0
  38. package/src/diagrams/cube.d.ts +2 -0
  39. package/src/diagrams/cube.js +68 -0
  40. package/src/diagrams/cube.js.map +1 -0
  41. package/src/diagrams/diamond.d.ts +2 -0
  42. package/src/diagrams/diamond.js +13 -0
  43. package/src/diagrams/diamond.js.map +1 -0
  44. package/src/diagrams/file.d.ts +2 -0
  45. package/src/diagrams/file.js +18 -0
  46. package/src/diagrams/file.js.map +1 -0
  47. package/src/diagrams/gif.d.ts +5 -0
  48. package/src/diagrams/gif.js +89 -0
  49. package/src/diagrams/gif.js.map +1 -0
  50. package/src/diagrams/hexagon.d.ts +2 -0
  51. package/src/diagrams/{hexagon.ts → hexagon.js} +55 -60
  52. package/src/diagrams/hexagon.js.map +1 -0
  53. package/src/diagrams/iframe.d.ts +2 -0
  54. package/src/diagrams/iframe.js +338 -0
  55. package/src/diagrams/iframe.js.map +1 -0
  56. package/src/diagrams/index.d.ts +71 -0
  57. package/src/diagrams/{index.ts → index.js} +77 -79
  58. package/src/diagrams/index.js.map +1 -0
  59. package/src/diagrams/line/arrow.d.ts +2 -0
  60. package/src/diagrams/line/arrow.js +128 -0
  61. package/src/diagrams/line/arrow.js.map +1 -0
  62. package/src/diagrams/line/curve.d.ts +16 -0
  63. package/src/diagrams/line/curve.js +227 -0
  64. package/src/diagrams/line/curve.js.map +1 -0
  65. package/src/diagrams/line/{index.ts → index.d.ts} +5 -5
  66. package/src/diagrams/line/index.js +6 -0
  67. package/src/diagrams/line/index.js.map +1 -0
  68. package/src/diagrams/line/line.d.ts +42 -0
  69. package/src/diagrams/line/line.js +375 -0
  70. package/src/diagrams/line/line.js.map +1 -0
  71. package/src/diagrams/line/polyline.d.ts +10 -0
  72. package/src/diagrams/line/polyline.js +627 -0
  73. package/src/diagrams/line/polyline.js.map +1 -0
  74. package/src/diagrams/line/smooth.d.ts +3 -0
  75. package/src/diagrams/line/smooth.js +136 -0
  76. package/src/diagrams/line/smooth.js.map +1 -0
  77. package/src/diagrams/message.d.ts +2 -0
  78. package/src/diagrams/message.js +15 -0
  79. package/src/diagrams/message.js.map +1 -0
  80. package/src/diagrams/mindLine.d.ts +3 -0
  81. package/src/diagrams/mindLine.js +30 -0
  82. package/src/diagrams/mindLine.js.map +1 -0
  83. package/src/diagrams/mindNode.d.ts +3 -0
  84. package/src/diagrams/mindNode.js +161 -0
  85. package/src/diagrams/mindNode.js.map +1 -0
  86. package/src/diagrams/panel.d.ts +2 -0
  87. package/src/diagrams/panel.js +131 -0
  88. package/src/diagrams/panel.js.map +1 -0
  89. package/src/diagrams/pentagon.d.ts +3 -0
  90. package/src/diagrams/pentagon.js +45 -0
  91. package/src/diagrams/pentagon.js.map +1 -0
  92. package/src/diagrams/pentagram.d.ts +3 -0
  93. package/src/diagrams/pentagram.js +51 -0
  94. package/src/diagrams/pentagram.js.map +1 -0
  95. package/src/diagrams/people.d.ts +2 -0
  96. package/src/diagrams/people.js +19 -0
  97. package/src/diagrams/people.js.map +1 -0
  98. package/src/diagrams/rectangle.d.ts +3 -0
  99. package/src/diagrams/rectangle.js +27 -0
  100. package/src/diagrams/rectangle.js.map +1 -0
  101. package/src/diagrams/svg/parse.d.ts +15 -0
  102. package/src/diagrams/svg/parse.js +279 -0
  103. package/src/diagrams/svg/parse.js.map +1 -0
  104. package/src/diagrams/svgPath.d.ts +2 -0
  105. package/src/diagrams/svgPath.js +29 -0
  106. package/src/diagrams/svgPath.js.map +1 -0
  107. package/src/diagrams/triangle.d.ts +3 -0
  108. package/src/diagrams/triangle.js +40 -0
  109. package/src/diagrams/triangle.js.map +1 -0
  110. package/src/diagrams/video.d.ts +5 -0
  111. package/src/diagrams/video.js +184 -0
  112. package/src/diagrams/video.js.map +1 -0
  113. package/src/dialog/dialog.d.ts +21 -0
  114. package/src/dialog/dialog.js +157 -0
  115. package/src/dialog/dialog.js.map +1 -0
  116. package/src/dialog/{index.ts → index.d.ts} +1 -1
  117. package/src/dialog/index.js +2 -0
  118. package/src/dialog/index.js.map +1 -0
  119. package/src/event/event.d.ts +102 -0
  120. package/src/event/event.js +22 -0
  121. package/src/event/event.js.map +1 -0
  122. package/src/event/{index.ts → index.d.ts} +1 -1
  123. package/src/event/index.js +2 -0
  124. package/src/event/index.js.map +1 -0
  125. package/src/map/{index.ts → index.d.ts} +1 -1
  126. package/src/map/index.js +2 -0
  127. package/src/map/index.js.map +1 -0
  128. package/src/map/map.d.ts +21 -0
  129. package/src/map/map.js +212 -0
  130. package/src/map/map.js.map +1 -0
  131. package/src/options.d.ts +130 -0
  132. package/src/options.js +80 -0
  133. package/src/options.js.map +1 -0
  134. package/src/pen/arrow.d.ts +4 -0
  135. package/src/pen/arrow.js +188 -0
  136. package/src/pen/arrow.js.map +1 -0
  137. package/src/pen/{index.ts → index.d.ts} +6 -6
  138. package/src/pen/index.js +7 -0
  139. package/src/pen/index.js.map +1 -0
  140. package/src/pen/math.d.ts +28 -0
  141. package/src/pen/math.js +213 -0
  142. package/src/pen/math.js.map +1 -0
  143. package/src/pen/model.d.ts +514 -0
  144. package/src/pen/model.js +210 -0
  145. package/src/pen/model.js.map +1 -0
  146. package/src/pen/plugin.d.ts +5 -0
  147. package/src/pen/plugin.js +58 -0
  148. package/src/pen/plugin.js.map +1 -0
  149. package/src/pen/render.d.ts +146 -0
  150. package/src/pen/render.js +3234 -0
  151. package/src/pen/render.js.map +1 -0
  152. package/src/pen/text.d.ts +8 -0
  153. package/src/pen/text.js +314 -0
  154. package/src/pen/text.js.map +1 -0
  155. package/src/pen/utils.d.ts +2 -0
  156. package/src/pen/utils.js +19 -0
  157. package/src/pen/utils.js.map +1 -0
  158. package/src/point/{index.ts → index.d.ts} +1 -1
  159. package/src/point/index.js +2 -0
  160. package/src/point/index.js.map +1 -0
  161. package/src/point/point.d.ts +65 -0
  162. package/src/point/point.js +178 -0
  163. package/src/point/point.js.map +1 -0
  164. package/src/rect/{index.ts → index.d.ts} +1 -1
  165. package/src/rect/index.js +2 -0
  166. package/src/rect/index.js.map +1 -0
  167. package/src/rect/rect.d.ts +52 -0
  168. package/src/rect/rect.js +427 -0
  169. package/src/rect/rect.js.map +1 -0
  170. package/src/rect/triangle.d.ts +2 -0
  171. package/src/rect/triangle.js +10 -0
  172. package/src/rect/triangle.js.map +1 -0
  173. package/src/scroll/{index.ts → index.d.ts} +1 -1
  174. package/src/scroll/index.js +2 -0
  175. package/src/scroll/index.js.map +1 -0
  176. package/src/scroll/scroll.d.ts +35 -0
  177. package/src/scroll/scroll.js +234 -0
  178. package/src/scroll/scroll.js.map +1 -0
  179. package/src/store/global.d.ts +25 -0
  180. package/src/store/global.js +18 -0
  181. package/src/store/global.js.map +1 -0
  182. package/src/store/{index.ts → index.d.ts} +2 -2
  183. package/src/store/index.js +3 -0
  184. package/src/store/index.js.map +1 -0
  185. package/src/store/store.d.ts +228 -0
  186. package/src/store/store.js +87 -0
  187. package/src/store/store.js.map +1 -0
  188. package/src/theme.d.ts +13 -0
  189. package/src/theme.js +23 -0
  190. package/src/theme.js.map +1 -0
  191. package/src/title/{index.ts → index.d.ts} +1 -1
  192. package/src/title/index.js +2 -0
  193. package/src/title/index.js.map +1 -0
  194. package/src/title/title.d.ts +30 -0
  195. package/src/title/title.js +99 -0
  196. package/src/title/title.js.map +1 -0
  197. package/src/tooltip/{index.ts → index.d.ts} +1 -1
  198. package/src/tooltip/index.js +2 -0
  199. package/src/tooltip/index.js.map +1 -0
  200. package/src/tooltip/tooltip.d.ts +40 -0
  201. package/src/tooltip/tooltip.js +172 -0
  202. package/src/tooltip/tooltip.js.map +1 -0
  203. package/src/utils/clone.d.ts +8 -0
  204. package/src/utils/clone.js +84 -0
  205. package/src/utils/clone.js.map +1 -0
  206. package/src/utils/color.d.ts +3 -0
  207. package/src/utils/color.js +110 -0
  208. package/src/utils/color.js.map +1 -0
  209. package/src/utils/error.d.ts +2 -0
  210. package/src/utils/error.js +6 -0
  211. package/src/utils/error.js.map +1 -0
  212. package/src/utils/file.d.ts +3 -0
  213. package/src/utils/file.js +40 -0
  214. package/src/utils/file.js.map +1 -0
  215. package/src/utils/{index.ts → index.d.ts} +9 -9
  216. package/src/utils/index.js +10 -0
  217. package/src/utils/index.js.map +1 -0
  218. package/src/utils/math.d.ts +18 -0
  219. package/src/utils/math.js +114 -0
  220. package/src/utils/math.js.map +1 -0
  221. package/src/utils/object.d.ts +2 -0
  222. package/src/utils/object.js +21 -0
  223. package/src/utils/object.js.map +1 -0
  224. package/src/utils/padding.d.ts +7 -0
  225. package/src/utils/padding.js +47 -0
  226. package/src/utils/padding.js.map +1 -0
  227. package/src/utils/time.d.ts +1 -0
  228. package/src/utils/time.js +17 -0
  229. package/src/utils/time.js.map +1 -0
  230. package/src/utils/url.d.ts +4 -0
  231. package/src/utils/url.js +27 -0
  232. package/src/utils/url.js.map +1 -0
  233. package/src/utils/uuid.d.ts +4 -0
  234. package/src/utils/uuid.js +13 -0
  235. package/src/utils/uuid.js.map +1 -0
  236. package/README.md +0 -13
  237. package/package.build.json +0 -39
  238. package/src/canvas/canvas.ts +0 -8639
  239. package/src/canvas/canvasImage.ts +0 -525
  240. package/src/canvas/canvasTemplate.ts +0 -257
  241. package/src/canvas/magnifierCanvas.ts +0 -142
  242. package/src/canvas/offscreen.ts +0 -14
  243. package/src/core.ts +0 -5128
  244. package/src/data.ts +0 -86
  245. package/src/diagrams/arrow.ts +0 -50
  246. package/src/diagrams/circle.ts +0 -19
  247. package/src/diagrams/cloud.ts +0 -34
  248. package/src/diagrams/cube.ts +0 -94
  249. package/src/diagrams/diamond.ts +0 -14
  250. package/src/diagrams/file.ts +0 -19
  251. package/src/diagrams/gif.ts +0 -105
  252. package/src/diagrams/iframe.ts +0 -365
  253. package/src/diagrams/line/arrow.ts +0 -175
  254. package/src/diagrams/line/curve.ts +0 -260
  255. package/src/diagrams/line/line.ts +0 -409
  256. package/src/diagrams/line/polyline.ts +0 -676
  257. package/src/diagrams/line/smooth.ts +0 -133
  258. package/src/diagrams/message.ts +0 -17
  259. package/src/diagrams/mindLine.ts +0 -31
  260. package/src/diagrams/mindNode.ts +0 -177
  261. package/src/diagrams/panel.ts +0 -149
  262. package/src/diagrams/pentagon.ts +0 -48
  263. package/src/diagrams/pentagram.ts +0 -63
  264. package/src/diagrams/people.ts +0 -23
  265. package/src/diagrams/rectangle.ts +0 -29
  266. package/src/diagrams/svg/parse.ts +0 -319
  267. package/src/diagrams/svgPath.ts +0 -53
  268. package/src/diagrams/triangle.ts +0 -43
  269. package/src/diagrams/video.ts +0 -202
  270. package/src/dialog/dialog.ts +0 -177
  271. package/src/event/event.ts +0 -142
  272. package/src/map/map.ts +0 -239
  273. package/src/options.ts +0 -205
  274. package/src/pen/arrow.ts +0 -259
  275. package/src/pen/math.ts +0 -253
  276. package/src/pen/model.ts +0 -785
  277. package/src/pen/plugin.ts +0 -57
  278. package/src/pen/render.ts +0 -3725
  279. package/src/pen/text.ts +0 -341
  280. package/src/pen/utils.ts +0 -21
  281. package/src/point/point.ts +0 -232
  282. package/src/rect/rect.ts +0 -507
  283. package/src/rect/triangle.ts +0 -16
  284. package/src/scroll/scroll.ts +0 -277
  285. package/src/store/global.ts +0 -38
  286. package/src/store/store.ts +0 -293
  287. package/src/theme.ts +0 -35
  288. package/src/title/title.ts +0 -115
  289. package/src/tooltip/tooltip.ts +0 -199
  290. package/src/utils/clone.ts +0 -79
  291. package/src/utils/color.ts +0 -126
  292. package/src/utils/error.ts +0 -7
  293. package/src/utils/file.ts +0 -46
  294. package/src/utils/math.ts +0 -120
  295. package/src/utils/object.ts +0 -23
  296. package/src/utils/padding.ts +0 -48
  297. package/src/utils/time.ts +0 -25
  298. package/src/utils/url.ts +0 -30
  299. package/src/utils/uuid.ts +0 -15
package/src/core.js ADDED
@@ -0,0 +1,4609 @@
1
+ import { commonAnchors, commonPens, cube } from './diagrams';
2
+ import { Canvas } from './canvas';
3
+ import { calcInView, calcTextDrawRect, calcTextLines, calcTextRect, facePen, formatAttrs, getAllChildren, getFromAnchor, getParent, getToAnchor, getWords, LockState, PenType, renderPenRaw, setElemPosition, connectLine, nearestAnchor, setChildValue, isAncestor, isShowChild, CanvasLayer, validationPlugin, setLifeCycleFunc, getAllFollowers, isInteraction, calcWorldAnchors } from './pen';
4
+ import { rotatePoint } from './point';
5
+ import { clearStore, EditType, globalStore, register, registerAnchors, registerCanvasDraw, useStore, } from './store';
6
+ import { formatPadding, loadCss, s8, valueInArray, valueInRange, } from './utils';
7
+ import { calcCenter, calcRelativeRect, getRect, rectInRect, } from './rect';
8
+ import { deepClone } from './utils/clone';
9
+ import { EventAction } from './event';
10
+ import { ViewMap } from './map';
11
+ import * as mqtt from 'mqtt/dist/mqtt.min.js';
12
+ import pkg from '../package.json';
13
+ import { lockedError } from './utils/error';
14
+ import { Scroll } from './scroll';
15
+ import { getter } from './utils/object';
16
+ import { queryURLParams } from './utils/url';
17
+ import { HotkeyType } from './data';
18
+ export class Meta2d {
19
+ store;
20
+ canvas;
21
+ websocket;
22
+ mqttClient;
23
+ websockets;
24
+ mqttClients;
25
+ penPluginMap = new Map();
26
+ socketFn;
27
+ events = {};
28
+ map;
29
+ mapTimer;
30
+ constructor(parent, opts = {}) {
31
+ this.store = useStore(s8());
32
+ this.setOptions(opts);
33
+ this.setDatabyOptions(opts);
34
+ this.init(parent);
35
+ this.register(commonPens());
36
+ this.registerCanvasDraw({ cube });
37
+ this.registerAnchors(commonAnchors());
38
+ globalThis.meta2d = this;
39
+ this.initEventFns();
40
+ this.store.emitter.on('*', this.onEvent);
41
+ }
42
+ facePen = facePen;
43
+ getWords = getWords;
44
+ calcTextLines = calcTextLines;
45
+ calcTextRect = calcTextRect;
46
+ calcTextDrawRect = calcTextDrawRect;
47
+ /**
48
+ * @deprecated 改用 beforeAddPens
49
+ */
50
+ get beforeAddPen() {
51
+ return this.canvas.beforeAddPen;
52
+ }
53
+ /**
54
+ * @deprecated 改用 beforeAddPens
55
+ */
56
+ set beforeAddPen(fn) {
57
+ this.canvas.beforeAddPen = fn;
58
+ }
59
+ get beforeAddPens() {
60
+ return this.canvas.beforeAddPens;
61
+ }
62
+ set beforeAddPens(fn) {
63
+ this.canvas.beforeAddPens = fn;
64
+ }
65
+ get beforeAddAnchor() {
66
+ return this.canvas.beforeAddAnchor;
67
+ }
68
+ set beforeAddAnchor(fn) {
69
+ this.canvas.beforeAddAnchor = fn;
70
+ }
71
+ get beforeRemovePens() {
72
+ return this.canvas.beforeRemovePens;
73
+ }
74
+ set beforeRemovePens(fn) {
75
+ this.canvas.beforeRemovePens = fn;
76
+ }
77
+ get beforeRemoveAnchor() {
78
+ return this.canvas.beforeRemoveAnchor;
79
+ }
80
+ set beforeRemoveAnchor(fn) {
81
+ this.canvas.beforeRemoveAnchor = fn;
82
+ }
83
+ setOptions(opts = {}) {
84
+ if (opts.grid !== undefined ||
85
+ opts.gridColor !== undefined ||
86
+ opts.gridSize !== undefined) {
87
+ // this.setGrid({
88
+ // grid: opts.grid,
89
+ // gridColor: opts.gridColor,
90
+ // gridSize: opts.gridSize,
91
+ // });
92
+ this.canvas && (this.canvas.canvasTemplate.bgPatchFlags = true);
93
+ }
94
+ if (opts.rule !== undefined || opts.ruleColor !== undefined || opts.ruleOptions !== undefined) {
95
+ // this.setRule({
96
+ // rule: opts.rule,
97
+ // ruleColor: opts.ruleColor,
98
+ // });
99
+ this.store.patchFlagsTop = true;
100
+ if (opts.ruleOptions) {
101
+ if (this.store.options?.ruleOptions) {
102
+ Object.assign(this.store.options.ruleOptions, opts.ruleOptions);
103
+ opts.ruleOptions = this.store.options.ruleOptions;
104
+ }
105
+ }
106
+ }
107
+ if (opts.background !== undefined) {
108
+ this.canvas && (this.canvas.canvasTemplate.bgPatchFlags = true);
109
+ }
110
+ if (opts.resizeMode !== undefined) {
111
+ if (!opts.resizeMode) {
112
+ this.canvas.hotkeyType = HotkeyType.None;
113
+ }
114
+ }
115
+ if (opts.width !== undefined || opts.height !== undefined) {
116
+ this.canvas && (this.canvas.canvasTemplate.bgPatchFlags = true);
117
+ if (this.canvas && this.canvas.canvasTemplate.canvas.style.backgroundImage) {
118
+ this.canvas.canvasTemplate.canvas.style.backgroundImage = '';
119
+ }
120
+ }
121
+ this.store.options = Object.assign(this.store.options, opts);
122
+ if (this.canvas && opts.scroll !== undefined) {
123
+ if (opts.scroll) {
124
+ !this.canvas.scroll && (this.canvas.scroll = new Scroll(this.canvas));
125
+ this.canvas.scroll.show();
126
+ }
127
+ else {
128
+ this.canvas.scroll && this.canvas.scroll.hide();
129
+ }
130
+ }
131
+ }
132
+ getOptions() {
133
+ return this.store.options;
134
+ }
135
+ setTheme(theme) {
136
+ this.store.data.theme = theme;
137
+ this.setBackgroundColor(this.store.theme[theme].background);
138
+ this.canvas.parentElement.style.background = this.store.theme[theme].parentBackground;
139
+ this.store.data.color = this.store.theme[theme].color;
140
+ this.setOptions({
141
+ ruleColor: this.store.theme[theme].ruleColor,
142
+ ruleOptions: this.store.theme[theme].ruleOptions
143
+ });
144
+ this.render();
145
+ }
146
+ setDatabyOptions(options = {}) {
147
+ const { color, activeColor, activeBackground, grid, gridColor, gridSize, fromArrow, toArrow, rule, ruleColor, textColor, x = 0, y = 0, } = options;
148
+ this.setRule({ rule, ruleColor });
149
+ this.setGrid({
150
+ grid,
151
+ gridColor,
152
+ gridSize,
153
+ });
154
+ this.store.data = Object.assign(this.store.data, {
155
+ textColor,
156
+ color,
157
+ activeColor,
158
+ activeBackground,
159
+ fromArrow,
160
+ toArrow,
161
+ x,
162
+ y
163
+ });
164
+ }
165
+ init(parent) {
166
+ if (typeof parent === 'string') {
167
+ this.canvas = new Canvas(this, document.getElementById(parent), this.store);
168
+ }
169
+ else {
170
+ this.canvas = new Canvas(this, parent, this.store);
171
+ }
172
+ this.resize();
173
+ this.canvas.listen();
174
+ }
175
+ initEventFns() {
176
+ this.events[EventAction.Link] = (pen, e) => {
177
+ if (window && e.value && typeof e.value === 'string') {
178
+ window.open(e.value, e.params ?? '_blank');
179
+ return;
180
+ }
181
+ console.warn('[meta2d] Link param is not a string');
182
+ };
183
+ this.events[EventAction.SetProps] = (pen, e) => {
184
+ // TODO: 若频繁地触发,重复 render 可能带来性能问题,待考虑
185
+ const value = e.value;
186
+ if (value && typeof value === 'object') {
187
+ const pens = e.params ? this.find(e.params) : this.find(pen.id);
188
+ pens.forEach((pen) => {
189
+ if (value.hasOwnProperty('visible')) {
190
+ if (pen.visible !== value.visible) {
191
+ this.setVisible(pen, value.visible);
192
+ }
193
+ }
194
+ this.setValue({ id: pen.id, ...value }, { render: false, doEvent: false });
195
+ });
196
+ this.render();
197
+ return;
198
+ }
199
+ console.warn('[meta2d] SetProps value is not an object');
200
+ };
201
+ this.events[EventAction.StartAnimate] = (pen, e) => {
202
+ let _pen = pen;
203
+ if (e.value) {
204
+ _pen = this.findOne(e.value);
205
+ }
206
+ if (this.store.animates.has(_pen) && !_pen.calculative.pause) {
207
+ return;
208
+ }
209
+ if (e.targetType && e.params) {
210
+ this.startAnimate(e.value || [pen], e.params);
211
+ return;
212
+ }
213
+ if (!e.value || typeof e.value === 'string') {
214
+ this.startAnimate(e.value || [pen]);
215
+ return;
216
+ }
217
+ console.warn('[meta2d] StartAnimate value is not a string');
218
+ };
219
+ this.events[EventAction.PauseAnimate] = (pen, e) => {
220
+ if (!e.value || typeof e.value === 'string') {
221
+ this.pauseAnimate(e.value || [pen]);
222
+ return;
223
+ }
224
+ console.warn('[meta2d] PauseAnimate value is not a string');
225
+ };
226
+ this.events[EventAction.StopAnimate] = (pen, e) => {
227
+ if (!e.value || typeof e.value === 'string') {
228
+ if (e.value) {
229
+ let _pen = this.findOne(e.value);
230
+ if (!this.store.animates.has(_pen)) {
231
+ return;
232
+ }
233
+ }
234
+ else {
235
+ if (!this.store.animates.has(pen)) {
236
+ return;
237
+ }
238
+ }
239
+ this.stopAnimate(e.value || [pen]);
240
+ return;
241
+ }
242
+ console.warn('[meta2d] StopAnimate event value is not a string');
243
+ };
244
+ this.events[EventAction.StartVideo] = (pen, e) => {
245
+ if (!e.value || typeof e.value === 'string') {
246
+ this.startVideo(e.value || [pen]);
247
+ return;
248
+ }
249
+ console.warn('[meta2d] StartVideo value is not a string');
250
+ };
251
+ this.events[EventAction.PauseVideo] = (pen, e) => {
252
+ if (!e.value || typeof e.value === 'string') {
253
+ this.pauseVideo(e.value || [pen]);
254
+ return;
255
+ }
256
+ console.warn('[meta2d] PauseVideo value is not a string');
257
+ };
258
+ this.events[EventAction.StopVideo] = (pen, e) => {
259
+ if (!e.value || typeof e.value === 'string') {
260
+ this.stopVideo(e.value || [pen]);
261
+ return;
262
+ }
263
+ console.warn('[meta2d] StopVideo event value is not a string');
264
+ };
265
+ this.events[EventAction.JS] = (pen, e) => {
266
+ if (e.value && !e.fn) {
267
+ try {
268
+ if (typeof e.value !== 'string') {
269
+ throw new Error('[meta2d] Function value must be string');
270
+ }
271
+ const fnJs = e.value;
272
+ e.fn = new Function('pen', 'params', 'context', fnJs);
273
+ }
274
+ catch (err) {
275
+ console.error('[meta2d]: Error on make a function:', err);
276
+ }
277
+ }
278
+ e.fn?.(pen, e.params, { meta2d: this, eventName: e.name });
279
+ };
280
+ this.events[EventAction.GlobalFn] = (pen, e) => {
281
+ if (typeof e.value !== 'string') {
282
+ console.warn('[meta2d] GlobalFn value must be a string');
283
+ return;
284
+ }
285
+ if (globalThis[e.value]) {
286
+ globalThis[e.value](pen, e.params);
287
+ }
288
+ };
289
+ this.events[EventAction.Emit] = (pen, e) => {
290
+ if (typeof e.value !== 'string') {
291
+ console.warn('[meta2d] Emit value must be a string');
292
+ return;
293
+ }
294
+ this.store.emitter.emit(e.value, {
295
+ pen,
296
+ params: e.params,
297
+ eventName: e.name,
298
+ });
299
+ };
300
+ this.events[EventAction.SendPropData] = (pen, e) => {
301
+ const value = deepClone(e.value);
302
+ if (value && typeof value === 'object') {
303
+ const _pen = e.params ? this.findOne(e.params) : pen;
304
+ for (let key in value) {
305
+ if (value[key] === undefined || value[key] === '') {
306
+ value[key] = _pen[key];
307
+ }
308
+ }
309
+ value.id = _pen.id;
310
+ this.doSendDataEvent(value, e.extend);
311
+ return;
312
+ }
313
+ console.warn('[meta2d] SendPropData value is not an object');
314
+ };
315
+ this.events[EventAction.SendVarData] = (pen, e) => {
316
+ const value = deepClone(e.value);
317
+ if (value && typeof value === 'object') {
318
+ const _pen = e.params ? this.findOne(e.params) : pen;
319
+ let array = [];
320
+ for (let key in value) {
321
+ let obj = {
322
+ dataId: key,
323
+ value: value[key],
324
+ };
325
+ if (!obj.value) {
326
+ let oneForm = _pen.form.find((_item) => _item.dataIds &&
327
+ _item.dataIds.dataId === obj.dataId);
328
+ if (oneForm) {
329
+ obj.value = _pen[oneForm.key];
330
+ }
331
+ }
332
+ array.push(obj);
333
+ }
334
+ this.doSendDataEvent(array, e.extend);
335
+ return;
336
+ }
337
+ console.warn('[meta2d] SendVarData value is not an object');
338
+ };
339
+ this.events[EventAction.Navigator] = (pen, e) => {
340
+ if (e.value && typeof e.value === 'string') {
341
+ this.navigatorTo(e.value);
342
+ }
343
+ };
344
+ this.events[EventAction.Dialog] = (pen, e) => {
345
+ if (e.params &&
346
+ typeof e.params === 'string') {
347
+ let url = e.params;
348
+ if (e.params.includes('${')) {
349
+ let keys = e.params.match(/(?<=\$\{).*?(?=\})/g);
350
+ if (keys) {
351
+ keys?.forEach((key) => {
352
+ url = url.replace(`\${${key}}`, pen[key]);
353
+ });
354
+ }
355
+ }
356
+ this.canvas.dialog.show(e.value, url, e.extend);
357
+ }
358
+ };
359
+ this.events[EventAction.SendData] = (pen, e) => {
360
+ const value = deepClone(e.value);
361
+ if (value && typeof value === 'object') {
362
+ if (e.targetType === 'id') {
363
+ const _pen = e.params ? this.findOne(e.params) : pen;
364
+ for (let key in value) {
365
+ if (value[key] === undefined || value[key] === '') {
366
+ value[key] = _pen[key];
367
+ }
368
+ }
369
+ // value.id = _pen.id;
370
+ if (_pen.deviceId) {
371
+ value.deviceId = _pen.deviceId;
372
+ }
373
+ this.sendDataToNetWork(value, pen, e);
374
+ return;
375
+ }
376
+ }
377
+ };
378
+ this.events[EventAction.PostMessage] = (pen, e) => {
379
+ if (typeof e.value !== 'string') {
380
+ console.warn('[meta2d] Emit value must be a string');
381
+ return;
382
+ }
383
+ const _pen = e.params ? this.findOne(e.params) : pen;
384
+ if (_pen.name !== 'iframe' || !_pen.iframe) {
385
+ console.warn('不是嵌入页面');
386
+ return;
387
+ }
388
+ let params = queryURLParams(_pen.iframe.split('?')[1]);
389
+ _pen.calculative.singleton.div.children[0].contentWindow.postMessage(JSON.stringify({
390
+ name: e.value,
391
+ id: params.id,
392
+ }), '*');
393
+ return;
394
+ };
395
+ this.events[EventAction.PostMessageToParent] = (pen, e) => {
396
+ if (typeof e.value !== 'string') {
397
+ console.warn('[meta2d] Emit value must be a string');
398
+ return;
399
+ }
400
+ window.parent.postMessage(JSON.stringify(e.value), '*');
401
+ return;
402
+ };
403
+ }
404
+ navigatorTo(id) {
405
+ if (!id) {
406
+ return;
407
+ }
408
+ let href = window.location.href;
409
+ let arr = href.split('id=');
410
+ if (arr.length > 1) {
411
+ let idx = arr[1].indexOf('&');
412
+ if (idx === -1) {
413
+ window.location.href = arr[0] + 'id=' + id;
414
+ }
415
+ else {
416
+ window.location.href = arr[0] + 'id=' + id + arr[1].slice(idx);
417
+ }
418
+ }
419
+ }
420
+ doSendDataEvent(value, topics) {
421
+ let data = JSON.stringify(value);
422
+ if (this.mqttClient && this.mqttClient.connected) {
423
+ if (topics) {
424
+ topics.split(',').forEach((topic) => {
425
+ this.mqttClient.publish(topic, data);
426
+ });
427
+ }
428
+ else {
429
+ this.store.data.mqttTopics &&
430
+ this.store.data.mqttTopics.split(',').forEach((topic) => {
431
+ this.mqttClient.publish(topic, data);
432
+ });
433
+ }
434
+ }
435
+ if (this.websocket && this.websocket.readyState === 1) {
436
+ this.websocket.send(data);
437
+ }
438
+ if (this.store.data.https || this.store.data.http) {
439
+ this.sendDatabyHttp(data);
440
+ }
441
+ this.store.emitter.emit('sendData', data);
442
+ }
443
+ async sendDataToNetWork(value, pen, e) {
444
+ const network = deepClone(e.network);
445
+ if (network.data) {
446
+ Object.assign(network, network.data);
447
+ delete network.data;
448
+ }
449
+ if (!network.url) {
450
+ return;
451
+ }
452
+ if (network.protocol === 'http') {
453
+ if (typeof network.headers === 'object') {
454
+ for (let i in network.headers) {
455
+ if (typeof network.headers[i] === 'string') {
456
+ let keys = network.headers[i].match(/(?<=\$\{).*?(?=\})/g);
457
+ if (keys) {
458
+ network.headers[i] = network.headers[i].replace(`\${${keys[0]}}`, this.getDynamicParam(keys[0]));
459
+ }
460
+ }
461
+ }
462
+ }
463
+ let params = undefined;
464
+ let url = network.url;
465
+ if (network.method === 'GET') {
466
+ params =
467
+ '?' +
468
+ Object.keys(value)
469
+ .map((key) => key + '=' + value[key])
470
+ .join('&');
471
+ }
472
+ if (network.method === 'POST') {
473
+ if (url.indexOf('${') > -1) {
474
+ let keys = url.match(/(?<=\$\{).*?(?=\})/g);
475
+ if (keys) {
476
+ keys.forEach((key) => {
477
+ url = url.replace(`\${${key}}`, getter(pen, key) || this.getDynamicParam(key));
478
+ });
479
+ }
480
+ }
481
+ }
482
+ const res = await fetch(url + (params ? params : ''), {
483
+ headers: network.headers || {},
484
+ method: network.method,
485
+ body: network.method === 'POST' ? JSON.stringify(value) : undefined,
486
+ });
487
+ if (res.ok) {
488
+ if (e.callback) {
489
+ const data = await res.text();
490
+ if (!e.fn) {
491
+ try {
492
+ if (typeof e.callback !== 'string') {
493
+ throw new Error('[meta2d] Function callback must be string');
494
+ }
495
+ const fnJs = e.callback;
496
+ e.fn = new Function('pen', 'data', 'context', fnJs);
497
+ }
498
+ catch (err) {
499
+ console.error('[meta2d]: Error on make a function:', err);
500
+ }
501
+ }
502
+ e.fn?.(pen, data, { meta2d: this, e });
503
+ }
504
+ console.info('http消息发送成功');
505
+ }
506
+ }
507
+ else if (network.protocol === 'mqtt') {
508
+ const clients = this.mqttClients.filter((client) => client.options.href === network.url);
509
+ if (clients && clients.length) {
510
+ if (clients[0].connected) {
511
+ network.topics.split(',').forEach((topic) => {
512
+ clients[0].publish(topic, value);
513
+ });
514
+ }
515
+ }
516
+ else {
517
+ //临时建立连接
518
+ let mqttClient = mqtt.connect(network.url, network.options);
519
+ mqttClient.on('connect', () => {
520
+ console.info('mqtt连接成功');
521
+ network.topics.split(',').forEach((topic) => {
522
+ mqttClient.publish(topic, value);
523
+ mqttClient?.end();
524
+ });
525
+ });
526
+ }
527
+ }
528
+ else if (network.protocol === 'websocket') {
529
+ const websockets = this.websockets.filter((socket) => socket.url === network.url);
530
+ if (websockets && websockets.length) {
531
+ if (websockets[0].readyState === 1) {
532
+ websockets[0].send(value);
533
+ }
534
+ }
535
+ else {
536
+ //临时建立连接
537
+ let websocket = new WebSocket(network.url, network.protocols || undefined);
538
+ websocket.onopen = function () {
539
+ console.info('websocket连接成功');
540
+ websocket.send(value);
541
+ setTimeout(() => {
542
+ websocket.close();
543
+ }, 100);
544
+ };
545
+ }
546
+ }
547
+ }
548
+ resize(width, height) {
549
+ this.canvas.resize(width, height);
550
+ this.render();
551
+ this.store.emitter.emit('resize', { width, height });
552
+ if (this.canvas.scroll && this.canvas.scroll.isShow) {
553
+ this.canvas.scroll.init();
554
+ }
555
+ }
556
+ /**
557
+ *
558
+ * @param emit 是否发送消息
559
+ */
560
+ async addPen(pen, history, emit = true) {
561
+ return await this.canvas.addPen(pen, history, emit);
562
+ }
563
+ async addPens(pens, history) {
564
+ return await this.canvas.addPens(pens, history);
565
+ }
566
+ render(patchFlags) {
567
+ this.canvas?.render(patchFlags);
568
+ }
569
+ async setBackgroundImage(url, data) {
570
+ let that = this;
571
+ async function loadImage(url) {
572
+ return new Promise((resolve) => {
573
+ const img = new Image();
574
+ img.src = url;
575
+ if (that.store.options.cdn &&
576
+ !(url.startsWith('http') ||
577
+ url.startsWith('//') ||
578
+ url.startsWith('data:image'))) {
579
+ img.src = that.store.options.cdn + url;
580
+ }
581
+ img.crossOrigin = 'anonymous';
582
+ img.onload = () => {
583
+ resolve(img);
584
+ };
585
+ });
586
+ }
587
+ this.store.data.bkImage = url;
588
+ const width = data?.width || this.store.data?.width || this.store.options?.width;
589
+ const height = data?.height || this.store.data?.height || this.store.options?.height;
590
+ if (width && height) {
591
+ this.canvas.canvasTemplate.canvas.style.backgroundImage = null;
592
+ this.canvas && (this.canvas.canvasTemplate.bgPatchFlags = true);
593
+ }
594
+ else {
595
+ this.canvas.canvasTemplate.canvas.style.backgroundImage = url
596
+ ? `url('${url}')`
597
+ : '';
598
+ }
599
+ if (url) {
600
+ const img = await loadImage(url);
601
+ // 用作 toPng 的绘制
602
+ this.store.bkImg = img;
603
+ if (width && height) {
604
+ if (this.canvas) {
605
+ this.canvas.canvasTemplate.init();
606
+ this.render();
607
+ }
608
+ }
609
+ }
610
+ else {
611
+ this.store.bkImg = null;
612
+ }
613
+ }
614
+ setBackgroundColor(color = this.store.data.background) {
615
+ this.store.data.background = color;
616
+ // this.store.patchFlagsBackground = true;
617
+ this.canvas && (this.canvas.canvasTemplate.bgPatchFlags = true);
618
+ }
619
+ setGrid({ grid = this.store.data.grid, gridColor = this.store.data.gridColor, gridSize = this.store.data.gridSize, gridRotate = this.store.data.gridRotate, } = {}) {
620
+ this.store.data.grid = grid;
621
+ this.store.data.gridColor = gridColor;
622
+ this.store.data.gridSize = gridSize < 0 ? 0 : gridSize;
623
+ this.store.data.gridRotate = gridRotate;
624
+ // this.store.patchFlagsBackground = true;
625
+ this.canvas && (this.canvas.canvasTemplate.bgPatchFlags = true);
626
+ }
627
+ setRule({ rule = this.store.data.rule, ruleColor = this.store.data.ruleColor, } = {}) {
628
+ this.store.data.rule = rule;
629
+ this.store.data.ruleColor = ruleColor;
630
+ this.store.patchFlagsTop = true;
631
+ }
632
+ open(data, render = true) {
633
+ this.clear(false, data?.template);
634
+ this.canvas.autoPolylineFlag = true;
635
+ if (data) {
636
+ this.setBackgroundImage(data.bkImage, data);
637
+ Object.assign(this.store.data, data);
638
+ this.store.data.pens = [];
639
+ // 第一遍赋初值
640
+ for (const pen of data.pens) {
641
+ if (!pen.id) {
642
+ pen.id = s8();
643
+ }
644
+ !pen.calculative && (pen.calculative = { canvas: this.canvas });
645
+ this.store.pens[pen.id] = pen;
646
+ }
647
+ for (const pen of data.pens) {
648
+ this.canvas.makePen(pen);
649
+ }
650
+ //首次计算连线bug
651
+ // for (const pen of data.pens) {
652
+ // this.canvas.updateLines(pen);
653
+ // }
654
+ }
655
+ this.canvas.patchFlagsLines.forEach((pen) => {
656
+ if (pen.type) {
657
+ this.canvas.initLineRect(pen);
658
+ }
659
+ });
660
+ if (!this.store.data.template) {
661
+ this.store.data.template = s8();
662
+ }
663
+ if (!render) {
664
+ this.canvas.opening = true;
665
+ }
666
+ this.initBindDatas();
667
+ this.initBinds();
668
+ this.initMessageEvents();
669
+ this.initGlobalTriggers();
670
+ this.render();
671
+ this.listenSocket();
672
+ this.connectSocket();
673
+ this.connectNetwork();
674
+ this.startDataMock();
675
+ this.startAnimate();
676
+ this.startVideo();
677
+ this.doInitJS();
678
+ this.doInitFn();
679
+ if (this.store.data.iconUrls) {
680
+ for (const item of this.store.data.iconUrls) {
681
+ loadCss(item, () => {
682
+ this.render();
683
+ });
684
+ }
685
+ }
686
+ this.canvas.autoPolylineFlag = false;
687
+ this.store.emitter.emit('opened');
688
+ if (this.canvas.scroll && this.canvas.scroll.isShow) {
689
+ this.canvas.scroll.init();
690
+ }
691
+ }
692
+ cacheData(id) {
693
+ if (id && this.store.options.cacheLength) {
694
+ let index = this.store.cacheDatas.findIndex((item) => item.data && item.data._id === id);
695
+ if (index === -1) {
696
+ this.store.cacheDatas.push({
697
+ data: deepClone(this.store.data, true),
698
+ // offscreen: new Array(2),
699
+ // flag: new Array(2)
700
+ });
701
+ if (this.store.cacheDatas.length > this.store.options.cacheLength) {
702
+ this.store.cacheDatas.shift();
703
+ }
704
+ }
705
+ else {
706
+ let cacheDatas = this.store.cacheDatas.splice(index, 1)[0];
707
+ this.store.cacheDatas.push(cacheDatas);
708
+ }
709
+ }
710
+ }
711
+ loadCacheData(id) {
712
+ let index = this.store.cacheDatas.findIndex((item) => item.data && item.data._id === id);
713
+ if (index === -1) {
714
+ return;
715
+ }
716
+ // const ctx = this.canvas.canvas.getContext('2d');
717
+ // ctx.clearRect(0, 0, this.canvas.canvas.width, this.canvas.canvas.height);
718
+ // for (let offs of this.store.cacheDatas[index].offscreen) {
719
+ // if (offs) {
720
+ // ctx.drawImage(offs, 0, 0, this.canvas.width, this.canvas.height);
721
+ // }
722
+ // }
723
+ // ctx.clearRect(0, 0, this.canvas.canvas.width, this.canvas.canvas.height);
724
+ this.store.data = this.store.cacheDatas[index].data;
725
+ this.setBackgroundImage(this.store.data.bkImage);
726
+ this.store.pens = {};
727
+ this.store.data.pens.forEach((pen) => {
728
+ pen.calculative.canvas = this.canvas;
729
+ this.store.pens[pen.id] = pen;
730
+ globalStore.path2dDraws[pen.name] &&
731
+ this.store.path2dMap.set(pen, globalStore.path2dDraws[pen.name](pen));
732
+ pen.type &&
733
+ this.store.path2dMap.set(pen, globalStore.path2dDraws[pen.name](pen));
734
+ if (pen.image) {
735
+ pen.calculative.imageDrawed = false;
736
+ this.canvas.loadImage(pen);
737
+ }
738
+ });
739
+ this.render();
740
+ }
741
+ initBindDatas() {
742
+ this.store.bindDatas = {};
743
+ this.store.data.pens.forEach((pen) => {
744
+ pen.form?.forEach((formItem) => {
745
+ let dataIds;
746
+ if (formItem.dataIds) {
747
+ if (Array.isArray(formItem.dataIds)) {
748
+ dataIds = formItem.dataIds;
749
+ }
750
+ else {
751
+ dataIds = [formItem.dataIds];
752
+ }
753
+ }
754
+ dataIds?.forEach((item) => {
755
+ if (!this.store.bindDatas[item.dataId]) {
756
+ this.store.bindDatas[item.dataId] = [];
757
+ }
758
+ this.store.bindDatas[item.dataId].push({
759
+ id: pen.id,
760
+ formItem,
761
+ });
762
+ });
763
+ });
764
+ });
765
+ }
766
+ initBinds() {
767
+ this.store.bind = {};
768
+ this.store.data.pens.forEach((pen) => {
769
+ pen.realTimes?.forEach((realTime) => {
770
+ if (realTime.bind && realTime.bind.id) {
771
+ if (!this.store.bind[realTime.bind.id]) {
772
+ this.store.bind[realTime.bind.id] = [];
773
+ }
774
+ this.store.bind[realTime.bind.id].push({
775
+ id: pen.id,
776
+ key: realTime.key,
777
+ });
778
+ }
779
+ });
780
+ });
781
+ }
782
+ connectSocket() {
783
+ this.connectWebsocket();
784
+ this.connectMqtt();
785
+ this.connectHttp();
786
+ }
787
+ /**
788
+ * open 后执行初始化 Js ,每个图纸可配置一个初始化 js
789
+ */
790
+ doInitJS() {
791
+ const initJs = this.store.data.initJs;
792
+ if (initJs && initJs.trim()) {
793
+ try {
794
+ const fn = new Function('context', initJs);
795
+ fn({ meta2d: this });
796
+ }
797
+ catch (e) {
798
+ console.warn('initJs error', e);
799
+ }
800
+ }
801
+ }
802
+ doInitFn() {
803
+ let params = queryURLParams();
804
+ let binds = [];
805
+ for (let key in params) {
806
+ if (params.hasOwnProperty(key)) {
807
+ if (key.startsWith('bind-')) {
808
+ binds.push({
809
+ id: key.replace('bind-', ''),
810
+ dataId: key.replace('bind-', ''),
811
+ value: params[key]
812
+ });
813
+ }
814
+ }
815
+ }
816
+ if (binds.length) {
817
+ this.setDatas(binds, { history: false });
818
+ }
819
+ }
820
+ drawLine(lineName) {
821
+ lineName && lockedError(this.store);
822
+ this.canvas.drawingLineName = lineName;
823
+ }
824
+ alignPenToGrid(pen) {
825
+ this.canvas.alignPenToGrid(pen);
826
+ }
827
+ drawingPencil() {
828
+ this.canvas.drawingPencil();
829
+ }
830
+ stopPencil() {
831
+ this.canvas.stopPencil();
832
+ }
833
+ lock(lock) {
834
+ this.store.data.locked = lock;
835
+ this.finishDrawLine(true);
836
+ this.canvas.drawingLineName = '';
837
+ this.stopPencil();
838
+ //恢复可选状态
839
+ this.store.data.pens.forEach((pen) => {
840
+ if (pen.externElement === true) {
841
+ // pen.onMove && pen.onMove(pen);
842
+ pen.calculative.singleton?.div && setElemPosition(pen, pen.calculative.singleton.div);
843
+ }
844
+ });
845
+ if (lock > 0) {
846
+ this.initMessageEvents();
847
+ }
848
+ }
849
+ // end - 当前鼠标位置,是否作为终点
850
+ async finishDrawLine(end) {
851
+ await this.canvas.finishDrawline(end);
852
+ }
853
+ async finishPencil() {
854
+ await this.canvas.finishPencil();
855
+ }
856
+ updateLineType(pen, lineName) {
857
+ if (!pen || pen.name != 'line' || !lineName || !this.canvas[lineName]) {
858
+ return;
859
+ }
860
+ pen.lineName = lineName;
861
+ const from = getFromAnchor(pen);
862
+ const to = getToAnchor(pen);
863
+ from.prev = undefined;
864
+ from.next = undefined;
865
+ to.prev = undefined;
866
+ to.next = undefined;
867
+ pen.calculative.worldAnchors = [from, to];
868
+ pen.calculative.activeAnchor = from;
869
+ this.canvas[lineName](this.store, pen, to);
870
+ if (pen.lineName === 'curve') {
871
+ from.prev = {
872
+ penId: from.penId,
873
+ x: from.x - 50,
874
+ y: from.y,
875
+ };
876
+ from.next = {
877
+ penId: from.penId,
878
+ x: from.x + 50,
879
+ y: from.y,
880
+ };
881
+ to.prev = {
882
+ penId: to.penId,
883
+ x: to.x - 50,
884
+ y: to.y,
885
+ };
886
+ to.next = {
887
+ penId: to.penId,
888
+ x: to.x + 50,
889
+ y: to.y,
890
+ };
891
+ }
892
+ pen.calculative.activeAnchor = undefined;
893
+ this.canvas.initLineRect(pen);
894
+ this.render();
895
+ }
896
+ addDrawLineFn(fnName, fn) {
897
+ this.canvas[fnName] = fn;
898
+ this.canvas.drawLineFns.push(fnName);
899
+ }
900
+ removeDrawLineFn(fnName) {
901
+ const index = this.canvas.drawLineFns.indexOf(fnName);
902
+ if (index > -1) {
903
+ this.canvas.drawLineFns.splice(index, 1);
904
+ }
905
+ }
906
+ showMagnifier() {
907
+ this.canvas.showMagnifier();
908
+ }
909
+ hideMagnifier() {
910
+ this.canvas.hideMagnifier();
911
+ }
912
+ toggleMagnifier() {
913
+ this.canvas.toggleMagnifier();
914
+ }
915
+ /**
916
+ * 擦除画布,释放 store 上的 pens
917
+ * @param render 是否重绘
918
+ */
919
+ clear(render = true, template) {
920
+ for (const pen of this.store.data.pens) {
921
+ pen.onDestroy?.(pen);
922
+ }
923
+ clearStore(this.store, template);
924
+ this.hideInput();
925
+ this.canvas.tooltip.hide();
926
+ if (this.map && this.map.isShow) {
927
+ this.map.show();
928
+ this.map.setView();
929
+ }
930
+ this.canvas.clearCanvas();
931
+ sessionStorage.removeItem('page');
932
+ this.store.clipboard = undefined;
933
+ if (!this.store.sameTemplate) {
934
+ this.canvas.canvasTemplate.bgPatchFlags = true;
935
+ }
936
+ this.store.patchFlagsBackground = true;
937
+ this.store.patchFlagsTop = true;
938
+ this.setBackgroundImage(undefined);
939
+ render && this.render();
940
+ }
941
+ emit(type, event) {
942
+ this.store.emitter.emit(type, event);
943
+ }
944
+ on(type, handler) {
945
+ this.store.emitter.on(type, handler);
946
+ return this;
947
+ }
948
+ off(type, handler) {
949
+ this.store.emitter.off(type, handler);
950
+ return this;
951
+ }
952
+ register = register;
953
+ registerCanvasDraw = registerCanvasDraw;
954
+ registerAnchors = registerAnchors;
955
+ // customeDock = (store, rect, pens, offset) => {xDock, yDock}
956
+ // customDock return:
957
+ // {
958
+ // xDock: {x, y, step, prev, penId},
959
+ // yDock: {x, y, step, prev, penId},
960
+ // }
961
+ // xDock,yDock - 水平或垂直方向的参考线
962
+ // prev - 参考线的起点
963
+ // x,y - 参考线的终点
964
+ // step - 自动吸附需要的偏移量
965
+ // penId - 参考线的笔
966
+ registerMoveDock(dock) {
967
+ this.canvas.customMoveDock = dock;
968
+ }
969
+ /**
970
+ * 参数同方法 registerMoveDock ,最后一个参数由 offset 偏移修改成了当前 resize 的点
971
+ */
972
+ registerResizeDock(dock) {
973
+ this.canvas.customResizeDock = dock;
974
+ }
975
+ find(idOrTag) {
976
+ return this.canvas.find(idOrTag);
977
+ }
978
+ findOne(idOrTag) {
979
+ return this.canvas.findOne(idOrTag);
980
+ }
981
+ getPenRect(pen) {
982
+ return this.canvas.getPenRect(pen);
983
+ }
984
+ setPenRect(pen, rect, render = true) {
985
+ this.canvas.setPenRect(pen, rect, render);
986
+ }
987
+ startAnimate(idOrTagOrPens, params) {
988
+ this.stopAnimate(idOrTagOrPens);
989
+ let pens;
990
+ // 没有参数 则播放有自动播放属性的动画
991
+ if (!idOrTagOrPens) {
992
+ pens = this.store.data.pens.filter((pen) => {
993
+ return (((pen.type || pen.frames) && pen.autoPlay) ||
994
+ (pen.animations &&
995
+ pen.animations.length &&
996
+ pen.animations.findIndex((i) => i.autoPlay) !== -1));
997
+ });
998
+ }
999
+ else if (typeof idOrTagOrPens === 'string') {
1000
+ pens = this.find(idOrTagOrPens);
1001
+ }
1002
+ else {
1003
+ pens = idOrTagOrPens;
1004
+ }
1005
+ if (!pens.length) {
1006
+ return;
1007
+ }
1008
+ pens.forEach((pen) => {
1009
+ if (pen.calculative.pause) {
1010
+ const d = Date.now() - pen.calculative.pause;
1011
+ pen.calculative.pause = undefined;
1012
+ pen.calculative.frameStart += d;
1013
+ pen.calculative.frameEnd += d;
1014
+ }
1015
+ else {
1016
+ let index = -1;
1017
+ if (params !== undefined && pen.animations) {
1018
+ if (typeof params === 'string') {
1019
+ index = pen.animations.findIndex((animation) => animation.name === params);
1020
+ if (index === -1) {
1021
+ return;
1022
+ }
1023
+ }
1024
+ else if (typeof params === 'number') {
1025
+ if (pen.animations.length > params) {
1026
+ index = params;
1027
+ }
1028
+ else {
1029
+ return;
1030
+ }
1031
+ }
1032
+ }
1033
+ else if (params === undefined) {
1034
+ index = pen.animations?.findIndex((i) => i.autoPlay);
1035
+ if (index === -1 && pen.animations?.length) {
1036
+ //默认执行第0个动画
1037
+ index = 0;
1038
+ }
1039
+ }
1040
+ if (index !== -1 && index !== undefined) {
1041
+ const animate = deepClone(pen.animations[index]);
1042
+ delete animate.name;
1043
+ animate.currentAnimation = index;
1044
+ if (!pen.type && animate.frames) {
1045
+ animate.showDuration = this.calcAnimateDuration(animate);
1046
+ }
1047
+ //animations成立
1048
+ this.setValue({
1049
+ id: pen.id,
1050
+ ...animate,
1051
+ }, {
1052
+ doEvent: false,
1053
+ history: false,
1054
+ });
1055
+ }
1056
+ this.store.animates.add(pen);
1057
+ if (!pen.type) {
1058
+ this.store.animateMap.set(pen, pen.calculative.canvas.getFrameProps(pen));
1059
+ }
1060
+ }
1061
+ });
1062
+ // this.canvas.canvasImage.init();
1063
+ // this.canvas.canvasImageBottom.init();
1064
+ this.initImageCanvas(pens);
1065
+ this.canvas.animate();
1066
+ }
1067
+ pauseAnimate(idOrTagOrPens) {
1068
+ let pens = [];
1069
+ if (!idOrTagOrPens) {
1070
+ this.store.animates.forEach((pen) => {
1071
+ pens.push(pen);
1072
+ });
1073
+ }
1074
+ else if (typeof idOrTagOrPens === 'string') {
1075
+ pens = this.find(idOrTagOrPens);
1076
+ }
1077
+ else {
1078
+ pens = idOrTagOrPens;
1079
+ }
1080
+ pens.forEach((pen) => {
1081
+ if (!pen.calculative.pause) {
1082
+ pen.calculative.pause = Date.now();
1083
+ }
1084
+ });
1085
+ }
1086
+ stopAnimate(idOrTagOrPens) {
1087
+ let pens = [];
1088
+ if (!idOrTagOrPens) {
1089
+ this.store.animates.forEach((pen) => {
1090
+ pens.push(pen);
1091
+ });
1092
+ }
1093
+ else if (typeof idOrTagOrPens === 'string') {
1094
+ pens = this.find(idOrTagOrPens);
1095
+ }
1096
+ else {
1097
+ pens = idOrTagOrPens;
1098
+ }
1099
+ pens.forEach((pen) => {
1100
+ pen.currentAnimation = undefined;
1101
+ pen.calculative.pause = undefined;
1102
+ pen.calculative.start = undefined;
1103
+ pen.calculative.duration = undefined;
1104
+ pen.calculative.animatePos = 0;
1105
+ this.store.animates.delete(pen);
1106
+ this.canvas.restoreNodeAnimate(pen);
1107
+ this.canvas.updateLines(pen);
1108
+ this.store.animateMap.delete(pen);
1109
+ });
1110
+ this.initImageCanvas(pens);
1111
+ setTimeout(() => {
1112
+ this.canvas?.calcActiveRect();
1113
+ this.render();
1114
+ }, 20);
1115
+ }
1116
+ startVideo(idOrTagOrPens) {
1117
+ let pens;
1118
+ if (!idOrTagOrPens) {
1119
+ pens = this.store.data.pens.filter((pen) => {
1120
+ return (pen.video || pen.audio) && pen.autoPlay;
1121
+ });
1122
+ }
1123
+ else if (typeof idOrTagOrPens === 'string') {
1124
+ pens = this.find(idOrTagOrPens);
1125
+ }
1126
+ else {
1127
+ pens = idOrTagOrPens;
1128
+ }
1129
+ pens.forEach((pen) => {
1130
+ pen.calculative.media?.play();
1131
+ pen.onStartVideo?.(pen);
1132
+ });
1133
+ }
1134
+ pauseVideo(idOrTagOrPens) {
1135
+ let pens = [];
1136
+ if (!idOrTagOrPens) {
1137
+ //TODO 寻找所有 而不是正在播放的
1138
+ pens = this.store.data.pens.filter((pen) => {
1139
+ return (pen.video || pen.audio) && pen.autoPlay;
1140
+ });
1141
+ }
1142
+ else if (typeof idOrTagOrPens === 'string') {
1143
+ pens = this.find(idOrTagOrPens);
1144
+ }
1145
+ else {
1146
+ pens = idOrTagOrPens;
1147
+ }
1148
+ pens.forEach((pen) => {
1149
+ pen.calculative.media?.pause();
1150
+ pen.onPauseVideo?.(pen);
1151
+ });
1152
+ }
1153
+ stopVideo(idOrTagOrPens) {
1154
+ let pens = [];
1155
+ if (!idOrTagOrPens) {
1156
+ pens = this.store.data.pens.filter((pen) => {
1157
+ return (pen.video || pen.audio) && pen.autoPlay;
1158
+ });
1159
+ }
1160
+ else if (typeof idOrTagOrPens === 'string') {
1161
+ pens = this.find(idOrTagOrPens);
1162
+ }
1163
+ else {
1164
+ pens = idOrTagOrPens;
1165
+ }
1166
+ pens.forEach((pen) => {
1167
+ if (pen.calculative.media) {
1168
+ pen.calculative.media.currentTime = 0;
1169
+ pen.calculative.media.pause();
1170
+ }
1171
+ pen.onStopVideo?.(pen);
1172
+ });
1173
+ }
1174
+ calcAnimateDuration(pen) {
1175
+ return pen.frames.reduce((prev, frame) => prev + frame.duration, 0);
1176
+ }
1177
+ /**
1178
+ * 组合
1179
+ * @param pens 组合的画笔们
1180
+ * @param showChild 组合后展示第几个孩子
1181
+ */
1182
+ combine(pens = this.store.active, showChild) {
1183
+ if (!pens || !pens.length) {
1184
+ return;
1185
+ }
1186
+ const initPens = deepClone(pens);
1187
+ if (pens.length === 1 && pens[0].type) {
1188
+ pens[0].type = PenType.Node;
1189
+ this.canvas.active(pens);
1190
+ this.pushHistory({
1191
+ type: EditType.Update,
1192
+ initPens,
1193
+ pens: deepClone(pens, true),
1194
+ });
1195
+ this.render();
1196
+ return;
1197
+ }
1198
+ const rect = getRect(pens);
1199
+ let parent = {
1200
+ id: s8(),
1201
+ name: 'combine',
1202
+ ...rect,
1203
+ children: [],
1204
+ showChild,
1205
+ };
1206
+ // const p = pens.find((pen) => {
1207
+ // // TODO: js 计算误差,可能导致包含着其它的 pens 的最大 pen 无法计算出来
1208
+ // return pen.width === rect.width && pen.height === rect.height;
1209
+ // });
1210
+ // // 其中一个认为是父节点
1211
+ // const oneIsParent = p && showChild == undefined;
1212
+ // if (oneIsParent) {
1213
+ // if (!p.children) {
1214
+ // p.children = [];
1215
+ // }
1216
+ // parent = p;
1217
+ // } else {
1218
+ // 若组合为状态,那么 parent 一定是 combine
1219
+ this.canvas.makePen(parent);
1220
+ // }
1221
+ const initParent = deepClone(parent);
1222
+ let minIndex = Infinity;
1223
+ pens.forEach((pen) => {
1224
+ const index = this.store.data.pens.findIndex((_pen) => _pen.id === pen.id);
1225
+ if (index < minIndex) {
1226
+ minIndex = index;
1227
+ }
1228
+ if (pen === parent || pen.parentId === parent.id || pen.id === parent.id) {
1229
+ return;
1230
+ }
1231
+ // pen 来自于 store.active ,不存在有 parentId 的情况
1232
+ parent.children.push(pen.id);
1233
+ pen.parentId = parent.id;
1234
+ const childRect = calcRelativeRect(pen.calculative.worldRect, rect);
1235
+ Object.assign(pen, childRect);
1236
+ pen.locked = pen.lockedOnCombine ?? LockState.DisableMove;
1237
+ pen.locked = (pen.interaction || isInteraction.includes(pen.name)) ? 0 : pen.locked;
1238
+ });
1239
+ //将组合后的父节点置底
1240
+ this.store.data.pens.splice(minIndex, 0, parent);
1241
+ this.store.data.pens.pop();
1242
+ this.canvas.active([parent]);
1243
+ let step = 1;
1244
+ // if (!oneIsParent) {
1245
+ // step = 2;
1246
+ // this.pushHistory({
1247
+ // type: EditType.Add,
1248
+ // pens: [parent],
1249
+ // step,
1250
+ // });
1251
+ // this.store.emitter.emit('add', [parent]);
1252
+ // }
1253
+ this.pushHistory({
1254
+ type: EditType.Add,
1255
+ pens: [initParent],
1256
+ step: 3,
1257
+ });
1258
+ this.pushHistory({
1259
+ type: EditType.Update,
1260
+ initPens: [initParent],
1261
+ pens: [parent],
1262
+ step: 3,
1263
+ });
1264
+ this.pushHistory({
1265
+ type: EditType.Update,
1266
+ initPens,
1267
+ pens,
1268
+ step: 3,
1269
+ });
1270
+ if (showChild != undefined) {
1271
+ pens.forEach((pen) => {
1272
+ calcInView(pen, true);
1273
+ });
1274
+ this.initImageCanvas([parent]);
1275
+ }
1276
+ this.store.emitter.emit('combine', [parent]);
1277
+ this.render();
1278
+ return parent;
1279
+ }
1280
+ uncombine(pen) {
1281
+ if (!pen && this.store.active) {
1282
+ pen = this.store.active[0];
1283
+ }
1284
+ if (!pen || !pen.children) {
1285
+ return;
1286
+ }
1287
+ const children = pen.children.map((childId) => this.store.pens[childId]);
1288
+ let initPens = deepClone(children);
1289
+ children.forEach((child) => {
1290
+ child.parentId = undefined;
1291
+ child.x = child.calculative.worldRect.x;
1292
+ child.y = child.calculative.worldRect.y;
1293
+ child.width = child.calculative.worldRect.width;
1294
+ child.height = child.calculative.worldRect.height;
1295
+ child.locked = LockState.None;
1296
+ child.calculative.active = undefined;
1297
+ child.calculative.hover = false;
1298
+ this.setVisible(child, true); // 子节点的 visible 属性已经改变,需要恢复
1299
+ });
1300
+ const step = this.isCombine(pen) ? 3 : 2;
1301
+ this.pushHistory({
1302
+ type: EditType.Update,
1303
+ initPens,
1304
+ pens: children,
1305
+ step,
1306
+ });
1307
+ initPens = [deepClone(pen)];
1308
+ pen.children = undefined;
1309
+ // 保存修改 children 的历史记录
1310
+ this.pushHistory({
1311
+ type: EditType.Update,
1312
+ initPens,
1313
+ pens: [pen],
1314
+ step,
1315
+ });
1316
+ if (this.isCombine(pen)) {
1317
+ this.delete([pen]);
1318
+ // delete 会记录 history , 更改 step 即可
1319
+ this.store.histories[this.store.histories.length - 1].step = step;
1320
+ }
1321
+ this.inactive();
1322
+ }
1323
+ appendChild(pens = this.store.active) {
1324
+ if (!pens) {
1325
+ return;
1326
+ }
1327
+ if (pens.length < 2) {
1328
+ return;
1329
+ }
1330
+ const pIdx = pens.findIndex(pen => pen.name === 'combine' && pen.showChild !== undefined);
1331
+ if (pIdx !== -1) {
1332
+ let parent = pens[pIdx];
1333
+ // this.pushChildren(parent,[...pens.slice(0, pIdx), ...pens.slice(pIdx + 1)]);
1334
+ const rect = getRect(pens);
1335
+ Object.assign(parent, rect);
1336
+ Object.assign(parent.calculative.worldRect, rect);
1337
+ calcWorldAnchors(parent);
1338
+ parent.children.forEach(penId => {
1339
+ const pen = this.store.pens[penId];
1340
+ const childRect = calcRelativeRect(pen.calculative.worldRect, rect);
1341
+ Object.assign(pen, childRect);
1342
+ });
1343
+ pens.forEach((pen) => {
1344
+ if (pen.id !== parent.id) {
1345
+ parent.children.push(pen.id);
1346
+ pen.parentId = parent.id;
1347
+ const childRect = calcRelativeRect(pen.calculative.worldRect, rect);
1348
+ Object.assign(pen, childRect);
1349
+ pen.locked = pen.lockedOnCombine ?? LockState.DisableMove;
1350
+ pen.locked = (pen.interaction || isInteraction.includes(pen.name)) ? 0 : pen.locked;
1351
+ calcInView(pen, true);
1352
+ }
1353
+ });
1354
+ this.initImageCanvas(pens);
1355
+ this.render();
1356
+ }
1357
+ else {
1358
+ console.warn('Invalid operation!');
1359
+ }
1360
+ }
1361
+ isCombine(pen) {
1362
+ if (pen.name === 'combine') {
1363
+ return true;
1364
+ }
1365
+ if (pen.children && pen.children.length > 0) {
1366
+ return true;
1367
+ }
1368
+ return false;
1369
+ }
1370
+ active(pens, emit = true) {
1371
+ this.canvas.active(pens, emit);
1372
+ }
1373
+ inactive() {
1374
+ this.canvas.inactive();
1375
+ }
1376
+ activeAll() {
1377
+ this.canvas.active(this.store.data.pens.filter((pen) => !pen.parentId && pen.locked !== LockState.Disable));
1378
+ this.render();
1379
+ }
1380
+ /**
1381
+ * 删除画笔
1382
+ * @param pens 需要删除的画笔们
1383
+ * @param canDelLocked 是否删除已经锁住的画笔
1384
+ */
1385
+ delete(pens, canDelLocked = false, history = true) {
1386
+ this.canvas.delete(pens, canDelLocked, history);
1387
+ }
1388
+ scale(scale, center = { x: 0, y: 0 }) {
1389
+ this.canvas.scale(scale, center);
1390
+ }
1391
+ translate(x, y) {
1392
+ this.canvas.translate(x, y);
1393
+ }
1394
+ translatePens(pens, x, y) {
1395
+ this.canvas.translatePens(pens, x, y);
1396
+ }
1397
+ getParent(pen, root) {
1398
+ return getParent(pen, root);
1399
+ }
1400
+ getAllChildren(pen) {
1401
+ return getAllChildren(pen, this.store);
1402
+ }
1403
+ getAllFollowers(pen) {
1404
+ return getAllFollowers(pen, this.store);
1405
+ }
1406
+ data() {
1407
+ const data = deepClone(this.store.data);
1408
+ const { pens, paths } = this.store.data;
1409
+ data.version = pkg.version;
1410
+ // TODO: 未在 delete 时清除,避免撤销等操作。
1411
+ // 清除一些未使用到的 paths
1412
+ data.paths = {};
1413
+ for (const pathId in paths) {
1414
+ if (Object.prototype.hasOwnProperty.call(paths, pathId)) {
1415
+ if (pens.find((pen) => pen.pathId === pathId)) {
1416
+ data.paths[pathId] = paths[pathId];
1417
+ }
1418
+ }
1419
+ }
1420
+ data.dataPoints = [...Object.keys(this.store.bind), ...Object.keys(this.store.bindDatas)];
1421
+ return data;
1422
+ }
1423
+ copy(pens) {
1424
+ this.canvas.copy(pens);
1425
+ }
1426
+ cut(pens) {
1427
+ this.canvas.cut(pens);
1428
+ }
1429
+ paste() {
1430
+ this.canvas.paste();
1431
+ }
1432
+ undo() {
1433
+ this.canvas.undo();
1434
+ }
1435
+ redo() {
1436
+ this.canvas.redo();
1437
+ }
1438
+ listenSocket() {
1439
+ try {
1440
+ let socketFn;
1441
+ const socketCbJs = this.store.data.socketCbJs;
1442
+ if (socketCbJs) {
1443
+ socketFn = new Function('e', 'context', socketCbJs);
1444
+ }
1445
+ if (!socketFn) {
1446
+ this.socketFn = null;
1447
+ return false;
1448
+ }
1449
+ this.socketFn = socketFn;
1450
+ }
1451
+ catch (e) {
1452
+ console.error('Create the function for socket:', e);
1453
+ return false;
1454
+ }
1455
+ return true;
1456
+ }
1457
+ websocketTimes = 0;
1458
+ connectWebsocket(websocket) {
1459
+ this.closeWebsocket();
1460
+ if (websocket) {
1461
+ this.store.data.websocket = websocket;
1462
+ }
1463
+ if (this.store.data.websocket) {
1464
+ this.websocket = new WebSocket(this.store.data.websocket, this.store.data.websocketProtocols || undefined);
1465
+ this.websocket.onmessage = (e) => {
1466
+ this.socketCallback(e.data, {
1467
+ type: 'websocket',
1468
+ url: this.store.data.websocket,
1469
+ });
1470
+ };
1471
+ this.websocket.onerror = (error) => {
1472
+ this.store.emitter.emit('error', { type: 'websocket', error });
1473
+ };
1474
+ this.websocket.onclose = () => {
1475
+ if (this.store.options.reconnetTimes) {
1476
+ this.websocketTimes++;
1477
+ if (this.websocketTimes >= this.store.options.reconnetTimes) {
1478
+ this.websocketTimes = 0;
1479
+ this.closeWebsocket();
1480
+ return;
1481
+ }
1482
+ }
1483
+ console.info('Canvas websocket closed and reconneting...');
1484
+ this.connectWebsocket();
1485
+ };
1486
+ }
1487
+ }
1488
+ closeWebsocket() {
1489
+ if (this.websocket) {
1490
+ this.websocket.onclose = undefined;
1491
+ this.websocket.close();
1492
+ this.websocket = undefined;
1493
+ }
1494
+ }
1495
+ mqttTimes = 0;
1496
+ connectMqtt(params) {
1497
+ this.closeMqtt();
1498
+ if (params) {
1499
+ this.store.data.mqtt = params.mqtt;
1500
+ this.store.data.mqttTopics = params.mqttTopics;
1501
+ this.store.data.mqttOptions = params.mqttOptions;
1502
+ }
1503
+ if (this.store.data.mqtt) {
1504
+ if (this.store.data.mqttOptions.clientId &&
1505
+ !this.store.data.mqttOptions.customClientId) {
1506
+ this.store.data.mqttOptions.clientId = s8();
1507
+ }
1508
+ const mqttOptions = { ...this.store.data.mqttOptions };
1509
+ // 如果没有username/password或为空字符串则删除username/password
1510
+ if (!mqttOptions.username) {
1511
+ delete mqttOptions.username;
1512
+ }
1513
+ if (!mqttOptions.password) {
1514
+ delete mqttOptions.password;
1515
+ }
1516
+ const { username, password } = mqttOptions;
1517
+ // username 和 password 必须同时存在或者同时不存在才去建立mqtt连接
1518
+ if ((username && password) || (!username && !password)) {
1519
+ this.mqttClient = mqtt.connect(this.store.data.mqtt, mqttOptions);
1520
+ this.mqttClient.on('message', (topic, message) => {
1521
+ this.socketCallback(message.toString(), {
1522
+ topic,
1523
+ type: 'mqtt',
1524
+ url: this.store.data.mqtt,
1525
+ });
1526
+ });
1527
+ this.mqttClient.on('error', (error) => {
1528
+ this.store.emitter.emit('error', { type: 'mqtt', error });
1529
+ });
1530
+ this.mqttClient.on('close', () => {
1531
+ if (this.store.options.reconnetTimes) {
1532
+ this.mqttTimes++;
1533
+ if (this.mqttTimes >= this.store.options.reconnetTimes) {
1534
+ this.mqttTimes = 0;
1535
+ this.closeMqtt();
1536
+ }
1537
+ }
1538
+ });
1539
+ if (this.store.data.mqttTopics) {
1540
+ this.mqttClient.subscribe(this.store.data.mqttTopics.split(','));
1541
+ }
1542
+ }
1543
+ else {
1544
+ console.warn('缺少用户名或密码');
1545
+ }
1546
+ }
1547
+ }
1548
+ closeMqtt() {
1549
+ this.mqttClient?.end();
1550
+ }
1551
+ httpTimer;
1552
+ httpTimerList = [];
1553
+ connectHttp() {
1554
+ this.closeHttp();
1555
+ const { https } = this.store.data;
1556
+ if (https) {
1557
+ if (!this.store.data.cancelFirstConnect) {
1558
+ https.forEach(async (item) => {
1559
+ this.oldRequestHttp(item);
1560
+ });
1561
+ }
1562
+ https.forEach((item, index) => {
1563
+ if (item.http) {
1564
+ item.times = 0;
1565
+ this.httpTimerList[index] = setInterval(async () => {
1566
+ // 默认每一秒请求一次
1567
+ this.oldRequestHttp(item);
1568
+ if (this.store.options.reconnetTimes) {
1569
+ item.times++;
1570
+ if (item.times >= this.store.options.reconnetTimes) {
1571
+ item.times = 0;
1572
+ clearInterval(this.httpTimerList[index]);
1573
+ this.httpTimerList[index] = undefined;
1574
+ }
1575
+ }
1576
+ }, item.httpTimeInterval || 1000);
1577
+ }
1578
+ });
1579
+ }
1580
+ else {
1581
+ const { http, httpTimeInterval, httpHeaders } = this.store.data;
1582
+ if (http) {
1583
+ this.httpTimer = setInterval(async () => {
1584
+ // 默认每一秒请求一次
1585
+ const res = await fetch(http, {
1586
+ headers: httpHeaders,
1587
+ });
1588
+ if (res.ok) {
1589
+ const data = await res.text();
1590
+ this.socketCallback(data, { type: 'http', url: http });
1591
+ }
1592
+ }, httpTimeInterval || 1000);
1593
+ }
1594
+ }
1595
+ }
1596
+ async oldRequestHttp(_req) {
1597
+ let req = deepClone(_req);
1598
+ if (req.http) {
1599
+ const res = await fetch(req.http, {
1600
+ headers: req.httpHeaders,
1601
+ method: req.method || 'GET',
1602
+ body: req.method === 'POST' ? JSON.stringify(req.body) : undefined,
1603
+ });
1604
+ if (res.ok) {
1605
+ const data = await res.text();
1606
+ this.socketCallback(data, { type: 'http', url: req.http });
1607
+ }
1608
+ else {
1609
+ this.store.emitter.emit('error', { type: 'http', error: res });
1610
+ }
1611
+ }
1612
+ }
1613
+ async sendDatabyHttp(data) {
1614
+ const { https } = this.store.data;
1615
+ if (https) {
1616
+ https.forEach(async (item) => {
1617
+ if (item.http) {
1618
+ const res = await fetch(item.http, {
1619
+ method: 'post',
1620
+ body: data,
1621
+ headers: item.httpHeaders,
1622
+ });
1623
+ if (res.ok) {
1624
+ console.info('http消息发送成功');
1625
+ }
1626
+ }
1627
+ });
1628
+ }
1629
+ else {
1630
+ const { http, httpHeaders } = this.store.data;
1631
+ if (http) {
1632
+ // 默认每一秒请求一次
1633
+ const res = await fetch(http, {
1634
+ method: 'post',
1635
+ body: data,
1636
+ headers: httpHeaders,
1637
+ });
1638
+ if (res.ok) {
1639
+ console.info('http消息发送成功');
1640
+ }
1641
+ }
1642
+ }
1643
+ }
1644
+ closeHttp() {
1645
+ clearInterval(this.httpTimer);
1646
+ this.httpTimer = undefined;
1647
+ this.httpTimerList &&
1648
+ this.httpTimerList.forEach((_httpTimer) => {
1649
+ clearInterval(_httpTimer);
1650
+ _httpTimer = undefined;
1651
+ });
1652
+ }
1653
+ updateTimer;
1654
+ updateTimerList = [];
1655
+ connectNetwork() {
1656
+ this.closeNetwork();
1657
+ const { networks } = this.store.data;
1658
+ const https = [];
1659
+ if (networks) {
1660
+ let mqttIndex = 0;
1661
+ this.mqttClients = [];
1662
+ let websocketIndex = 0;
1663
+ this.websockets = [];
1664
+ networks.forEach((net) => {
1665
+ if (net.type === 'subscribe') {
1666
+ if (net.protocol === 'mqtt') {
1667
+ net.index = mqttIndex;
1668
+ if (net.options.clientId && !net.options.customClientId) {
1669
+ net.options.clientId = s8();
1670
+ }
1671
+ net.times = 0;
1672
+ this.mqttClients[mqttIndex] = mqtt.connect(net.url, net.options);
1673
+ this.mqttClients[mqttIndex].on('message', (topic, message) => {
1674
+ this.socketCallback(message.toString(), {
1675
+ topic,
1676
+ type: 'mqtt',
1677
+ url: net.url,
1678
+ });
1679
+ });
1680
+ this.mqttClients[mqttIndex].on('error', (error) => {
1681
+ this.store.emitter.emit('error', { type: 'mqtt', error });
1682
+ });
1683
+ this.mqttClients[mqttIndex].on('close', () => {
1684
+ if (this.store.options.reconnetTimes) {
1685
+ net.times++;
1686
+ if (net.times >= this.store.options.reconnetTimes) {
1687
+ net.times = 0;
1688
+ this.mqttClients && this.mqttClients[net.index]?.end();
1689
+ }
1690
+ }
1691
+ });
1692
+ if (net.topics) {
1693
+ this.mqttClients[mqttIndex].subscribe(net.topics.split(','));
1694
+ }
1695
+ mqttIndex += 1;
1696
+ }
1697
+ else if (net.protocol === 'websocket') {
1698
+ net.index = websocketIndex;
1699
+ this.connectNetWebSocket(net);
1700
+ // this.websockets[websocketIndex] = new WebSocket(
1701
+ // net.url,
1702
+ // net.protocols || undefined
1703
+ // );
1704
+ // this.websockets[websocketIndex].onmessage = (e) => {
1705
+ // this.socketCallback(e.data, { type: 'websocket', url: net.url });
1706
+ // };
1707
+ // this.websockets[websocketIndex].onerror = (error) => {
1708
+ // this.store.emitter.emit('error', { type: 'websocket', error });
1709
+ // };
1710
+ // this.websockets[websocketIndex].onclose = () => {
1711
+ // if (this.store.options.reconnetTimes) {
1712
+ // net.times++;
1713
+ // if (net.times >= this.store.options.reconnetTimes) {
1714
+ // net.times = 0;
1715
+ // this.websockets[net.index]?.close();
1716
+ // return;
1717
+ // }
1718
+ // }
1719
+ // console.info('Canvas websocket closed and reconneting...');
1720
+ // };
1721
+ websocketIndex += 1;
1722
+ }
1723
+ else if (net.protocol === 'http') {
1724
+ https.push({
1725
+ url: net.url,
1726
+ interval: net.interval,
1727
+ headers: net.headers || undefined,
1728
+ method: net.method,
1729
+ body: net.body,
1730
+ });
1731
+ }
1732
+ }
1733
+ });
1734
+ }
1735
+ this.onNetworkConnect(https);
1736
+ }
1737
+ connectNetWebSocket(net) {
1738
+ if (this.websockets[net.index]) {
1739
+ this.websockets[net.index].onclose = undefined;
1740
+ this.websockets[net.index]?.close();
1741
+ this.websockets[net.index] = undefined;
1742
+ }
1743
+ this.websockets[net.index] = new WebSocket(net.url, net.protocols || undefined);
1744
+ this.websockets[net.index].onmessage = (e) => {
1745
+ this.socketCallback(e.data, { type: 'websocket', url: net.url });
1746
+ };
1747
+ this.websockets[net.index].onerror = (error) => {
1748
+ this.store.emitter.emit('error', { type: 'websocket', error });
1749
+ };
1750
+ this.websockets[net.index].onclose = () => {
1751
+ if (this.store.options.reconnetTimes) {
1752
+ net.times++;
1753
+ if (net.times >= this.store.options.reconnetTimes) {
1754
+ net.times = 0;
1755
+ this.websockets[net.index].onclose = undefined;
1756
+ this.websockets[net.index]?.close();
1757
+ this.websockets[net.index] = undefined;
1758
+ return;
1759
+ }
1760
+ }
1761
+ setTimeout(() => {
1762
+ console.info('Canvas websocket closed and reconneting...');
1763
+ this.connectNetWebSocket(net);
1764
+ }, 2000);
1765
+ };
1766
+ }
1767
+ randomString(e) {
1768
+ e = e || 32;
1769
+ let t = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678', a = t.length, n = '';
1770
+ for (let i = 0; i < e; i++) {
1771
+ n += t.charAt(Math.floor(Math.random() * a));
1772
+ }
1773
+ return n;
1774
+ }
1775
+ mockValue(data) {
1776
+ let value = undefined;
1777
+ if (data.enableMock && data.mock !== undefined) {
1778
+ if (data.type === 'float') {
1779
+ if (data.mock && data.mock.indexOf(',') !== -1) {
1780
+ let arr = data.mock.split(',');
1781
+ let rai = Math.floor(Math.random() * arr.length);
1782
+ value = parseFloat(arr[rai]);
1783
+ }
1784
+ else if (data.mock && data.mock.indexOf('-') !== -1) {
1785
+ let max;
1786
+ let min;
1787
+ let len;
1788
+ let arr = data.mock.split('-');
1789
+ if (data.mock.charAt(0) === '-') {
1790
+ //负数
1791
+ if (arr.length === 4) {
1792
+ max = -parseFloat(arr[3]);
1793
+ min = -parseFloat(arr[1]);
1794
+ len = arr[3];
1795
+ }
1796
+ else {
1797
+ max = parseFloat(arr[2]);
1798
+ min = -parseFloat(arr[1]);
1799
+ len = arr[2];
1800
+ }
1801
+ }
1802
+ else {
1803
+ max = parseFloat(arr[1]);
1804
+ min = parseFloat(arr[0]);
1805
+ len = arr[1];
1806
+ }
1807
+ if ((len + '').indexOf('.') !== -1) {
1808
+ let length = (len + '').split('.')[1].length;
1809
+ value = (Math.random() * (max - min) + min).toFixed(length);
1810
+ }
1811
+ else {
1812
+ value = Math.random() * (max - min) + min;
1813
+ }
1814
+ }
1815
+ else {
1816
+ value = parseFloat(data.mock);
1817
+ }
1818
+ }
1819
+ else if (data.type === 'integer') {
1820
+ if (data.mock && data.mock.indexOf(',') !== -1) {
1821
+ let arr = data.mock.split(',');
1822
+ let rai = Math.floor(Math.random() * arr.length);
1823
+ value = parseInt(arr[rai]);
1824
+ }
1825
+ else if (data.mock && data.mock.indexOf('-') !== -1) {
1826
+ let max;
1827
+ let min;
1828
+ let arr = data.mock.split('-');
1829
+ if (data.mock.charAt(0) === '-') {
1830
+ if (arr.length === 4) {
1831
+ max = -parseFloat(arr[3]);
1832
+ min = -parseFloat(arr[1]);
1833
+ }
1834
+ else {
1835
+ max = parseFloat(arr[2]);
1836
+ min = -parseFloat(arr[1]);
1837
+ }
1838
+ }
1839
+ else {
1840
+ max = parseInt(arr[1]);
1841
+ min = parseInt(arr[0]);
1842
+ }
1843
+ value = parseInt(Math.random() * (max - min) + min + '');
1844
+ }
1845
+ else {
1846
+ value = parseInt(data.mock);
1847
+ }
1848
+ }
1849
+ else if (data.type === 'bool') {
1850
+ if (typeof data.mock === 'boolean') {
1851
+ value = data.mock;
1852
+ }
1853
+ else if ('true' === data.mock) {
1854
+ value = true;
1855
+ }
1856
+ else if ('false' === data.mock) {
1857
+ value = false;
1858
+ }
1859
+ else {
1860
+ value = Math.random() < 0.5;
1861
+ }
1862
+ }
1863
+ else if (data.type === 'object' || data.type === 'array') {
1864
+ if (data.mock) {
1865
+ //对象or数组 不mock
1866
+ // _d[realTime.key] = realTime.value;
1867
+ }
1868
+ }
1869
+ else {
1870
+ //if (realTime.type === 'string')
1871
+ if (data.mock && data.mock.indexOf(',') !== -1) {
1872
+ let str = data.mock.substring(1, data.mock.length - 1);
1873
+ let arr = str.split(',');
1874
+ let rai = Math.floor(Math.random() * arr.length);
1875
+ value = arr[rai];
1876
+ }
1877
+ else if (data.mock &&
1878
+ data.mock.startsWith('[') &&
1879
+ data.mock.endsWith(']')) {
1880
+ let len = parseInt(data.mock.substring(1, data.mock.length - 1));
1881
+ value = this.randomString(len);
1882
+ }
1883
+ else {
1884
+ value = data.mock;
1885
+ }
1886
+ }
1887
+ }
1888
+ return value;
1889
+ }
1890
+ //数据模拟
1891
+ dataMock() {
1892
+ let arr = [];
1893
+ this.store.data.dataset?.devices?.forEach((data) => {
1894
+ let value = this.mockValue(data);
1895
+ if (value !== undefined) {
1896
+ arr.push({
1897
+ id: data.id,
1898
+ value
1899
+ });
1900
+ }
1901
+ });
1902
+ if (arr.length) {
1903
+ this.setDatas(arr, {
1904
+ render: true,
1905
+ doEvent: true,
1906
+ history: false,
1907
+ });
1908
+ }
1909
+ }
1910
+ startDataMock() {
1911
+ let enable = this.store.data.enableMock;
1912
+ if (enable) {
1913
+ this.stopDataMock();
1914
+ this.initBinds();
1915
+ this.updateTimer = setInterval(() => {
1916
+ //本地调试
1917
+ this.store.data.pens.forEach((pen) => {
1918
+ this.penMock(pen);
1919
+ });
1920
+ this.dataMock();
1921
+ this.render();
1922
+ }, this.store.data.networkInterval || 1000);
1923
+ }
1924
+ }
1925
+ stopDataMock() {
1926
+ clearInterval(this.updateTimer);
1927
+ this.updateTimer = undefined;
1928
+ }
1929
+ penMock(pen) {
1930
+ if (pen.realTimes) {
1931
+ let _d = {};
1932
+ pen.realTimes.forEach((realTime) => {
1933
+ let value = this.mockValue(realTime);
1934
+ if (value !== undefined) {
1935
+ _d[realTime.key] = value;
1936
+ }
1937
+ });
1938
+ if (Object.keys(_d).length) {
1939
+ let data = pen.onBeforeValue ? pen.onBeforeValue(pen, _d) : _d;
1940
+ this.canvas.updateValue(pen, data);
1941
+ // this.store.emitter.emit('valueUpdate', pen);
1942
+ pen.onValue?.(pen);
1943
+ this.store.emitter.emit('valueUpdate', pen);
1944
+ }
1945
+ }
1946
+ }
1947
+ penNetwork(pen) {
1948
+ const penNetwork = {
1949
+ url: pen.apiUrl,
1950
+ method: pen.apiMethod,
1951
+ headers: pen.apiHeaders,
1952
+ body: pen.apiBody,
1953
+ };
1954
+ //临时请求一次
1955
+ this.requestHttp(penNetwork);
1956
+ if (pen.apiEnable) {
1957
+ if (!this.store.pensNetwork) {
1958
+ this.store.pensNetwork = {};
1959
+ }
1960
+ this.store.pensNetwork[pen.id] = penNetwork;
1961
+ }
1962
+ else {
1963
+ delete this.store.pensNetwork[pen.id];
1964
+ }
1965
+ }
1966
+ //获取动态参数
1967
+ getDynamicParam(key) {
1968
+ function getCookie(name) {
1969
+ let arr;
1970
+ const reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)');
1971
+ if ((arr = document.cookie.match(reg))) {
1972
+ return decodeURIComponent(arr[2]);
1973
+ }
1974
+ else {
1975
+ return '';
1976
+ }
1977
+ }
1978
+ let params = queryURLParams();
1979
+ let value = params[key] || localStorage[key] || getCookie(key) || '';
1980
+ return value;
1981
+ }
1982
+ onNetworkConnect(https) {
1983
+ // let enable = this.store.data.enableMock;
1984
+ if (!(https && https.length)) {
1985
+ return;
1986
+ }
1987
+ if (this.store.pensNetwork) {
1988
+ for (let key in this.store.pensNetwork) {
1989
+ https.push(this.store.pensNetwork[key]);
1990
+ }
1991
+ }
1992
+ if (!this.store.data.cancelFirstConnect) {
1993
+ https.forEach(async (_item) => {
1994
+ this.requestHttp(_item);
1995
+ });
1996
+ }
1997
+ // if( enable ){
1998
+ // this.updateTimer = setInterval(() => {
1999
+ // //模拟数据
2000
+ // this.store.data.pens.forEach((pen) => {
2001
+ // this.penMock(pen);
2002
+ // });
2003
+ // // https.forEach(async (_item) => {
2004
+ // // this.requestHttp(_item);
2005
+ // // });
2006
+ // this.render();
2007
+ // }, this.store.data.networkInterval || 1000);
2008
+ // }
2009
+ https.forEach((_item, index) => {
2010
+ _item.times = 0;
2011
+ this.updateTimerList[index] = setInterval(async () => {
2012
+ this.requestHttp(_item);
2013
+ if (this.store.options.reconnetTimes) {
2014
+ _item.times++;
2015
+ if (_item.times >= this.store.options.reconnetTimes) {
2016
+ _item.times = 0;
2017
+ clearInterval(this.updateTimerList[index]);
2018
+ this.updateTimerList[index] = undefined;
2019
+ }
2020
+ }
2021
+ }, _item.interval || 1000);
2022
+ });
2023
+ }
2024
+ async requestHttp(_req) {
2025
+ let req = deepClone(_req);
2026
+ if (req.url) {
2027
+ if (typeof req.headers === 'object') {
2028
+ for (let i in req.headers) {
2029
+ if (typeof req.headers[i] === 'string') {
2030
+ let keys = req.headers[i].match(/(?<=\$\{).*?(?=\})/g);
2031
+ if (keys) {
2032
+ req.headers[i] = req.headers[i].replace(`\${${keys[0]}}`, this.getDynamicParam(keys[0]));
2033
+ }
2034
+ }
2035
+ }
2036
+ }
2037
+ if (typeof req.body === 'object') {
2038
+ for (let i in req.body) {
2039
+ if (typeof req.body[i] === 'string') {
2040
+ let keys = req.body[i].match(/(?<=\$\{).*?(?=\})/g);
2041
+ if (keys) {
2042
+ req.body[i] = req.body[i].replace(`\${${keys[0]}}`, this.getDynamicParam(keys[0]));
2043
+ }
2044
+ }
2045
+ }
2046
+ }
2047
+ // 默认每一秒请求一次
2048
+ const res = await fetch(req.url, {
2049
+ headers: req.headers,
2050
+ method: req.method,
2051
+ body: req.method === 'GET' ? undefined : JSON.stringify(req.body),
2052
+ });
2053
+ if (res.ok) {
2054
+ const data = await res.text();
2055
+ this.socketCallback(data, { type: 'http', url: req.url });
2056
+ }
2057
+ else {
2058
+ this.store.emitter.emit('error', { type: 'http', error: res });
2059
+ }
2060
+ }
2061
+ }
2062
+ closeNetwork() {
2063
+ this.mqttClients &&
2064
+ this.mqttClients.forEach((mqttClient) => {
2065
+ mqttClient.end();
2066
+ });
2067
+ this.websockets &&
2068
+ this.websockets.forEach((websocket) => {
2069
+ if (websocket) {
2070
+ websocket.onclose = undefined;
2071
+ websocket.close();
2072
+ websocket = undefined;
2073
+ }
2074
+ });
2075
+ this.mqttClients = undefined;
2076
+ this.websockets = undefined;
2077
+ // clearInterval(this.updateTimer);
2078
+ // this.updateTimer = undefined;
2079
+ this.updateTimerList &&
2080
+ this.updateTimerList.forEach((_updateTimer) => {
2081
+ clearInterval(_updateTimer);
2082
+ _updateTimer = undefined;
2083
+ });
2084
+ }
2085
+ socketCallback(message, context) {
2086
+ this.store.emitter.emit('socket', { message, context });
2087
+ let _message = message;
2088
+ if (this.socketFn) {
2089
+ _message = this.socketFn(message, {
2090
+ meta2d: this,
2091
+ type: context.type,
2092
+ topic: context.topic,
2093
+ url: context.url,
2094
+ });
2095
+ if (!_message) {
2096
+ return;
2097
+ }
2098
+ }
2099
+ if (_message === true) {
2100
+ _message = message;
2101
+ }
2102
+ let data;
2103
+ if (_message.constructor === Object || _message.constructor === Array) {
2104
+ data = _message;
2105
+ }
2106
+ else if (typeof _message === 'string') {
2107
+ try {
2108
+ data = JSON.parse(_message);
2109
+ }
2110
+ catch (error) {
2111
+ console.warn('Invalid socket data:', data, error);
2112
+ }
2113
+ }
2114
+ else {
2115
+ return;
2116
+ }
2117
+ if (!data) {
2118
+ return;
2119
+ }
2120
+ if (!Array.isArray(data)) {
2121
+ data = [data];
2122
+ }
2123
+ if (!data.length) {
2124
+ return;
2125
+ }
2126
+ if (data[0].dataId) {
2127
+ this.setDatas(data);
2128
+ }
2129
+ else {
2130
+ data.forEach((_data) => {
2131
+ this.setValue(_data);
2132
+ });
2133
+ }
2134
+ }
2135
+ // 绑定变量方式更新组件数据
2136
+ setDatas(datas, { render = true, doEvent = true, history, } = {}) {
2137
+ // 把{dataId: string; value: any}转成setValue格式数据
2138
+ const penValues = new Map();
2139
+ datas.forEach((v) => {
2140
+ this.store.bindDatas[v.dataId]?.forEach((p) => {
2141
+ const pen = this.store.pens[p.id];
2142
+ if (!pen) {
2143
+ return;
2144
+ }
2145
+ let penValue = penValues.get(pen);
2146
+ if (!pen.noOnBinds && typeof pen.onBinds === 'function') {
2147
+ // 已经计算了
2148
+ if (penValue) {
2149
+ return;
2150
+ }
2151
+ penValues.set(pen, pen.onBinds(pen, datas, p.formItem));
2152
+ return;
2153
+ }
2154
+ if (penValue) {
2155
+ penValue[p.formItem.key] = v.value;
2156
+ }
2157
+ else {
2158
+ penValue = {
2159
+ id: p.id,
2160
+ [p.formItem.key]: v.value,
2161
+ };
2162
+ penValues.set(pen, penValue);
2163
+ }
2164
+ });
2165
+ this.store.bind[v.id]?.forEach((p) => {
2166
+ const pen = this.store.pens[p.id];
2167
+ if (!pen) {
2168
+ return;
2169
+ }
2170
+ let penValue = penValues.get(pen);
2171
+ // if (typeof pen.onBinds === 'function') {
2172
+ // // 已经计算了
2173
+ // if (penValue) {
2174
+ // return;
2175
+ // }
2176
+ // //TODO onBinds的情况
2177
+ // penValues.set(pen, pen.onBinds(pen, datas));
2178
+ // return;
2179
+ // }
2180
+ if (penValue) {
2181
+ penValue[p.key] = v.value;
2182
+ }
2183
+ else {
2184
+ penValue = {
2185
+ id: p.id,
2186
+ [p.key]: v.value,
2187
+ };
2188
+ penValues.set(pen, penValue);
2189
+ }
2190
+ });
2191
+ });
2192
+ this.store.data.locked && this.doDataEvent(datas);
2193
+ let initPens;
2194
+ let pens;
2195
+ if (history) {
2196
+ initPens = [];
2197
+ }
2198
+ penValues.forEach((value, pen) => {
2199
+ this.setValue(value, { render: false, doEvent, history: false });
2200
+ if (history) {
2201
+ initPens.push(deepClone(pen, true));
2202
+ pens.push(pen);
2203
+ }
2204
+ });
2205
+ render && this.render();
2206
+ if (history) {
2207
+ this.pushHistory({
2208
+ type: EditType.Update,
2209
+ initPens,
2210
+ pens,
2211
+ });
2212
+ }
2213
+ }
2214
+ setValue(data, { render = true, doEvent = true, history, } = {}) {
2215
+ let pens = [];
2216
+ if (!data) {
2217
+ return;
2218
+ }
2219
+ if (data.id) {
2220
+ if (data.id === this.store.data.id) {
2221
+ this.setDatabyOptions(data);
2222
+ if (data.bkImage) {
2223
+ this.setBackgroundImage(data.bkImage);
2224
+ }
2225
+ if (data.background) {
2226
+ this.setBackgroundColor(data.background);
2227
+ }
2228
+ this.render();
2229
+ return;
2230
+ }
2231
+ const pen = this.store.pens[data.id];
2232
+ if (pen) {
2233
+ pens = [pen];
2234
+ }
2235
+ else {
2236
+ //bind 绑定变量的情况
2237
+ let bindArr = this.store.bind[data.id];
2238
+ if (bindArr && bindArr.length) {
2239
+ pens = [];
2240
+ this.setDatas([data], {
2241
+ render,
2242
+ doEvent,
2243
+ history,
2244
+ });
2245
+ return;
2246
+ }
2247
+ }
2248
+ }
2249
+ else if (data.dataId) {
2250
+ pens = [];
2251
+ this.setDatas([data], {
2252
+ render,
2253
+ doEvent,
2254
+ history,
2255
+ });
2256
+ return;
2257
+ }
2258
+ else if (data.tag) {
2259
+ pens = this.find(data.tag);
2260
+ }
2261
+ else {
2262
+ let binds = [];
2263
+ for (let key in data) {
2264
+ binds.push({
2265
+ dataId: key,
2266
+ id: key,
2267
+ value: data[key]
2268
+ });
2269
+ }
2270
+ ;
2271
+ if (binds.length) {
2272
+ this.setDatas(binds, {
2273
+ render,
2274
+ doEvent,
2275
+ history,
2276
+ });
2277
+ }
2278
+ return;
2279
+ }
2280
+ history = history && !this.store.data.locked;
2281
+ let initPens;
2282
+ if (history) {
2283
+ initPens = deepClone(pens);
2284
+ }
2285
+ pens.forEach((pen) => {
2286
+ const afterData = pen.onBeforeValue
2287
+ ? pen.onBeforeValue(pen, data)
2288
+ : data;
2289
+ if (data.frames) {
2290
+ this.stopAnimate([pen]);
2291
+ if (!data.showDuration) {
2292
+ data.showDuration = data.frames.reduce((total, item) => {
2293
+ return total + item.duration;
2294
+ }, 0);
2295
+ }
2296
+ }
2297
+ setChildValue(pen, afterData);
2298
+ this.canvas.updateValue(pen, afterData);
2299
+ pen.onValue?.(pen);
2300
+ });
2301
+ if (!this.store.data.locked &&
2302
+ this.store.active.length &&
2303
+ !this.canvas.movingPens) {
2304
+ // 移动过程中,不重算 activeRect
2305
+ this.canvas.calcActiveRect();
2306
+ }
2307
+ if (history) {
2308
+ let _pens = deepClone(pens);
2309
+ this.pushHistory({
2310
+ type: EditType.Update,
2311
+ initPens,
2312
+ pens: _pens,
2313
+ });
2314
+ }
2315
+ doEvent &&
2316
+ pens.forEach((pen) => {
2317
+ this.store.emitter.emit('valueUpdate', pen);
2318
+ });
2319
+ render && this.render();
2320
+ }
2321
+ /**
2322
+ * @deprecated 改用 setValue
2323
+ */
2324
+ _setValue(data, history = false) {
2325
+ this.setValue(data, { history, render: false, doEvent: false });
2326
+ }
2327
+ pushHistory(action) {
2328
+ this.canvas.pushHistory(action);
2329
+ }
2330
+ showInput(pen, rect) {
2331
+ this.canvas.showInput(pen, rect);
2332
+ }
2333
+ hideInput() {
2334
+ this.canvas.hideInput();
2335
+ }
2336
+ clearDropdownList() {
2337
+ this.canvas.clearDropdownList();
2338
+ }
2339
+ clearRuleLines() {
2340
+ this.canvas.clearRuleLines();
2341
+ }
2342
+ onEvent = (eventName, e) => {
2343
+ switch (eventName) {
2344
+ case 'add':
2345
+ {
2346
+ e.forEach((pen) => {
2347
+ pen.onAdd?.(pen);
2348
+ });
2349
+ }
2350
+ this.onSizeUpdate();
2351
+ break;
2352
+ case 'enter':
2353
+ e && e.onMouseEnter && e.onMouseEnter(e, this.canvas.mousePos);
2354
+ this.store.data.locked && this.doEvent(e, eventName);
2355
+ break;
2356
+ case 'leave':
2357
+ e && e.onMouseLeave && e.onMouseLeave(e, this.canvas.mousePos);
2358
+ this.store.data.locked && this.doEvent(e, eventName);
2359
+ break;
2360
+ case 'active':
2361
+ case 'inactive':
2362
+ {
2363
+ this.store.data.locked &&
2364
+ e.forEach((pen) => {
2365
+ this.doEvent(pen, eventName);
2366
+ });
2367
+ }
2368
+ break;
2369
+ case 'click':
2370
+ e.pen && e.pen.onClick && (!e.pen.disabled) && e.pen.onClick(e.pen, this.canvas.mousePos);
2371
+ this.store.data.locked && e.pen && (!e.pen.disabled) && this.doEvent(e.pen, eventName);
2372
+ break;
2373
+ case 'contextmenu':
2374
+ e.pen &&
2375
+ e.pen.onContextmenu && (!e.pen.disabled) &&
2376
+ e.pen.onContextmenu(e.pen, this.canvas.mousePos);
2377
+ this.store.data.locked && e.pen && (!e.pen.disabled) && this.doEvent(e.pen, eventName);
2378
+ break;
2379
+ case 'mousedown':
2380
+ e.pen &&
2381
+ e.pen.onMouseDown && (!e.pen.disabled) &&
2382
+ e.pen.onMouseDown(e.pen, this.canvas.mousePos);
2383
+ this.store.data.locked && e.pen && (!e.pen.disabled) && this.doEvent(e.pen, eventName);
2384
+ break;
2385
+ case 'mouseup':
2386
+ e.pen &&
2387
+ e.pen.onMouseUp && (!e.pen.disabled) &&
2388
+ e.pen.onMouseUp(e.pen, this.canvas.mousePos);
2389
+ this.store.data.locked && e.pen && (!e.pen.disabled) && this.doEvent(e.pen, eventName);
2390
+ break;
2391
+ case 'dblclick':
2392
+ this.store.data.locked && e.pen && (!e.pen.disabled) && this.doEvent(e.pen, eventName);
2393
+ break;
2394
+ case 'valueUpdate':
2395
+ this.store.data.locked && this.doEvent(e, eventName);
2396
+ this.canvas.tooltip.updateText(e);
2397
+ break;
2398
+ case 'update':
2399
+ case 'delete':
2400
+ case 'translatePens':
2401
+ case 'rotatePens':
2402
+ case 'resizePens':
2403
+ this.onSizeUpdate();
2404
+ break;
2405
+ case 'navigator':
2406
+ if (!this.store.data.id) {
2407
+ console.warn('请先保存当前图纸');
2408
+ }
2409
+ this.navigatorTo(e.params);
2410
+ break;
2411
+ case 'input':
2412
+ this.store.data.locked && e && (!e.disabled) && this.doEvent(e, eventName);
2413
+ break;
2414
+ case 'change':
2415
+ this.store.data.locked && e && (!e.disabled) && this.doEvent(e, eventName);
2416
+ break;
2417
+ }
2418
+ this.doMessageEvent(eventName);
2419
+ };
2420
+ doEvent = (pen, eventName) => {
2421
+ if (!pen) {
2422
+ return;
2423
+ }
2424
+ let old = false; //是否是旧的事件
2425
+ let indexArr = []; //事件条件成立的索引
2426
+ pen.events?.forEach((event, index) => {
2427
+ if (event.actions && event.actions.length) {
2428
+ if (event.name === eventName) {
2429
+ //条件成立
2430
+ let flag = false;
2431
+ if (event.conditions && event.conditions.length) {
2432
+ if (event.conditionType === 'and') {
2433
+ flag = event.conditions.every((condition) => {
2434
+ return this.judgeCondition(pen, condition.key, condition);
2435
+ });
2436
+ }
2437
+ else if (event.conditionType === 'or') {
2438
+ flag = event.conditions.some((condition) => {
2439
+ return this.judgeCondition(pen, condition.key, condition);
2440
+ });
2441
+ }
2442
+ }
2443
+ else {
2444
+ flag = true;
2445
+ }
2446
+ if (flag) {
2447
+ // event.actions.forEach((action) => {
2448
+ // if (this.events[action.action]) {
2449
+ // this.events[action.action](pen, action);
2450
+ // }
2451
+ // });
2452
+ indexArr.push(index);
2453
+ }
2454
+ }
2455
+ }
2456
+ else {
2457
+ old = true;
2458
+ if (this.events[event.action] && event.name === eventName) {
2459
+ let can = !event.where?.type;
2460
+ if (event.where) {
2461
+ const { fn, fnJs, comparison, key, value } = event.where;
2462
+ if (fn) {
2463
+ can = fn(pen, { meta2d: this });
2464
+ }
2465
+ else if (fnJs) {
2466
+ try {
2467
+ event.where.fn = new Function('pen', 'context', fnJs);
2468
+ }
2469
+ catch (err) {
2470
+ console.error('Error: make function:', err);
2471
+ }
2472
+ if (event.where.fn) {
2473
+ can = event.where.fn(pen, { meta2d: this });
2474
+ }
2475
+ }
2476
+ else {
2477
+ let pValue = pen[key];
2478
+ if (['x', 'y', 'width', 'height'].includes(key)) {
2479
+ pValue = this.getPenRect(pen)[key];
2480
+ }
2481
+ switch (comparison) {
2482
+ case '>':
2483
+ can = pValue > +value;
2484
+ break;
2485
+ case '>=':
2486
+ can = pValue >= +value;
2487
+ break;
2488
+ case '<':
2489
+ can = pValue < +value;
2490
+ break;
2491
+ case '<=':
2492
+ can = pValue <= +value;
2493
+ break;
2494
+ case '=':
2495
+ case '==':
2496
+ can = pValue == value;
2497
+ break;
2498
+ case '!=':
2499
+ can = pValue != value;
2500
+ break;
2501
+ case '[)':
2502
+ can = valueInRange(+pValue, value);
2503
+ break;
2504
+ case '![)':
2505
+ can = !valueInRange(+pValue, value);
2506
+ break;
2507
+ case '[]':
2508
+ can = valueInArray(pValue, value);
2509
+ break;
2510
+ case '![]':
2511
+ can = !valueInArray(pValue, value);
2512
+ break;
2513
+ }
2514
+ }
2515
+ }
2516
+ // can && this.events[event.action](pen, event);
2517
+ if (can) {
2518
+ indexArr.push(index);
2519
+ }
2520
+ }
2521
+ }
2522
+ });
2523
+ //所有的条件判断后,再统一执行条件成立的事件
2524
+ if (old) {
2525
+ pen.events?.forEach((event, index) => {
2526
+ if (indexArr.includes(index)) {
2527
+ this.events[event.action](pen, event);
2528
+ }
2529
+ });
2530
+ }
2531
+ else {
2532
+ pen.events?.forEach((event, index) => {
2533
+ if (indexArr.includes(index)) {
2534
+ event.actions.forEach((action) => {
2535
+ if (this.events[action.action]) {
2536
+ this.events[action.action](pen, action);
2537
+ }
2538
+ });
2539
+ }
2540
+ });
2541
+ }
2542
+ if (eventName === 'valueUpdate') {
2543
+ pen.realTimes?.forEach((realTime) => {
2544
+ let indexArr = [];
2545
+ realTime.triggers?.forEach((trigger, index) => {
2546
+ let flag = false;
2547
+ if (trigger.conditions?.length) {
2548
+ if (trigger.conditionType === 'and') {
2549
+ flag = trigger.conditions.every((condition) => {
2550
+ return this.judgeCondition(pen, realTime.key, condition);
2551
+ });
2552
+ }
2553
+ else if (trigger.conditionType === 'or') {
2554
+ flag = trigger.conditions.some((condition) => {
2555
+ return this.judgeCondition(pen, realTime.key, condition);
2556
+ });
2557
+ }
2558
+ }
2559
+ else {
2560
+ //无条件
2561
+ flag = true;
2562
+ }
2563
+ if (flag) {
2564
+ indexArr.push(index);
2565
+ // trigger.actions?.forEach((event) => {
2566
+ // this.events[event.action](pen, event);
2567
+ // });
2568
+ }
2569
+ });
2570
+ //执行
2571
+ realTime.triggers?.forEach((trigger, index) => {
2572
+ if (indexArr.includes(index)) {
2573
+ trigger.actions?.forEach((event) => {
2574
+ this.events[event.action](pen, event);
2575
+ });
2576
+ }
2577
+ });
2578
+ });
2579
+ //全局
2580
+ let indexArr = [];
2581
+ this.store.globalTriggers[pen.id]?.forEach((trigger, index) => {
2582
+ let flag = false;
2583
+ if (trigger.conditions?.length) {
2584
+ if (trigger.conditionType === 'and') {
2585
+ flag = trigger.conditions.every((condition) => {
2586
+ return this.judgeCondition(this.store.pens[condition.source], condition.key, condition);
2587
+ });
2588
+ }
2589
+ else if (trigger.conditionType === 'or') {
2590
+ flag = trigger.conditions.some((condition) => {
2591
+ return this.judgeCondition(this.store.pens[condition.source], condition.key, condition);
2592
+ });
2593
+ }
2594
+ }
2595
+ else {
2596
+ //无条件
2597
+ flag = true;
2598
+ }
2599
+ if (flag) {
2600
+ indexArr.push(index);
2601
+ }
2602
+ });
2603
+ this.store.globalTriggers[pen.id]?.forEach((trigger, index) => {
2604
+ if (indexArr.includes(index)) {
2605
+ trigger.actions?.forEach((event) => {
2606
+ this.events[event.action](pen, event);
2607
+ });
2608
+ }
2609
+ });
2610
+ //triggers
2611
+ if (pen.triggers?.length) {
2612
+ for (let trigger of pen.triggers) {
2613
+ if (trigger.status?.length) {
2614
+ for (let state of trigger.status) {
2615
+ let flag = false;
2616
+ if (state.conditions?.length) {
2617
+ if (state.conditionType === 'and') {
2618
+ flag = state.conditions.every((condition) => {
2619
+ return this.judgeCondition(pen, condition.key, condition);
2620
+ });
2621
+ }
2622
+ else if (state.conditionType === 'or') {
2623
+ flag = state.conditions.some((condition) => {
2624
+ return this.judgeCondition(pen, condition.key, condition);
2625
+ });
2626
+ }
2627
+ }
2628
+ else {
2629
+ //无条件
2630
+ flag = true;
2631
+ }
2632
+ if (flag) {
2633
+ state.actions?.forEach((event) => {
2634
+ this.events[event.action](pen, event);
2635
+ });
2636
+ break;
2637
+ }
2638
+ }
2639
+ }
2640
+ }
2641
+ }
2642
+ }
2643
+ // 事件冒泡,子执行完,父执行
2644
+ this.doEvent(this.store.pens[pen.parentId], eventName);
2645
+ };
2646
+ doMessageEvent(eventName) {
2647
+ if (this.store.messageEvents[eventName]) {
2648
+ this.store.messageEvents[eventName].forEach((item) => {
2649
+ let flag = false;
2650
+ if (item.event.conditions && item.event.conditions.length) {
2651
+ if (item.event.conditionType === 'and') {
2652
+ flag = item.event.conditions.every((condition) => {
2653
+ return this.judgeCondition(item.pen, condition.key, condition);
2654
+ });
2655
+ }
2656
+ else if (item.event.conditionType === 'or') {
2657
+ flag = item.event.conditions.some((condition) => {
2658
+ return this.judgeCondition(item.pen, condition.key, condition);
2659
+ });
2660
+ }
2661
+ }
2662
+ else {
2663
+ flag = true;
2664
+ }
2665
+ if (flag) {
2666
+ item.event.actions.forEach((action) => {
2667
+ this.events[action.action](item.pen, action);
2668
+ });
2669
+ }
2670
+ });
2671
+ }
2672
+ }
2673
+ doDataEvent = (datas) => {
2674
+ if (!(this.store.data.dataEvents?.length)) {
2675
+ return;
2676
+ }
2677
+ const data = datas.reduce((accumulator, { dataId, id, value }) => {
2678
+ accumulator[id || dataId] = value;
2679
+ return accumulator;
2680
+ }, {});
2681
+ let indexArr = [];
2682
+ this.store.data.dataEvents?.forEach((event, index) => {
2683
+ let flag = false;
2684
+ if (event.conditions && event.conditions.length) {
2685
+ if (event.conditionType === 'and') {
2686
+ flag = event.conditions.every((condition) => {
2687
+ return this.dataJudegeCondition(data, condition.key, condition);
2688
+ });
2689
+ }
2690
+ else if (event.conditionType === 'or') {
2691
+ flag = event.conditions.some((condition) => {
2692
+ return this.dataJudegeCondition(data, condition.key, condition);
2693
+ });
2694
+ }
2695
+ }
2696
+ else {
2697
+ flag = true;
2698
+ }
2699
+ if (flag) {
2700
+ indexArr.push(index);
2701
+ }
2702
+ });
2703
+ this.store.data.dataEvents?.forEach((event, index) => {
2704
+ if (indexArr.includes(index)) {
2705
+ event.actions?.forEach((action) => {
2706
+ this.events[action.action](data, action);
2707
+ });
2708
+ }
2709
+ });
2710
+ };
2711
+ initGlobalTriggers() {
2712
+ this.store.globalTriggers = {};
2713
+ this.store.data.triggers?.forEach((trigger) => {
2714
+ trigger.conditions.forEach((condition) => {
2715
+ if (condition.source) {
2716
+ if (!this.store.globalTriggers[condition.source]) {
2717
+ this.store.globalTriggers[condition.source] = [];
2718
+ }
2719
+ if (!this.store.globalTriggers[condition.source].includes(trigger)) {
2720
+ this.store.globalTriggers[condition.source].push(trigger);
2721
+ }
2722
+ }
2723
+ });
2724
+ });
2725
+ }
2726
+ initMessageEvents() {
2727
+ this.store.messageEvents = {};
2728
+ this.store.data.pens.forEach((pen) => {
2729
+ pen.events?.forEach((event) => {
2730
+ if (event.name === 'message' && event.message) {
2731
+ if (!this.store.messageEvents[event.message]) {
2732
+ this.store.messageEvents[event.message] = [];
2733
+ }
2734
+ this.store.messageEvents[event.message].push({
2735
+ pen: pen,
2736
+ event: event,
2737
+ });
2738
+ }
2739
+ });
2740
+ });
2741
+ }
2742
+ dataJudegeCondition(data, key, condition) {
2743
+ const { type, target, fnJs, fn, operator, valueType } = condition;
2744
+ let can = false;
2745
+ if (type === 'fn') {
2746
+ //方法
2747
+ if (fn) {
2748
+ can = fn(data, { meta2d: this });
2749
+ }
2750
+ else if (fnJs) {
2751
+ try {
2752
+ condition.fn = new Function('data', 'context', fnJs);
2753
+ }
2754
+ catch (err) {
2755
+ console.error('Error: make function:', err);
2756
+ }
2757
+ if (condition.fn) {
2758
+ can = condition.fn(data, { meta2d: this });
2759
+ }
2760
+ }
2761
+ }
2762
+ else {
2763
+ //TODO boolean类型 数字类型
2764
+ let value = condition.value;
2765
+ if (valueType === 'prop') {
2766
+ value = data[condition.value];
2767
+ }
2768
+ let compareValue = data[key];
2769
+ switch (operator) {
2770
+ case '>':
2771
+ can = compareValue > +value;
2772
+ break;
2773
+ case '>=':
2774
+ can = compareValue >= +value;
2775
+ break;
2776
+ case '<':
2777
+ can = compareValue < +value;
2778
+ break;
2779
+ case '<=':
2780
+ can = compareValue <= +value;
2781
+ break;
2782
+ case '=':
2783
+ case '==':
2784
+ can = compareValue == value;
2785
+ break;
2786
+ case '!=':
2787
+ can = compareValue != value;
2788
+ break;
2789
+ case '[)':
2790
+ can = valueInRange(+compareValue, value);
2791
+ break;
2792
+ case '![)':
2793
+ can = !valueInRange(+compareValue, value);
2794
+ break;
2795
+ case '[]':
2796
+ can = valueInArray(compareValue, value);
2797
+ break;
2798
+ case '![]':
2799
+ can = !valueInArray(compareValue, value);
2800
+ break;
2801
+ }
2802
+ }
2803
+ return can;
2804
+ }
2805
+ judgeCondition(pen, key, condition) {
2806
+ const { type, target, fnJs, fn, operator, valueType } = condition;
2807
+ let can = false;
2808
+ if (type === 'fn') {
2809
+ //方法
2810
+ if (fn) {
2811
+ can = fn(pen, { meta2d: this });
2812
+ }
2813
+ else if (fnJs) {
2814
+ try {
2815
+ condition.fn = new Function('pen', 'context', fnJs);
2816
+ }
2817
+ catch (err) {
2818
+ console.error('Error: make function:', err);
2819
+ }
2820
+ if (condition.fn) {
2821
+ can = condition.fn(pen, { meta2d: this });
2822
+ }
2823
+ }
2824
+ }
2825
+ else {
2826
+ //TODO boolean类型 数字类型
2827
+ let value = condition.value;
2828
+ if (valueType === 'prop') {
2829
+ value = this.store.pens[target][condition.value];
2830
+ }
2831
+ let compareValue = getter(pen, key);
2832
+ if (['x', 'y', 'width', 'height'].includes(key)) {
2833
+ compareValue = this.getPenRect(pen)[key];
2834
+ }
2835
+ switch (operator) {
2836
+ case '>':
2837
+ can = compareValue > +value;
2838
+ break;
2839
+ case '>=':
2840
+ can = compareValue >= +value;
2841
+ break;
2842
+ case '<':
2843
+ can = compareValue < +value;
2844
+ break;
2845
+ case '<=':
2846
+ can = compareValue <= +value;
2847
+ break;
2848
+ case '=':
2849
+ case '==':
2850
+ can = compareValue == value;
2851
+ break;
2852
+ case '!=':
2853
+ can = compareValue != value;
2854
+ break;
2855
+ case '[)':
2856
+ can = valueInRange(+compareValue, value);
2857
+ break;
2858
+ case '![)':
2859
+ can = !valueInRange(+compareValue, value);
2860
+ break;
2861
+ case '[]':
2862
+ can = valueInArray(compareValue, value);
2863
+ break;
2864
+ case '![]':
2865
+ can = !valueInArray(compareValue, value);
2866
+ break;
2867
+ }
2868
+ }
2869
+ return can;
2870
+ }
2871
+ pushChildren(parent, children) {
2872
+ const initUpdatePens = [deepClone(parent, true)];
2873
+ const addPens = [];
2874
+ if (!parent.children) {
2875
+ parent.children = [];
2876
+ }
2877
+ const updatePens = [];
2878
+ children.forEach((pen) => {
2879
+ let oldPen = deepClone(pen, true);
2880
+ if (!pen.id || !this.store.pens[pen.id]) {
2881
+ // 不存在于 store 中
2882
+ this.canvas.makePen(pen);
2883
+ oldPen = null; // 添加操作
2884
+ }
2885
+ if (pen.parentId) {
2886
+ const oldParent = this.store.pens[pen.parentId];
2887
+ const i = oldParent.children.findIndex((id) => id === pen.id);
2888
+ initUpdatePens.push(deepClone(oldParent, true));
2889
+ oldParent.children.splice(i, 1);
2890
+ updatePens.push(deepClone(oldParent, true));
2891
+ }
2892
+ parent.children.push(pen.id);
2893
+ pen.parentId = parent.id;
2894
+ const childRect = calcRelativeRect(pen.calculative.worldRect, parent.calculative.worldRect);
2895
+ Object.assign(pen, childRect);
2896
+ pen.locked = pen.lockedOnCombine ?? LockState.DisableMove;
2897
+ pen.locked = (pen.interaction || isInteraction.includes(pen.name)) ? 0 : pen.locked;
2898
+ if (!oldPen) {
2899
+ addPens.push(deepClone(pen, true));
2900
+ }
2901
+ else {
2902
+ initUpdatePens.push(oldPen);
2903
+ updatePens.push(deepClone(pen, true));
2904
+ }
2905
+ });
2906
+ updatePens.push(deepClone(parent, true));
2907
+ let step = 1;
2908
+ if (addPens.length) {
2909
+ step = 2;
2910
+ this.pushHistory({
2911
+ type: EditType.Add,
2912
+ pens: addPens,
2913
+ step,
2914
+ });
2915
+ }
2916
+ this.pushHistory({
2917
+ type: EditType.Update,
2918
+ initPens: initUpdatePens,
2919
+ pens: updatePens,
2920
+ step,
2921
+ });
2922
+ }
2923
+ renderPenRaw = renderPenRaw;
2924
+ toPng(padding, callback, containBkImg = false, maxWidth) {
2925
+ return this.canvas.toPng(padding, callback, containBkImg, maxWidth);
2926
+ }
2927
+ activeToPng(padding, maxWidth) {
2928
+ return this.canvas.activeToPng(padding, maxWidth);
2929
+ }
2930
+ pensToPng(pens = this.store.active, padding, maxWidth) {
2931
+ return this.canvas.pensToPng(pens, padding, maxWidth);
2932
+ }
2933
+ /**
2934
+ * 下载 png
2935
+ * @param name 传入参数自带文件后缀名 例如:'test.png'
2936
+ * @param padding 上右下左的内边距
2937
+ */
2938
+ downloadPng(name, padding, maxWidth) {
2939
+ for (const pen of this.store.data.pens) {
2940
+ if (pen.calculative.img || ['iframe'].includes(pen.name)) {
2941
+ //重新生成绘制图片
2942
+ pen.onRenderPenRaw?.(pen);
2943
+ }
2944
+ }
2945
+ setTimeout(() => {
2946
+ const a = document.createElement('a');
2947
+ a.setAttribute('download', (name || this.store.data.name || 'le5le.meta2d') + '.png');
2948
+ a.setAttribute('href', this.toPng(padding, undefined, true, maxWidth));
2949
+ const evt = document.createEvent('MouseEvents');
2950
+ evt.initEvent('click', true, true);
2951
+ a.dispatchEvent(evt);
2952
+ }, 1000);
2953
+ }
2954
+ downloadSvg() {
2955
+ if (!window.C2S) {
2956
+ console.error('请先加载乐吾乐官网下的canvas2svg.js', 'https://assets.le5lecdn.com/2d/canvas2svg.js');
2957
+ throw new Error('请先加载乐吾乐官网下的canvas2svg.js');
2958
+ }
2959
+ const rect = this.getRect();
2960
+ rect.x -= 10;
2961
+ rect.y -= 10;
2962
+ const ctx = new window.C2S(rect.width + 20, rect.height + 20);
2963
+ ctx.textBaseline = 'middle';
2964
+ for (const pen of this.store.data.pens) {
2965
+ if (pen.visible == false || !isShowChild(pen, this.store)) {
2966
+ continue;
2967
+ }
2968
+ renderPenRaw(ctx, pen, rect, true);
2969
+ }
2970
+ let mySerializedSVG = ctx.getSerializedSvg();
2971
+ if (this.store.data.background) {
2972
+ mySerializedSVG = mySerializedSVG.replace('{{bk}}', '');
2973
+ mySerializedSVG = mySerializedSVG.replace('{{bkRect}}', `<rect x="0" y="0" width="100%" height="100%" fill="${this.store.data.background}"></rect>`);
2974
+ }
2975
+ else {
2976
+ mySerializedSVG = mySerializedSVG.replace('{{bk}}', '');
2977
+ mySerializedSVG = mySerializedSVG.replace('{{bkRect}}', '');
2978
+ }
2979
+ mySerializedSVG = mySerializedSVG.replace(/--le5le--/g, '&#x');
2980
+ const urlObject = window.URL;
2981
+ const export_blob = new Blob([mySerializedSVG]);
2982
+ const url = urlObject.createObjectURL(export_blob);
2983
+ const a = document.createElement('a');
2984
+ a.setAttribute('download', `${this.store.data.name || 'le5le.meta2d'}.svg`);
2985
+ a.setAttribute('href', url);
2986
+ const evt = document.createEvent('MouseEvents');
2987
+ evt.initEvent('click', true, true);
2988
+ a.dispatchEvent(evt);
2989
+ }
2990
+ getRect(pens = this.store.data.pens) {
2991
+ return getRect(pens);
2992
+ }
2993
+ hiddenTemplate() {
2994
+ this.canvas.canvasTemplate.hidden();
2995
+ }
2996
+ showTemplate() {
2997
+ this.canvas.canvasTemplate.show();
2998
+ }
2999
+ lockTemplate(lock) {
3000
+ //锁定
3001
+ this.store.data.pens.forEach((pen) => {
3002
+ // if (pen.template) {
3003
+ // pen.locked = lock;
3004
+ // }
3005
+ if (pen.canvasLayer === CanvasLayer.CanvasTemplate) {
3006
+ pen.locked = lock;
3007
+ }
3008
+ });
3009
+ }
3010
+ /**
3011
+ * 放大到屏幕尺寸,并居中
3012
+ * @param fit true,填满但完整展示;false,填满,但长边可能截取(即显示不完整)
3013
+ */
3014
+ fitView(fit = true, viewPadding = 10) {
3015
+ // 默认垂直填充,两边留白
3016
+ if (!this.hasView())
3017
+ return;
3018
+ // 1. 重置画布尺寸为容器尺寸
3019
+ const { canvas } = this.canvas;
3020
+ const { offsetWidth: width, offsetHeight: height } = canvas;
3021
+ this.resize(width, height);
3022
+ // 2. 获取设置的留白值
3023
+ const padding = formatPadding(viewPadding);
3024
+ // 3. 获取图形尺寸
3025
+ const rect = this.getRect();
3026
+ // 4. 计算缩放比例
3027
+ const w = (width - padding[1] - padding[3]) / rect.width;
3028
+ const h = (height - padding[0] - padding[2]) / rect.height;
3029
+ let ratio = w;
3030
+ if (fit) {
3031
+ // 完整显示取小的
3032
+ ratio = w > h ? h : w;
3033
+ }
3034
+ else {
3035
+ ratio = w > h ? w : h;
3036
+ }
3037
+ if (this.store.data.fits?.length) {
3038
+ this.canvas.opening = true;
3039
+ }
3040
+ // 该方法直接更改画布的 scale 属性,所以比率应该乘以当前 scale
3041
+ this.scale(ratio * this.store.data.scale);
3042
+ // 5. 居中
3043
+ this.centerView();
3044
+ if (this.store.data.fits?.length) {
3045
+ this.fillView();
3046
+ }
3047
+ }
3048
+ fillView() {
3049
+ const rect = this.getRect();
3050
+ const wGap = this.canvas.width - rect.width;
3051
+ const hGap = this.canvas.height - rect.height;
3052
+ //宽度拉伸
3053
+ if (Math.abs(wGap) > 10) {
3054
+ this.store.data.fits?.forEach((fit) => {
3055
+ let pens = [];
3056
+ fit.children.forEach((id) => {
3057
+ this.store.pens[id].locked = LockState.None;
3058
+ pens.push(this.store.pens[id]);
3059
+ });
3060
+ let r = wGap / 2;
3061
+ if (fit.left && fit.right) {
3062
+ //整体拉伸
3063
+ let left = fit.leftValue;
3064
+ let right = fit.rightValue;
3065
+ if (left) {
3066
+ left = Math.abs(left) < 1 ? left * this.canvas.width : left;
3067
+ }
3068
+ else {
3069
+ left = 0;
3070
+ }
3071
+ if (right) {
3072
+ right = Math.abs(right) < 1 ? right * this.canvas.width : right;
3073
+ }
3074
+ else {
3075
+ right = 0;
3076
+ }
3077
+ let ratio = (this.canvas.width - left - right) / (rect.width - left - right);
3078
+ pens.forEach((pen) => {
3079
+ if (pen.image && pen.imageRatio) {
3080
+ if (pen.calculative.worldRect.width / this.canvas.width > 0.1) {
3081
+ pen.imageRatio = false;
3082
+ }
3083
+ }
3084
+ pen.calculative.worldRect.x = rect.x - wGap / 2 + left + (pen.calculative.worldRect.x - rect.x) * ratio; //(fit.leftValue || 0)+ (pen.calculative.worldRect.x + pen.calculative.worldRect.width/2)-( pen.calculative.worldRect.width*ratio)*(range/2- (fit.rightValue || 0))/(range- (fit.leftValue || 0)-(fit.rightValue || 0));
3085
+ pen.calculative.worldRect.width *= ratio;
3086
+ pen.calculative.worldRect.ex = pen.calculative.worldRect.x + pen.calculative.worldRect.width;
3087
+ pen.calculative.width = pen.calculative.worldRect.width;
3088
+ pen.calculative.x = pen.calculative.worldRect.x;
3089
+ pen.width = pen.calculative.worldRect.width;
3090
+ pen.x = pen.calculative.worldRect.x;
3091
+ this.canvas.updatePenRect(pen, { worldRectIsReady: false });
3092
+ if (pen.externElement) {
3093
+ pen.onResize?.(pen);
3094
+ }
3095
+ });
3096
+ }
3097
+ else if (fit.left) {
3098
+ //左移
3099
+ r = -r;
3100
+ if (fit.leftValue) {
3101
+ r += (Math.abs(fit.leftValue) < 1 ? fit.leftValue * this.canvas.width : fit.leftValue);
3102
+ }
3103
+ this.translatePens(pens, r, 0);
3104
+ }
3105
+ else if (fit.right) {
3106
+ //右移
3107
+ if (fit.rightValue) {
3108
+ r = r - (Math.abs(fit.rightValue) < 1 ? fit.rightValue * this.canvas.width : fit.rightValue);
3109
+ }
3110
+ this.translatePens(pens, r, 0);
3111
+ }
3112
+ });
3113
+ const iframePens = this.store.data.pens.filter((pen) => pen.name === 'iframe');
3114
+ iframePens?.forEach((pen) => {
3115
+ const worldRect = pen.calculative.worldRect;
3116
+ if (worldRect.width / this.store.data.scale > rect.width * 0.8) {
3117
+ let bfW = worldRect.width;
3118
+ pen.calculative.worldRect.x = worldRect.x - wGap / 2;
3119
+ pen.calculative.worldRect.width =
3120
+ worldRect.width + wGap;
3121
+ pen.calculative.worldRect.ex = worldRect.ex + wGap;
3122
+ pen.operationalRect.x =
3123
+ (pen.operationalRect.x * bfW) / pen.calculative.worldRect.width;
3124
+ pen.operationalRect.width =
3125
+ (pen.calculative.worldRect.width -
3126
+ (1 - pen.operationalRect.width) * bfW) /
3127
+ pen.calculative.worldRect.width;
3128
+ pen.onBeforeValue?.(pen, {
3129
+ operationalRect: pen.operationalRect,
3130
+ });
3131
+ pen.onResize?.(pen);
3132
+ }
3133
+ });
3134
+ }
3135
+ //高度拉伸
3136
+ if (Math.abs(hGap) > 10) {
3137
+ this.store.data.fits?.forEach((fit) => {
3138
+ let pens = [];
3139
+ fit.children.forEach((id) => {
3140
+ this.store.pens[id].locked = LockState.None;
3141
+ pens.push(this.store.pens[id]);
3142
+ });
3143
+ let r = hGap / 2;
3144
+ if (fit.top && fit.bottom) {
3145
+ let top = fit.topValue;
3146
+ let bottom = fit.bottomValue;
3147
+ if (top) {
3148
+ top = Math.abs(top) < 1 ? top * this.canvas.height : top;
3149
+ }
3150
+ else {
3151
+ top = 0;
3152
+ }
3153
+ if (bottom) {
3154
+ bottom = Math.abs(bottom) < 1 ? bottom * this.canvas.height : bottom;
3155
+ }
3156
+ else {
3157
+ bottom = 0;
3158
+ }
3159
+ let ratio = (this.canvas.height - top - bottom) / (rect.height - top - bottom);
3160
+ pens.forEach((pen) => {
3161
+ if (pen.image && pen.imageRatio) {
3162
+ if (pen.calculative.worldRect.height / this.canvas.height > 0.1) {
3163
+ pen.imageRatio = false;
3164
+ }
3165
+ }
3166
+ pen.calculative.worldRect.y = rect.y - hGap / 2 + top + (pen.calculative.worldRect.y - rect.y) * ratio; //(fit.leftValue || 0)+ (pen.calculative.worldRect.x + pen.calculative.worldRect.width/2)-( pen.calculative.worldRect.width*ratio)*(range/2- (fit.rightValue || 0))/(range- (fit.leftValue || 0)-(fit.rightValue || 0));
3167
+ pen.calculative.worldRect.height *= ratio;
3168
+ pen.calculative.worldRect.ey = pen.calculative.worldRect.y + pen.calculative.worldRect.height;
3169
+ pen.calculative.height = pen.calculative.worldRect.height;
3170
+ pen.calculative.y = pen.calculative.worldRect.y;
3171
+ pen.height = pen.calculative.worldRect.height;
3172
+ pen.y = pen.calculative.worldRect.y;
3173
+ this.canvas.updatePenRect(pen, { worldRectIsReady: false });
3174
+ if (pen.externElement) {
3175
+ pen.onResize?.(pen);
3176
+ }
3177
+ });
3178
+ }
3179
+ else if (fit.top) {
3180
+ r = -r;
3181
+ if (fit.topValue) {
3182
+ r += (Math.abs(fit.topValue) < 1 ? fit.topValue * this.canvas.height : fit.topValue);
3183
+ }
3184
+ this.translatePens(pens, 0, r);
3185
+ }
3186
+ else if (fit.bottom) {
3187
+ if (fit.bottomValue) {
3188
+ r = r - (Math.abs(fit.bottomValue) < 1 ? fit.bottomValue * this.canvas.height : fit.bottomValue);
3189
+ }
3190
+ this.translatePens(pens, 0, r);
3191
+ }
3192
+ });
3193
+ const iframePens = this.store.data.pens.filter((pen) => pen.name === 'iframe');
3194
+ iframePens?.forEach((pen) => {
3195
+ const worldRect = pen.calculative.worldRect;
3196
+ if (worldRect.height / this.store.data.scale > rect.height * 0.8) {
3197
+ let bfH = worldRect.height;
3198
+ pen.calculative.worldRect.y = worldRect.y - hGap / 2;
3199
+ pen.calculative.worldRect.height =
3200
+ worldRect.height + hGap;
3201
+ pen.calculative.worldRect.ey = worldRect.ey + hGap;
3202
+ pen.operationalRect.y =
3203
+ (pen.operationalRect.y * bfH) / pen.calculative.worldRect.width;
3204
+ pen.operationalRect.height =
3205
+ (pen.calculative.worldRect.height -
3206
+ (1 - pen.operationalRect.height) * bfH) /
3207
+ pen.calculative.worldRect.height;
3208
+ pen.onBeforeValue?.(pen, {
3209
+ operationalRect: pen.operationalRect,
3210
+ });
3211
+ pen.onResize?.(pen);
3212
+ }
3213
+ });
3214
+ }
3215
+ this.canvas.canvasTemplate.init();
3216
+ this.canvas.canvasImage.init();
3217
+ this.canvas.canvasImageBottom.init();
3218
+ this.render(true);
3219
+ }
3220
+ trimPens() {
3221
+ //去除空连线
3222
+ let pens = this.store.data.pens.filter((pen) => pen.name === 'line' && pen.anchors.length < 2);
3223
+ this.delete(pens);
3224
+ }
3225
+ /**
3226
+ * 放大到屏幕尺寸,并居中
3227
+ * @param fit true,填满但完整展示;false,填满,但长边可能截取(即显示不完整)
3228
+ */
3229
+ fitTemplateView(fit = true, viewPadding = 10) {
3230
+ //  默认垂直填充,两边留白
3231
+ if (!this.hasView())
3232
+ return;
3233
+ // 1. 重置画布尺寸为容器尺寸
3234
+ const { canvas } = this.canvas;
3235
+ const { offsetWidth: width, offsetHeight: height } = canvas;
3236
+ // 2. 获取设置的留白值
3237
+ const padding = formatPadding(viewPadding);
3238
+ // 3. 获取图形尺寸
3239
+ const rect = this.getRect();
3240
+ // 4. 计算缩放比例
3241
+ const w = (width - padding[1] - padding[3]) / rect.width;
3242
+ const h = (height - padding[0] - padding[2]) / rect.height;
3243
+ let ratio = w;
3244
+ if (fit) {
3245
+ // 完整显示取小的
3246
+ ratio = w > h ? h : w;
3247
+ }
3248
+ else {
3249
+ ratio = w > h ? w : h;
3250
+ }
3251
+ // 该方法直接更改画布的 scale 属性,所以比率应该乘以当前 scale
3252
+ this.canvas.templateScale(ratio * this.store.data.scale);
3253
+ let _rect = this.getRect();
3254
+ let pens = this.store.data.pens.filter((pen) => !pen.parentId);
3255
+ this.canvas.templateTranslatePens(pens, -_rect.x, -_rect.y);
3256
+ // 5. 居中
3257
+ this.store.data.pens.forEach((pen) => {
3258
+ if (!pen.type) {
3259
+ this.canvas.updateLines(pen);
3260
+ }
3261
+ else {
3262
+ this.canvas.initLineRect(pen);
3263
+ }
3264
+ });
3265
+ this.centerView();
3266
+ }
3267
+ fitSizeView(fit = true, viewPadding = 10) {
3268
+ // 默认垂直填充,两边留白
3269
+ // if (!this.hasView()) return;
3270
+ // 1. 重置画布尺寸为容器尺寸
3271
+ const { canvas } = this.canvas;
3272
+ const { offsetWidth: width, offsetHeight: height } = canvas;
3273
+ this.resize(width, height);
3274
+ // 2. 获取设置的留白值
3275
+ const padding = formatPadding(viewPadding);
3276
+ const _width = (this.store.data.width || this.store.options.width) *
3277
+ this.store.data.scale;
3278
+ const _height = (this.store.data.height || this.store.options.height) *
3279
+ this.store.data.scale;
3280
+ // 4. 计算缩放比例
3281
+ const w = (width - padding[1] - padding[3]) / _width;
3282
+ const h = (height - padding[0] - padding[2]) / _height;
3283
+ let ratio = w;
3284
+ if (fit === 'width') {
3285
+ ratio = w;
3286
+ }
3287
+ else if (fit === 'height') {
3288
+ ratio = h;
3289
+ }
3290
+ else {
3291
+ if (fit) {
3292
+ // 完整显示取小的
3293
+ ratio = w > h ? h : w;
3294
+ }
3295
+ else {
3296
+ ratio = w > h ? w : h;
3297
+ }
3298
+ }
3299
+ if (this.store.data.fits?.length) {
3300
+ this.canvas.opening = true;
3301
+ }
3302
+ // 该方法直接更改画布的 scale 属性,所以比率应该乘以当前 scale
3303
+ this.scale(ratio * this.store.data.scale);
3304
+ // 5. 居中
3305
+ this.centerSizeView();
3306
+ if (this.store.data.fits?.length) {
3307
+ this.fillView();
3308
+ }
3309
+ }
3310
+ centerSizeView() {
3311
+ // if (!this.hasView()) return;
3312
+ const viewCenter = this.getViewCenter();
3313
+ //根据画布尺寸居中对齐
3314
+ const _width = this.store.data.width || this.store.options.width;
3315
+ const _height = this.store.data.height || this.store.options.height;
3316
+ const pensRect = {
3317
+ x: 0,
3318
+ y: 0,
3319
+ width: _width,
3320
+ height: _height,
3321
+ };
3322
+ calcCenter(pensRect);
3323
+ const { center } = pensRect;
3324
+ const { scale, origin, x: dataX, y: dataY } = this.store.data;
3325
+ this.translate((viewCenter.x - origin.x) / scale - center.x - dataX / scale, (viewCenter.y - origin.y) / scale - center.y - dataY / scale);
3326
+ const { canvas } = this.canvas;
3327
+ const x = (canvas.scrollWidth - canvas.offsetWidth) / 2;
3328
+ const y = (canvas.scrollHeight - canvas.offsetHeight) / 2;
3329
+ canvas.scrollTo(x, y);
3330
+ }
3331
+ /**
3332
+ * 宽度放大到屏幕尺寸,并滚动到最顶部
3333
+ *
3334
+ */
3335
+ scrollView(viewPadding = 10, pageMode = false) {
3336
+ if (!this.hasView())
3337
+ return;
3338
+ //滚动状态下
3339
+ if (!this.canvas.scroll) {
3340
+ return;
3341
+ }
3342
+ const { canvas } = this.canvas;
3343
+ const { offsetWidth: width, offsetHeight: height } = canvas;
3344
+ this.resize(width, height);
3345
+ const padding = formatPadding(viewPadding);
3346
+ const rect = this.getRect();
3347
+ const ratio = (width - padding[1] - padding[3]) / rect.width;
3348
+ this.scale(ratio * this.store.data.scale);
3349
+ this.topView(padding[0]);
3350
+ if (pageMode) {
3351
+ this.canvas.scroll.changeMode();
3352
+ }
3353
+ }
3354
+ screenView(viewPadding = 10, WorH = true) {
3355
+ if (!this.hasView())
3356
+ return;
3357
+ const { canvas } = this.canvas;
3358
+ const { offsetWidth: width, offsetHeight: height } = canvas;
3359
+ this.resize(width, height);
3360
+ const padding = formatPadding(viewPadding);
3361
+ const rect = this.getRect();
3362
+ //默认宽度充满
3363
+ let ratio = (width - padding[1] - padding[3]) / rect.width;
3364
+ if (!WorH) {
3365
+ ratio = (height - padding[0] - padding[2]) / rect.height;
3366
+ }
3367
+ this.scale(ratio * this.store.data.scale);
3368
+ //height充满时是居中
3369
+ this.topView(padding[0]);
3370
+ }
3371
+ topView(paddingTop = 10) {
3372
+ if (!this.hasView())
3373
+ return;
3374
+ const rect = this.getRect();
3375
+ const viewCenter = this.getViewCenter();
3376
+ const pensRect = this.getPenRect(rect);
3377
+ calcCenter(pensRect);
3378
+ const { center } = pensRect;
3379
+ const { scale, origin, x: dataX, y: dataY } = this.store.data;
3380
+ this.translate((viewCenter.x - origin.x) / scale - center.x - dataX / scale, (paddingTop - origin.y) / scale - pensRect.y - dataY / scale);
3381
+ const { canvas } = this.canvas;
3382
+ const x = (canvas.scrollWidth - canvas.offsetWidth) / 2;
3383
+ const y = (canvas.scrollHeight - canvas.offsetHeight) / 2;
3384
+ canvas.scrollTo(x, y);
3385
+ }
3386
+ centerView() {
3387
+ if (!this.hasView())
3388
+ return;
3389
+ const rect = this.getRect();
3390
+ const viewCenter = this.getViewCenter();
3391
+ const pensRect = this.getPenRect(rect);
3392
+ calcCenter(pensRect);
3393
+ const { center } = pensRect;
3394
+ const { scale, origin, x: dataX, y: dataY } = this.store.data;
3395
+ // center 的值,在缩放和拖拽画布过程中不发生变化,是相对值
3396
+ // viewCenter 是一个绝对值,需要根据 origin 的值,来计算出相对的值
3397
+ // store.data.x 是画布偏移值,在 translate 方法中与 scale 相关,这里也需要计算
3398
+ this.translate((viewCenter.x - origin.x) / scale - center.x - dataX / scale, (viewCenter.y - origin.y) / scale - center.y - dataY / scale);
3399
+ const { canvas } = this.canvas;
3400
+ const x = (canvas.scrollWidth - canvas.offsetWidth) / 2;
3401
+ const y = (canvas.scrollHeight - canvas.offsetHeight) / 2;
3402
+ canvas.scrollTo(x, y);
3403
+ }
3404
+ /**
3405
+ * 画布是否有 画笔
3406
+ * RuleLine 不算
3407
+ */
3408
+ hasView() {
3409
+ return !!this.store.data.pens.filter((pen) => !pen.isRuleLine).length;
3410
+ }
3411
+ getViewCenter() {
3412
+ const { width, height } = this.canvas;
3413
+ return {
3414
+ x: width / 2,
3415
+ y: height / 2,
3416
+ };
3417
+ }
3418
+ /**
3419
+ * 大小相同
3420
+ * @param pens 画笔们
3421
+ */
3422
+ beSameByFirst(pens = this.store.data.pens, attribute) {
3423
+ const initPens = deepClone(pens); // 原 pens ,深拷贝一下
3424
+ // 1. 得到第一个画笔的 宽高
3425
+ const firstPen = pens[0];
3426
+ const { width, height } = this.getPenRect(firstPen);
3427
+ for (let i = 1; i < pens.length; i++) {
3428
+ const pen = pens[i];
3429
+ if (attribute === 'width') {
3430
+ this.setValue({ id: pen.id, width }, { render: false, doEvent: false });
3431
+ }
3432
+ else if (attribute === 'height') {
3433
+ this.setValue({ id: pen.id, height }, { render: false, doEvent: false });
3434
+ }
3435
+ else {
3436
+ this.setValue({ id: pen.id, width, height }, { render: false, doEvent: false });
3437
+ }
3438
+ }
3439
+ this.render();
3440
+ this.pushHistory({
3441
+ type: EditType.Update,
3442
+ initPens,
3443
+ pens,
3444
+ });
3445
+ }
3446
+ /**
3447
+ * 大小相同
3448
+ * @param pens 画笔们
3449
+ */
3450
+ beSameByLast(pens = this.store.data.pens, attribute) {
3451
+ const initPens = deepClone(pens); // 原 pens ,深拷贝一下
3452
+ // 1. 得到最后一个画笔的 宽高
3453
+ const lastPen = pens[pens.length - 1];
3454
+ const { width, height } = this.getPenRect(lastPen);
3455
+ for (let i = 0; i < pens.length - 1; i++) {
3456
+ const pen = pens[i];
3457
+ if (attribute === 'width') {
3458
+ this.setValue({ id: pen.id, width }, { render: false, doEvent: false });
3459
+ }
3460
+ else if (attribute === 'height') {
3461
+ this.setValue({ id: pen.id, height }, { render: false, doEvent: false });
3462
+ }
3463
+ else {
3464
+ this.setValue({ id: pen.id, width, height }, { render: false, doEvent: false });
3465
+ }
3466
+ }
3467
+ this.render();
3468
+ this.pushHistory({
3469
+ type: EditType.Update,
3470
+ initPens,
3471
+ pens,
3472
+ });
3473
+ }
3474
+ /**
3475
+ * 格式刷(样式相同,大小无需一致。)
3476
+ * @param pens 画笔们
3477
+ */
3478
+ formatPainterByFirst(pens = this.store.data.pens) {
3479
+ const initPens = deepClone(pens); // 原 pens ,深拷贝一下
3480
+ const firstPen = pens[0];
3481
+ // 格式刷修改的属性,除开宽高
3482
+ const attrs = {};
3483
+ formatAttrs.forEach((attr) => {
3484
+ attrs[attr] = firstPen[attr];
3485
+ });
3486
+ for (let i = 1; i < pens.length; i++) {
3487
+ const pen = pens[i];
3488
+ this.setValue({ id: pen.id, ...attrs }, { render: false, doEvent: false });
3489
+ }
3490
+ this.render();
3491
+ this.pushHistory({
3492
+ type: EditType.Update,
3493
+ initPens,
3494
+ pens,
3495
+ });
3496
+ }
3497
+ /**
3498
+ * 格式刷(样式相同,大小无需一致。)
3499
+ * @param pens 画笔们
3500
+ */
3501
+ formatPainterByLast(pens = this.store.data.pens) {
3502
+ const initPens = deepClone(pens); // 原 pens ,深拷贝一下
3503
+ const firstPen = pens[pens.length - 1];
3504
+ // 格式刷修改的属性,除开宽高
3505
+ const attrs = {};
3506
+ formatAttrs.forEach((attr) => {
3507
+ attrs[attr] = firstPen[attr];
3508
+ });
3509
+ for (let i = 0; i < pens.length - 1; i++) {
3510
+ const pen = pens[i];
3511
+ this.setValue({ id: pen.id, ...attrs }, { render: false, doEvent: false });
3512
+ }
3513
+ this.render();
3514
+ this.pushHistory({
3515
+ type: EditType.Update,
3516
+ initPens,
3517
+ pens,
3518
+ });
3519
+ }
3520
+ setFormatPainter() {
3521
+ const pens = this.store.active;
3522
+ const attrs = {};
3523
+ if (pens.length > 0) {
3524
+ const firstPen = pens[0];
3525
+ formatAttrs.forEach((attr) => {
3526
+ attrs[attr] =
3527
+ firstPen[attr] !== undefined ? firstPen[attr] :
3528
+ (this.store.options.defaultFormat[attr] ||
3529
+ this.store.options[attr]);
3530
+ });
3531
+ }
3532
+ else {
3533
+ //默认值
3534
+ const attrs = {};
3535
+ formatAttrs.forEach((attr) => {
3536
+ attrs[attr] =
3537
+ this.store.options.defaultFormat[attr] ||
3538
+ this.store.options[attr] ||
3539
+ undefined;
3540
+ });
3541
+ }
3542
+ localStorage.setItem('meta2d-formatPainter', JSON.stringify(attrs));
3543
+ }
3544
+ formatPainter() {
3545
+ const pens = this.store.active;
3546
+ const initPens = deepClone(pens);
3547
+ const attrs = JSON.parse(localStorage.getItem('meta2d-formatPainter'));
3548
+ for (let i = 0; i < pens.length; i++) {
3549
+ const pen = pens[i];
3550
+ this.setValue({ id: pen.id, ...attrs }, { render: false, doEvent: false });
3551
+ }
3552
+ this.render();
3553
+ this.pushHistory({
3554
+ type: EditType.Update,
3555
+ initPens,
3556
+ pens,
3557
+ });
3558
+ }
3559
+ clearFormatPainter() {
3560
+ const pens = this.store.active;
3561
+ const initPens = deepClone(pens);
3562
+ formatAttrs.forEach((attr) => {
3563
+ for (let i = 0; i < pens.length; i++) {
3564
+ const pen = pens[i];
3565
+ const { fontSize, lineHeight } = this.store.options;
3566
+ if (attr === 'lineWidth') {
3567
+ pen.lineWidth = 1;
3568
+ pen.calculative.lineWidth = 1;
3569
+ }
3570
+ else if (attr === 'fontSize') {
3571
+ pen.fontSize = fontSize;
3572
+ pen.calculative.fontSize = fontSize;
3573
+ }
3574
+ else if (attr === 'lineHeight') {
3575
+ pen.lineHeight = lineHeight;
3576
+ pen.calculative.lineHeight = lineHeight;
3577
+ }
3578
+ else {
3579
+ delete pen[attr];
3580
+ delete pen.calculative[attr];
3581
+ }
3582
+ }
3583
+ });
3584
+ this.render();
3585
+ this.pushHistory({
3586
+ type: EditType.Update,
3587
+ initPens,
3588
+ pens,
3589
+ });
3590
+ }
3591
+ alignNodes(align, pens = this.store.data.pens, rect) {
3592
+ !rect && (rect = this.getPenRect(this.getRect(pens)));
3593
+ const initPens = deepClone(pens); // 原 pens ,深拷贝一下
3594
+ for (const item of pens) {
3595
+ this.alignPen(align, item, rect);
3596
+ }
3597
+ this.initImageCanvas(pens);
3598
+ this.initTemplateCanvas(pens);
3599
+ this.render();
3600
+ this.pushHistory({
3601
+ type: EditType.Update,
3602
+ initPens,
3603
+ pens,
3604
+ });
3605
+ }
3606
+ //对齐大屏
3607
+ alignNodesV(align, pens = this.store.data.pens) {
3608
+ const width = this.store.data.width || this.store.options.width;
3609
+ const height = this.store.data.height || this.store.options.height;
3610
+ let rect = {
3611
+ x: 0,
3612
+ y: 0,
3613
+ width,
3614
+ height,
3615
+ };
3616
+ const initPens = deepClone(pens); // 原 pens ,深拷贝一下
3617
+ for (const item of pens) {
3618
+ this.alignPen(align, item, rect);
3619
+ }
3620
+ this.initImageCanvas(pens);
3621
+ this.initTemplateCanvas(pens);
3622
+ this.render();
3623
+ this.pushHistory({
3624
+ type: EditType.Update,
3625
+ initPens,
3626
+ pens,
3627
+ });
3628
+ }
3629
+ /**
3630
+ * 对齐画笔,基于第一个画笔
3631
+ * @param align 左对齐,右对齐,上对齐,下对齐,居中对齐
3632
+ * @param pens
3633
+ */
3634
+ alignNodesByFirst(align, pens = this.store.data.pens) {
3635
+ const initPens = deepClone(pens); // 原 pens ,深拷贝一下
3636
+ const firstPen = pens[0];
3637
+ const rect = this.getPenRect(firstPen);
3638
+ for (let i = 1; i < pens.length; i++) {
3639
+ const pen = pens[i];
3640
+ this.alignPen(align, pen, rect);
3641
+ }
3642
+ this.initImageCanvas(pens);
3643
+ this.initTemplateCanvas(pens);
3644
+ this.render();
3645
+ this.pushHistory({
3646
+ type: EditType.Update,
3647
+ initPens,
3648
+ pens,
3649
+ });
3650
+ }
3651
+ /**
3652
+ * 对齐画笔,基于最后选中的画笔
3653
+ * @param align 左对齐,右对齐,上对齐,下对齐,居中对齐
3654
+ * @param pens
3655
+ */
3656
+ alignNodesByLast(align, pens = this.store.data.pens) {
3657
+ const initPens = deepClone(pens); // 原 pens ,深拷贝一下
3658
+ const lastPen = pens[pens.length - 1];
3659
+ const rect = this.getPenRect(lastPen);
3660
+ for (let i = 0; i < pens.length - 1; i++) {
3661
+ const pen = pens[i];
3662
+ this.alignPen(align, pen, rect);
3663
+ }
3664
+ this.initImageCanvas(pens);
3665
+ this.initTemplateCanvas(pens);
3666
+ this.render();
3667
+ this.pushHistory({
3668
+ type: EditType.Update,
3669
+ initPens,
3670
+ pens,
3671
+ });
3672
+ }
3673
+ /**
3674
+ * 将画笔参照 rect 进行 align 对齐
3675
+ * @param align 左对齐,右对齐,上对齐,下对齐,居中对齐
3676
+ * @param pen 当前需要对齐的画笔
3677
+ * @param rect 参照矩形
3678
+ * @returns
3679
+ */
3680
+ alignPen(align, pen, rect) {
3681
+ const penRect = this.getPenRect(pen);
3682
+ switch (align) {
3683
+ case 'left':
3684
+ penRect.x = rect.x;
3685
+ break;
3686
+ case 'right':
3687
+ penRect.x = rect.x + rect.width - penRect.width;
3688
+ break;
3689
+ case 'top':
3690
+ penRect.y = rect.y;
3691
+ break;
3692
+ case 'bottom':
3693
+ penRect.y = rect.y + rect.height - penRect.height;
3694
+ break;
3695
+ case 'center':
3696
+ penRect.x = rect.x + rect.width / 2 - penRect.width / 2;
3697
+ break;
3698
+ case 'middle':
3699
+ penRect.y = rect.y + rect.height / 2 - penRect.height / 2;
3700
+ break;
3701
+ }
3702
+ this.setValue({ id: pen.id, ...penRect }, { render: false, doEvent: false });
3703
+ }
3704
+ /**
3705
+ * 水平或垂直方向的均分
3706
+ * @param direction 方向,width 说明水平方向间距相同
3707
+ * @param pens 节点们,默认全部的
3708
+ * @param distance 总的宽 or 高
3709
+ */
3710
+ spaceBetweenByDirection(direction, pens = this.store.data.pens, distance) {
3711
+ //TODO 暂时修复,待优化
3712
+ // !distance && (distance = this.getPenRect(this.getRect(pens))[direction]);
3713
+ if (!distance) {
3714
+ let start = Infinity, end = -Infinity, key = direction === 'width' ? 'x' : 'y';
3715
+ pens.forEach((item) => {
3716
+ start = Math.min(start, item.calculative.worldRect[key]);
3717
+ end = Math.max(end, item.calculative.worldRect['e' + key]);
3718
+ });
3719
+ distance = (end - start) / this.store.data.scale;
3720
+ }
3721
+ // 过滤出非父节点
3722
+ pens = pens.filter((item) => !item.parentId);
3723
+ if (pens.length <= 2) {
3724
+ return;
3725
+ }
3726
+ const initPens = deepClone(pens); // 原 pens ,深拷贝一下
3727
+ // 计算间距
3728
+ const allDistance = pens.reduce((distance, currentPen) => {
3729
+ const currentPenRect = this.getPenRect(currentPen);
3730
+ return distance + currentPenRect[direction];
3731
+ }, 0);
3732
+ const space = (distance - allDistance) / (pens.length - 1);
3733
+ // 按照大小顺序排列画笔
3734
+ pens = pens.sort((a, b) => {
3735
+ if (direction === 'width') {
3736
+ return a.x - b.x;
3737
+ }
3738
+ return a.y - b.y;
3739
+ });
3740
+ const pen0Rect = this.getPenRect(pens[0]);
3741
+ let left = direction === 'width' ? pen0Rect.x : pen0Rect.y;
3742
+ for (const pen of pens) {
3743
+ const penRect = this.getPenRect(pen);
3744
+ direction === 'width' ? (penRect.x = left) : (penRect.y = left);
3745
+ left += penRect[direction] + space;
3746
+ this.setValue({ id: pen.id, ...penRect }, { render: false, doEvent: false });
3747
+ }
3748
+ this.initImageCanvas(pens);
3749
+ this.initTemplateCanvas(pens);
3750
+ this.render();
3751
+ this.pushHistory({
3752
+ type: EditType.Update,
3753
+ initPens,
3754
+ pens,
3755
+ });
3756
+ }
3757
+ spaceBetween(pens, width) {
3758
+ this.spaceBetweenByDirection('width', pens, width);
3759
+ }
3760
+ spaceBetweenColumn(pens, height) {
3761
+ this.spaceBetweenByDirection('height', pens, height);
3762
+ }
3763
+ layout(pens = this.store.data.pens, width, space = 30) {
3764
+ const rect = this.getPenRect(getRect(pens));
3765
+ !width && (width = rect.width);
3766
+ // 1. 拿到全部节点中最大的高
3767
+ pens = pens.filter((item) => !item.type && !item.parentId);
3768
+ const initPens = deepClone(pens); // 原 pens ,深拷贝一下
3769
+ let maxHeight = 0;
3770
+ pens.forEach((pen) => {
3771
+ const penRect = this.getPenRect(pen);
3772
+ penRect.height > maxHeight && (maxHeight = penRect.height);
3773
+ });
3774
+ // 2. 遍历节点调整位置
3775
+ let currentX = rect.x;
3776
+ let currentY = rect.y;
3777
+ pens.forEach((pen, index) => {
3778
+ const penRect = this.getPenRect(pen);
3779
+ penRect.x = currentX;
3780
+ penRect.y = currentY + maxHeight / 2 - penRect.height / 2;
3781
+ this.setValue({ id: pen.id, ...penRect }, { render: false, doEvent: false });
3782
+ if (index === pens.length - 1) {
3783
+ return;
3784
+ }
3785
+ const currentWidth = currentX + penRect.width - rect.x;
3786
+ const nextPenRect = this.getPenRect(pens[index + 1]);
3787
+ if (Math.round(width - currentWidth) >=
3788
+ Math.round(nextPenRect.width + space))
3789
+ // 当前行
3790
+ currentX += penRect.width + space;
3791
+ else {
3792
+ // 换行
3793
+ currentX = rect.x;
3794
+ currentY += maxHeight + space;
3795
+ }
3796
+ });
3797
+ this.initImageCanvas(pens);
3798
+ this.initTemplateCanvas(pens);
3799
+ this.render();
3800
+ this.pushHistory({
3801
+ type: EditType.Update,
3802
+ initPens,
3803
+ pens,
3804
+ });
3805
+ }
3806
+ gotoView(pen) {
3807
+ const center = this.getViewCenter();
3808
+ const x = center.x -
3809
+ pen.calculative.worldRect.x -
3810
+ pen.calculative.worldRect.width / 2;
3811
+ const y = center.y -
3812
+ pen.calculative.worldRect.y -
3813
+ pen.calculative.worldRect.height / 2;
3814
+ if (this.canvas.scroll && this.canvas.scroll.isShow) {
3815
+ this.canvas.scroll.translate(x - this.store.data.x, y - this.store.data.y);
3816
+ }
3817
+ this.store.data.x = x;
3818
+ this.store.data.y = y;
3819
+ for (const pen of this.store.data.pens) {
3820
+ calcInView(pen);
3821
+ }
3822
+ this.canvas.canvasImage.init();
3823
+ this.canvas.canvasImageBottom.init();
3824
+ this.render();
3825
+ }
3826
+ showMap() {
3827
+ if (!this.map) {
3828
+ this.map = new ViewMap(this.canvas);
3829
+ }
3830
+ this.map.show();
3831
+ }
3832
+ hideMap() {
3833
+ this.map.hide();
3834
+ }
3835
+ onSizeUpdate() {
3836
+ if (this.mapTimer) {
3837
+ clearTimeout(this.mapTimer);
3838
+ this.mapTimer = undefined;
3839
+ }
3840
+ this.mapTimer = setTimeout(() => {
3841
+ if (this.map && this.map.isShow) {
3842
+ this.map.show();
3843
+ }
3844
+ if (this.canvas.scroll && this.canvas.scroll.isShow) {
3845
+ this.canvas.scroll.resize();
3846
+ }
3847
+ }, 500);
3848
+ }
3849
+ toggleAnchorMode() {
3850
+ this.canvas.toggleAnchorMode();
3851
+ }
3852
+ addAnchorHand() {
3853
+ this.canvas.addAnchorHand();
3854
+ }
3855
+ removeAnchorHand() {
3856
+ this.canvas.removeAnchorHand();
3857
+ }
3858
+ toggleAnchorHand() {
3859
+ this.canvas.toggleAnchorHand();
3860
+ }
3861
+ /**
3862
+ * 将该画笔置顶,即放到数组最后,最后绘制即在顶部
3863
+ * @param pens pen 置顶的画笔
3864
+ */
3865
+ top(pens) {
3866
+ if (!pens)
3867
+ pens = this.store.active;
3868
+ if (!Array.isArray(pens))
3869
+ pens = [pens]; // 兼容
3870
+ for (const pen of pens) {
3871
+ const _pens = this.store.data.pens;
3872
+ // 获取它包含它的子节点
3873
+ const allIds = [...getAllChildren(pen, this.store), pen].map((p) => p.id);
3874
+ const allPens = _pens.filter((p) => allIds.includes(p.id));
3875
+ allPens.forEach((pen) => {
3876
+ const index = _pens.findIndex((p) => p.id === pen.id);
3877
+ if (index > -1) {
3878
+ _pens.push(_pens[index]);
3879
+ _pens.splice(index, 1);
3880
+ this.initTemplateCanvas([pen]);
3881
+ this.initImageCanvas([pen]);
3882
+ }
3883
+ this.specificLayerMove(pen, 'top');
3884
+ });
3885
+ }
3886
+ this.store.emitter.emit('layer', { type: 'top', pens });
3887
+ }
3888
+ /**
3889
+ * 若本次改变的画笔存在图片,并且在上层 or 下层,需要擦除上层 or 下层
3890
+ * 子节点中包含图片,也需要重绘
3891
+ * @param pens 本次改变的 pens
3892
+ */
3893
+ initImageCanvas(pens) {
3894
+ this.canvas.initImageCanvas(pens);
3895
+ }
3896
+ /**
3897
+ * 模版图元图层改变
3898
+ * @param pens 本次改变的 pens
3899
+ */
3900
+ initTemplateCanvas(pens) {
3901
+ this.canvas.initTemplateCanvas(pens);
3902
+ }
3903
+ /**
3904
+ * 该画笔置底,即放到数组最前,最后绘制即在底部
3905
+ * @param pens 画笔们,注意 pen 必须在该数组内才有效
3906
+ */
3907
+ bottom(pens) {
3908
+ if (!pens)
3909
+ pens = this.store.active;
3910
+ if (!Array.isArray(pens))
3911
+ pens = [pens]; // 兼容
3912
+ for (const pen of pens) {
3913
+ const _pens = this.store.data.pens;
3914
+ const allIds = [...getAllChildren(pen, this.store), pen].map((p) => p.id);
3915
+ const allPens = _pens.filter((p) => allIds.includes(p.id));
3916
+ // 从后往前,保证 allPens 顺序不变
3917
+ for (let i = allPens.length - 1; i >= 0; i--) {
3918
+ const pen = allPens[i];
3919
+ const index = _pens.findIndex((p) => p.id === pen.id);
3920
+ if (index > -1) {
3921
+ _pens.unshift(_pens[index]);
3922
+ _pens.splice(index + 1, 1);
3923
+ this.initTemplateCanvas([pen]);
3924
+ this.initImageCanvas([pen]);
3925
+ }
3926
+ this.specificLayerMove(pen, 'bottom');
3927
+ }
3928
+ }
3929
+ this.store.emitter.emit('layer', { type: 'bottom', pens });
3930
+ }
3931
+ /**
3932
+ * data.pens 决定了绘制顺序,即越后面的越在上层
3933
+ * 该方法通过区域重叠计算,找出该画笔之后第一个与其重叠的画笔,然后把该画笔放到找出的画笔之后
3934
+ * @param pen 画笔
3935
+ */
3936
+ upByArea(pen) {
3937
+ const index = this.store.data.pens.findIndex((p) => p.id === pen.id);
3938
+ if (index === -1) {
3939
+ // 画笔不在画布上,不处理
3940
+ console.warn('upByArea: pen not in canvas');
3941
+ return;
3942
+ }
3943
+ const allPens = [pen, ...getAllChildren(pen, this.store)];
3944
+ let allIndexs = allPens.map((p) => this.store.data.pens.findIndex((p2) => p2.id === p.id));
3945
+ if (allIndexs.includes(-1)) {
3946
+ // 画笔不在画布上,脏数据
3947
+ console.warn('upByArea: pen children not in canvas');
3948
+ allIndexs = allIndexs.filter((i) => i !== -1);
3949
+ }
3950
+ const minIndex = Math.min(...allIndexs);
3951
+ const penRect = pen.calculative.worldRect;
3952
+ const nextHitIndex = this.store.data.pens.findIndex((p, i) => {
3953
+ if (i <= minIndex) {
3954
+ // 不考虑前面的
3955
+ return false;
3956
+ }
3957
+ if (p.id === pen.id || isAncestor(p, pen)) {
3958
+ // 不考虑后代和自身
3959
+ return false;
3960
+ }
3961
+ const currentRect = p.calculative.worldRect;
3962
+ return rectInRect(penRect, currentRect);
3963
+ });
3964
+ if (nextHitIndex === -1) {
3965
+ this.up(pen);
3966
+ return;
3967
+ }
3968
+ this.store.data.pens.splice(nextHitIndex + 1, 0, ...allPens);
3969
+ // 删除靠前的 allPens
3970
+ for (const pen of allPens) {
3971
+ const index = this.store.data.pens.findIndex((p) => p.id === pen.id);
3972
+ if (index > -1) {
3973
+ this.store.data.pens.splice(index, 1);
3974
+ }
3975
+ }
3976
+ this.initImageCanvas([pen]);
3977
+ }
3978
+ //特殊图元层级处理
3979
+ specificLayerMove(pen, type) {
3980
+ //image
3981
+ if (pen.image && pen.name !== 'gif') {
3982
+ // let isBottom = false;
3983
+ // if (type === 'bottom' || type === 'down') {
3984
+ // isBottom = true;
3985
+ // }
3986
+ // this.setValue(
3987
+ // { id: pen.id, isBottom },
3988
+ // { render: false, doEvent: false, history: false }
3989
+ // );
3990
+ let layer = CanvasLayer.CanvasImageBottom;
3991
+ if (type === 'top') {
3992
+ layer = CanvasLayer.CanvasImage;
3993
+ }
3994
+ else if (type === 'up' || type === 'down') {
3995
+ layer = CanvasLayer.CanvasMain;
3996
+ }
3997
+ this.setValue({ id: pen.id, canvasLayer: layer }, { render: false, doEvent: false, history: false });
3998
+ }
3999
+ else if (pen.externElement || pen.name === 'gif') {
4000
+ let zIndex = 0;
4001
+ // let zIndex = pen.calculative.zIndex === undefined ? 5 : pen.calculative.zIndex + 1;
4002
+ if (type === 'top') {
4003
+ pen.calculative.canvas.maxZindex += 1;
4004
+ zIndex = pen.calculative.canvas.maxZindex;
4005
+ }
4006
+ else if (type === 'up') {
4007
+ zIndex =
4008
+ pen.calculative.zIndex === undefined ? 6 : pen.calculative.zIndex + 1;
4009
+ }
4010
+ else if (type === 'down') {
4011
+ zIndex =
4012
+ pen.calculative.zIndex === undefined ? 3 : pen.calculative.zIndex - 1;
4013
+ if (zIndex < 0) {
4014
+ zIndex = 0;
4015
+ }
4016
+ }
4017
+ this.setValue({ id: pen.id, zIndex }, { render: false, doEvent: false, history: false });
4018
+ pen.calculative.singleton?.div &&
4019
+ setElemPosition(pen, pen.calculative.singleton.div);
4020
+ }
4021
+ }
4022
+ /**
4023
+ * 该画笔上移,即把该画笔在数组中的位置向后移动一个
4024
+ * @param pens 画笔
4025
+ */
4026
+ up(pens) {
4027
+ if (!pens)
4028
+ pens = this.store.active;
4029
+ if (!Array.isArray(pens))
4030
+ pens = [pens]; // 兼容
4031
+ for (const pen of pens) {
4032
+ const _pens = this.store.data.pens;
4033
+ if (pen.children && pen.children.length) {
4034
+ //组合图元
4035
+ const preMovePens = [...getAllChildren(pen, this.store), pen];
4036
+ //先保证组合图元的顺序正确。
4037
+ const orderPens = [];
4038
+ for (let index = 0; index < _pens.length; index++) {
4039
+ const _pen = _pens[index];
4040
+ if (preMovePens.findIndex((p) => p.id === _pen.id) !== -1) {
4041
+ _pen.temIndex = index;
4042
+ orderPens.push(_pen);
4043
+ }
4044
+ }
4045
+ let lastIndex = -1;
4046
+ let offset = 0;
4047
+ orderPens.forEach((_pen) => {
4048
+ _pen.temIndex -= offset;
4049
+ _pens.splice(_pen.temIndex, 1);
4050
+ offset += 1;
4051
+ lastIndex = _pen.temIndex;
4052
+ delete _pen.temIndex;
4053
+ this.specificLayerMove(_pen, 'up');
4054
+ });
4055
+ _pens.splice(lastIndex + 1, 0, ...orderPens);
4056
+ this.initTemplateCanvas(orderPens);
4057
+ this.initImageCanvas(orderPens);
4058
+ }
4059
+ else {
4060
+ const index = _pens.findIndex((p) => p.id === pen.id);
4061
+ if (index > -1 && index !== _pens.length - 1) {
4062
+ _pens.splice(index + 2, 0, _pens[index]);
4063
+ _pens.splice(index, 1);
4064
+ this.initTemplateCanvas([pen]);
4065
+ this.initImageCanvas([pen]);
4066
+ }
4067
+ this.specificLayerMove(pen, 'up');
4068
+ }
4069
+ }
4070
+ this.store.emitter.emit('layer', { type: 'up', pens });
4071
+ }
4072
+ /**
4073
+ * 该画笔下移,即把该画笔在该数组中的位置前移一个
4074
+ * @param pen 画笔
4075
+ */
4076
+ down(pens) {
4077
+ if (!pens)
4078
+ pens = this.store.active;
4079
+ if (!Array.isArray(pens))
4080
+ pens = [pens]; // 兼容
4081
+ for (const pen of pens) {
4082
+ const _pens = this.store.data.pens;
4083
+ if (pen.children && pen.children.length) {
4084
+ //组合图元
4085
+ const preMovePens = [...getAllChildren(pen, this.store), pen];
4086
+ //先保证组合图元的顺序正确。
4087
+ const orderPens = [];
4088
+ for (let index = 0; index < _pens.length; index++) {
4089
+ const _pen = _pens[index];
4090
+ if (preMovePens.findIndex((p) => p.id === _pen.id) !== -1) {
4091
+ _pen.temIndex = index;
4092
+ orderPens.push(_pen);
4093
+ }
4094
+ }
4095
+ let firstIndex = -1;
4096
+ let offset = 0;
4097
+ orderPens.forEach((_pen, index) => {
4098
+ _pen.temIndex -= offset;
4099
+ _pens.splice(_pen.temIndex, 1);
4100
+ offset += 1;
4101
+ if (index === 0) {
4102
+ firstIndex = _pen.temIndex;
4103
+ }
4104
+ delete _pen.temIndex;
4105
+ this.specificLayerMove(_pen, 'down');
4106
+ });
4107
+ _pens.splice(firstIndex - 1, 0, ...orderPens);
4108
+ this.initTemplateCanvas(orderPens);
4109
+ this.initImageCanvas(orderPens);
4110
+ }
4111
+ else {
4112
+ const index = _pens.findIndex((p) => p.id === pen.id);
4113
+ if (index > -1 && index !== 0) {
4114
+ _pens.splice(index - 1, 0, _pens[index]);
4115
+ _pens.splice(index + 1, 1);
4116
+ this.initTemplateCanvas([pen]);
4117
+ this.initImageCanvas([pen]);
4118
+ }
4119
+ this.specificLayerMove(pen, 'down');
4120
+ }
4121
+ }
4122
+ this.store.emitter.emit('layer', { type: 'down', pens });
4123
+ }
4124
+ setLayer(pen, toIndex, pens = this.store.data.pens) {
4125
+ const index = pens.findIndex((p) => p.id === pen.id);
4126
+ if (index > -1) {
4127
+ if (index > toIndex) {
4128
+ // 原位置在后,新位置在前
4129
+ pens.splice(toIndex, 0, pens[index]);
4130
+ pens.splice(index + 1, 1);
4131
+ }
4132
+ else if (index < toIndex) {
4133
+ // 新位置在后
4134
+ pens.splice(toIndex, 0, pens[index]);
4135
+ pens.splice(index, 1);
4136
+ }
4137
+ }
4138
+ }
4139
+ changePenId(oldId, newId) {
4140
+ this.canvas.changePenId(oldId, newId);
4141
+ }
4142
+ /**
4143
+ * 得到与当前节点连接的线
4144
+ * @param node 节点,非连线
4145
+ * @param type 类型,全部的连接线/入线/出线
4146
+ */
4147
+ getLines(node, type = 'all') {
4148
+ if (node.type === PenType.Line) {
4149
+ return [];
4150
+ }
4151
+ const lines = [];
4152
+ node.connectedLines?.forEach(({ lineId }) => {
4153
+ const line = this.store.pens[lineId];
4154
+ if (!line) {
4155
+ console.warn(node, 'node contain a error connectedLine');
4156
+ return;
4157
+ }
4158
+ if (lines.find((_line) => _line.id === line.id)) {
4159
+ //去重
4160
+ return;
4161
+ }
4162
+ switch (type) {
4163
+ case 'all':
4164
+ lines.push(line);
4165
+ break;
4166
+ case 'in':
4167
+ // 进入该节点的线,即 线锚点的最后一个 connectTo 对应该节点
4168
+ getToAnchor(line).connectTo === node.id && lines.push(line);
4169
+ break;
4170
+ case 'out':
4171
+ // 从该节点出去的线,即 线锚点的第一个 connectTo 对应该节点
4172
+ getFromAnchor(line).connectTo === node.id && lines.push(line);
4173
+ break;
4174
+ }
4175
+ });
4176
+ return lines;
4177
+ }
4178
+ /**
4179
+ * 得到当前节点的下一个节点,即出口节点数组
4180
+ * 得到当前连线的出口节点
4181
+ * @param pen 节点或连线
4182
+ */
4183
+ nextNode(pen) {
4184
+ if (pen.type === PenType.Line) {
4185
+ const nextNode = this.store.pens[getToAnchor(pen).connectTo];
4186
+ return nextNode ? [nextNode] : [];
4187
+ }
4188
+ else {
4189
+ // 1. 得到所有的出线
4190
+ const lines = this.getLines(pen, 'out');
4191
+ const nextNodes = [];
4192
+ // 2. 遍历出线的 nextNode
4193
+ lines.forEach((line) => {
4194
+ const lineNextNode = this.nextNode(line);
4195
+ for (const node of lineNextNode) {
4196
+ const have = nextNodes.find((next) => next.id === node.id);
4197
+ // 3. 不重复的才加进去
4198
+ !have && nextNodes.push(node);
4199
+ }
4200
+ });
4201
+ return nextNodes;
4202
+ }
4203
+ }
4204
+ /**
4205
+ * 得到当前节点的上一个节点,即入口节点数组
4206
+ * 得到当前连线的入口节点
4207
+ * @param pen 节点或连线
4208
+ */
4209
+ previousNode(pen) {
4210
+ if (pen.type === PenType.Line) {
4211
+ const preNode = this.store.pens[getFromAnchor(pen).connectTo];
4212
+ return preNode ? [preNode] : [];
4213
+ }
4214
+ else {
4215
+ // 1. 得到所有的入线
4216
+ const lines = this.getLines(pen, 'in');
4217
+ const preNodes = [];
4218
+ // 2. 遍历入线的 preNode
4219
+ lines.forEach((line) => {
4220
+ const linePreNode = this.previousNode(line);
4221
+ for (const node of linePreNode) {
4222
+ const have = preNodes.find((pre) => pre.id === node.id);
4223
+ // 3. 不重复的才加进去
4224
+ !have && preNodes.push(node);
4225
+ }
4226
+ });
4227
+ return preNodes;
4228
+ }
4229
+ }
4230
+ /**
4231
+ * 获取节点所有的下一个连接关系
4232
+ * @param pen
4233
+ *
4234
+ */
4235
+ getNext(pen) {
4236
+ if (pen.type === PenType.Line) {
4237
+ console.warn('非连线节点');
4238
+ return;
4239
+ }
4240
+ const next = [];
4241
+ pen.connectedLines?.forEach(({ lineId, anchor }) => {
4242
+ const fromAnchor = pen.anchors?.filter((_anchor) => _anchor.id === anchor)[0];
4243
+ const line = this.findOne(lineId);
4244
+ if (line.anchors[0].connectTo == pen.id) {
4245
+ //from
4246
+ const connectTo = line.anchors[line.anchors.length - 1].connectTo;
4247
+ if (connectTo) {
4248
+ const _next = this.findOne(connectTo);
4249
+ const connectedLine = _next.connectedLines?.filter((item) => item.lineId === line.id)[0];
4250
+ const penAnchor = _next.anchors.filter((_anchor) => _anchor.id === connectedLine.anchor)[0];
4251
+ next.push({
4252
+ from: pen,
4253
+ fromAnchor,
4254
+ line,
4255
+ to: _next,
4256
+ toAnchor: penAnchor,
4257
+ });
4258
+ }
4259
+ }
4260
+ });
4261
+ return next;
4262
+ }
4263
+ /**
4264
+ * 为画布添加锚点
4265
+ * @param pen 画笔
4266
+ * @param anchor 待添加锚点
4267
+ * @param index 连线类型 添加锚点到哪个位置
4268
+ */
4269
+ addAnchor(pen, anchor, index) {
4270
+ if (!pen) {
4271
+ return;
4272
+ }
4273
+ if (!pen.anchors) {
4274
+ pen.anchors = [];
4275
+ }
4276
+ if (!pen.calculative.worldAnchors) {
4277
+ pen.calculative.worldAnchors = [];
4278
+ }
4279
+ if (pen.type === PenType.Line) {
4280
+ if (index < 0) {
4281
+ index = pen.anchors.length + 1 + index;
4282
+ }
4283
+ if (index > pen.anchors.length) {
4284
+ index = pen.anchors.length;
4285
+ }
4286
+ if (index < 0) {
4287
+ index = 0;
4288
+ }
4289
+ if ((index == 0 && pen.anchors[0].connectTo) ||
4290
+ (index == pen.anchors.length && pen.anchors[index - 1].connectTo)) {
4291
+ console.warn('端点存在连接关系');
4292
+ return;
4293
+ }
4294
+ }
4295
+ let _anchor = null;
4296
+ let _worldAnchor = null;
4297
+ if (anchor.x <= 1 && anchor.x >= 0 && anchor.y <= 1 && anchor.y >= 0) {
4298
+ //relative
4299
+ _worldAnchor = {
4300
+ id: anchor.id || s8(),
4301
+ penId: pen.id,
4302
+ x: pen.calculative.worldRect.x +
4303
+ pen.calculative.worldRect.width * anchor.x,
4304
+ y: pen.calculative.worldRect.y +
4305
+ pen.calculative.worldRect.height * anchor.y,
4306
+ };
4307
+ if (pen.calculative.worldRect) {
4308
+ if (pen.rotate % 360) {
4309
+ rotatePoint(_worldAnchor, pen.rotate, pen.calculative.worldRect.center);
4310
+ }
4311
+ }
4312
+ _anchor = {
4313
+ id: _worldAnchor.id,
4314
+ penId: pen.id,
4315
+ x: anchor.x,
4316
+ y: anchor.y,
4317
+ };
4318
+ }
4319
+ else {
4320
+ //absolute
4321
+ _worldAnchor = {
4322
+ id: anchor.id || s8(),
4323
+ penId: pen.id,
4324
+ x: anchor.x,
4325
+ y: anchor.y,
4326
+ };
4327
+ if (pen.calculative.worldRect) {
4328
+ if (pen.rotate % 360) {
4329
+ rotatePoint(anchor, -pen.rotate, pen.calculative.worldRect.center);
4330
+ }
4331
+ _anchor = {
4332
+ id: _worldAnchor.id,
4333
+ penId: pen.id,
4334
+ x: (anchor.x - pen.calculative.worldRect.x) /
4335
+ pen.calculative.worldRect.width,
4336
+ y: (anchor.y - pen.calculative.worldRect.y) /
4337
+ pen.calculative.worldRect.height,
4338
+ };
4339
+ }
4340
+ }
4341
+ if (pen.type === PenType.Line) {
4342
+ //Line
4343
+ pen.calculative.worldAnchors.splice(index, 0, _worldAnchor);
4344
+ pen.anchors.splice(index, 0, _anchor);
4345
+ this.canvas.updateLines(pen);
4346
+ this.canvas.initLineRect(pen);
4347
+ this.render();
4348
+ }
4349
+ else {
4350
+ //Node
4351
+ pen.calculative.worldAnchors.push(_worldAnchor);
4352
+ pen.anchors.push(_anchor);
4353
+ }
4354
+ }
4355
+ /**
4356
+ *
4357
+ * @param from 连接节点
4358
+ * @param fromAnchor 连接节点锚点
4359
+ * @param to 被连接节点
4360
+ * @param toAnchor 被连接节点锚点
4361
+ */
4362
+ connectLine(from, to, fromAnchor, toAnchor, render = true) {
4363
+ if (!fromAnchor) {
4364
+ const _worldRect = to.calculative.worldRect;
4365
+ fromAnchor = nearestAnchor(from, {
4366
+ x: _worldRect.x + _worldRect.width / 2,
4367
+ y: _worldRect.y + _worldRect.height / 2,
4368
+ });
4369
+ }
4370
+ if (!toAnchor) {
4371
+ const _worldRect = from.calculative.worldRect;
4372
+ toAnchor = nearestAnchor(to, {
4373
+ x: _worldRect.x + _worldRect.width / 2,
4374
+ y: _worldRect.y + _worldRect.height / 2,
4375
+ });
4376
+ }
4377
+ const absWidth = Math.abs(fromAnchor.x - toAnchor.x);
4378
+ const absHeight = Math.abs(fromAnchor.y - toAnchor.y);
4379
+ const line = {
4380
+ height: absHeight,
4381
+ lineName: 'line',
4382
+ lineWidth: 1,
4383
+ name: 'line',
4384
+ type: 1,
4385
+ width: absWidth,
4386
+ x: Math.min(fromAnchor.x, toAnchor.x),
4387
+ y: Math.min(fromAnchor.y, toAnchor.y),
4388
+ anchors: [
4389
+ {
4390
+ x: fromAnchor.x > toAnchor.x ? 1 : 0,
4391
+ y: fromAnchor.y > toAnchor.y ? 1 : 0,
4392
+ id: s8(),
4393
+ },
4394
+ {
4395
+ x: fromAnchor.x > toAnchor.x ? 0 : 1,
4396
+ y: fromAnchor.x > toAnchor.x ? 0 : 1,
4397
+ id: s8(),
4398
+ },
4399
+ ],
4400
+ };
4401
+ this.addPens([line]);
4402
+ connectLine(from, fromAnchor, line, line.calculative.worldAnchors[0]);
4403
+ connectLine(to, toAnchor, line, line.calculative.worldAnchors[1]);
4404
+ line.calculative.active = false;
4405
+ this.canvas.updateLines(line);
4406
+ this.canvas.updateLines(from);
4407
+ this.canvas.updateLines(to);
4408
+ this.canvas.initLineRect(line);
4409
+ if (render) {
4410
+ this.render();
4411
+ }
4412
+ return line;
4413
+ }
4414
+ /**
4415
+ * 生成一个拷贝组合后的 画笔数组(组合图形),不影响原画布画笔,常用作 二次复用的组件
4416
+ * @param pens 画笔数组
4417
+ * @param showChild 是否作为状态复用(参考 combine showChild)
4418
+ * @param anchor 是否产生默认的锚点
4419
+ * @returns 组合图形
4420
+ */
4421
+ toComponent(pens = this.store.data.pens, showChild, anchor) {
4422
+ if (pens.length === 1) {
4423
+ const pen = deepClone(pens[0]);
4424
+ pen.type = PenType.Node;
4425
+ pen.id = undefined;
4426
+ return [pen];
4427
+ }
4428
+ const components = deepClone(pens, true);
4429
+ const rect = getRect(components);
4430
+ let parent = {
4431
+ id: s8(),
4432
+ name: 'combine',
4433
+ ...rect,
4434
+ children: [],
4435
+ showChild,
4436
+ };
4437
+ if (anchor) {
4438
+ parent.anchors = [
4439
+ {
4440
+ id: '0',
4441
+ penId: parent.id,
4442
+ x: 0.5,
4443
+ y: 0,
4444
+ },
4445
+ {
4446
+ id: '1',
4447
+ penId: parent.id,
4448
+ x: 1,
4449
+ y: 0.5,
4450
+ },
4451
+ {
4452
+ id: '2',
4453
+ penId: parent.id,
4454
+ x: 0.5,
4455
+ y: 1,
4456
+ },
4457
+ {
4458
+ id: '3',
4459
+ penId: parent.id,
4460
+ x: 0,
4461
+ y: 0.5,
4462
+ },
4463
+ ];
4464
+ }
4465
+ //如果本身就是 一个 组合图元
4466
+ const parents = components.filter((pen) => !pen.parentId);
4467
+ const p = components.find((pen) => {
4468
+ return pen.width === rect.width && pen.height === rect.height;
4469
+ });
4470
+ const oneIsParent = p && showChild === undefined;
4471
+ if (parents.length === 1) {
4472
+ parent = parents[0];
4473
+ }
4474
+ else if (oneIsParent) {
4475
+ if (!p.children) {
4476
+ p.children = [];
4477
+ }
4478
+ parent = p;
4479
+ }
4480
+ else {
4481
+ // 不影响画布数据,生成一个组合图形便于二次复用
4482
+ // this.canvas.makePen(parent);
4483
+ }
4484
+ components.forEach((pen) => {
4485
+ if (pen === parent || pen.parentId === parent.id) {
4486
+ return;
4487
+ }
4488
+ if (pen.parentId) {
4489
+ // 已经是其它节点的子节点,x,y,w,h 已经是百分比了
4490
+ return;
4491
+ }
4492
+ parent.children.push(pen.id);
4493
+ pen.parentId = parent.id;
4494
+ const childRect = calcRelativeRect(pen.calculative.worldRect, rect);
4495
+ Object.assign(pen, childRect);
4496
+ pen.locked = pen.lockedOnCombine ?? LockState.DisableMove;
4497
+ // pen.type = PenType.Node;
4498
+ });
4499
+ return (oneIsParent || parents.length === 1)
4500
+ ? deepClone(components)
4501
+ : deepClone([parent, ...components]);
4502
+ }
4503
+ // TODO 安装pen插件 此处是否应当进行相关的适配?不再让插件内部处理install的目标逻辑?
4504
+ /**
4505
+ * @description 安装插件方法
4506
+ * @param plugins 插件列表及其配置项
4507
+ * @param pen {string | Pen} 接受tag、name、或者Pen对象*/
4508
+ installPenPlugins(pen, plugins) {
4509
+ if (!pen.tag && !pen.name && !pen.id)
4510
+ return;
4511
+ let type;
4512
+ pen.id ? type = 'id' :
4513
+ pen.tag ? type = 'tag' :
4514
+ pen.name ? type = 'name' : '';
4515
+ plugins.forEach(pluginConfig => {
4516
+ let plugin = pluginConfig.plugin;
4517
+ let option = pluginConfig.options;
4518
+ if (!plugin)
4519
+ return;
4520
+ // 插件校验
4521
+ if (validationPlugin(plugin) && type) {
4522
+ plugin.install(pen, option);
4523
+ // 若当前不存在此插件
4524
+ if (!this.penPluginMap.has(plugin)) {
4525
+ this.penPluginMap.set(plugin, [{ [type]: pen[type], option }]);
4526
+ }
4527
+ else {
4528
+ let op = this.penPluginMap.get(plugin).find((i) => {
4529
+ return i[type] === pen[type];
4530
+ });
4531
+ // 存在替换
4532
+ if (op) {
4533
+ op.option = option;
4534
+ }
4535
+ else {
4536
+ this.penPluginMap.get(plugin).push({
4537
+ [type]: pen[type],
4538
+ option
4539
+ });
4540
+ }
4541
+ }
4542
+ }
4543
+ });
4544
+ }
4545
+ uninstallPenPlugins(pen, plugins) {
4546
+ let type;
4547
+ pen.id ? type = 'id' :
4548
+ pen.tag ? type = 'tag' :
4549
+ pen.name ? type = 'name' : '';
4550
+ if (!type)
4551
+ return;
4552
+ plugins.forEach(pluginConfig => {
4553
+ let plugin = pluginConfig.plugin;
4554
+ plugin.uninstall(pen, pluginConfig.options);
4555
+ let mapList = this.penPluginMap.get(plugin);
4556
+ let op = mapList.findIndex(i => i[type] === pen[type]);
4557
+ if (op !== -1) {
4558
+ mapList.splice(op, 1);
4559
+ // TODO 在运行时 插件卸载后是否需要移除?
4560
+ if (mapList.length === 0) {
4561
+ this.penPluginMap.delete(plugin);
4562
+ }
4563
+ }
4564
+ });
4565
+ }
4566
+ setVisible(pen, visible, render = true) {
4567
+ this.onSizeUpdate();
4568
+ this.setValue({ id: pen.id, visible }, { render: false, doEvent: false });
4569
+ if (pen.children) {
4570
+ for (const childId of pen.children) {
4571
+ const child = this.store.pens[childId];
4572
+ child && this.setVisible(child, visible, false);
4573
+ }
4574
+ }
4575
+ let allPens = getAllChildren(pen, this.store);
4576
+ allPens.push(pen);
4577
+ this.initImageCanvas(allPens);
4578
+ render && this.render();
4579
+ }
4580
+ clearHover() {
4581
+ this.canvas.clearHover();
4582
+ }
4583
+ closeSocket() {
4584
+ this.closeWebsocket();
4585
+ this.closeMqtt();
4586
+ this.closeHttp();
4587
+ }
4588
+ setElemPosition = setElemPosition;
4589
+ setLifeCycleFunc = setLifeCycleFunc;
4590
+ destroy(onlyData) {
4591
+ this.clear(false);
4592
+ this.closeSocket();
4593
+ this.closeNetwork();
4594
+ this.store.emitter.all.clear(); // 内存释放
4595
+ this.canvas.destroy();
4596
+ this.canvas = undefined;
4597
+ globalStore[this.store.id] = undefined;
4598
+ if (!onlyData) {
4599
+ for (const k in globalStore) {
4600
+ delete globalStore[k];
4601
+ }
4602
+ globalStore.path2dDraws = {};
4603
+ globalStore.canvasDraws = {};
4604
+ globalStore.anchors = {};
4605
+ globalStore.htmlElements = {};
4606
+ }
4607
+ }
4608
+ }
4609
+ //# sourceMappingURL=core.js.map