@meta2d/core 1.0.56 → 1.0.57

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