@tmagic/stage 1.0.0-beta.8 → 1.0.0-rc.10

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.
@@ -21,15 +21,20 @@ import { EventEmitter } from 'events';
21
21
 
22
22
  import type { MoveableOptions } from 'moveable';
23
23
  import Moveable from 'moveable';
24
+ import MoveableHelper from 'moveable-helper';
24
25
 
25
- import { GHOST_EL_ID_PREFIX, GuidesType, Mode } from './const';
26
+ import { DRAG_EL_ID_PREFIX, GHOST_EL_ID_PREFIX, GuidesType, Mode, ZIndex } from './const';
26
27
  import StageCore from './StageCore';
27
28
  import type { SortEventData, StageDragResizeConfig } from './types';
28
- import { getAbsolutePosition, getMode, getOffset } from './util';
29
+ import { getAbsolutePosition, getGuideLineFromCache, getMode, getOffset } from './util';
29
30
 
31
+ /** 拖动状态 */
30
32
  enum ActionStatus {
33
+ /** 开始拖动 */
31
34
  START = 'start',
35
+ /** 拖动中 */
32
36
  ING = 'ing',
37
+ /** 拖动结束 */
33
38
  END = 'end',
34
39
  }
35
40
 
@@ -38,23 +43,38 @@ enum ActionStatus {
38
43
  */
39
44
  export default class StageDragResize extends EventEmitter {
40
45
  public core: StageCore;
46
+ /** 画布容器 */
41
47
  public container: HTMLElement;
48
+ /** 目标节点 */
42
49
  public target?: HTMLElement;
43
- public dragEl?: HTMLElement;
50
+ /** 目标节点在蒙层中的占位节点 */
51
+ public dragEl: HTMLDivElement;
52
+ /** Moveable拖拽类实例 */
44
53
  public moveable?: Moveable;
45
- public horizontalGuidelines: number[] = [];
46
- public verticalGuidelines: number[] = [];
54
+ /** 水平参考线 */
55
+ public horizontalGuidelines: number[] = getGuideLineFromCache(GuidesType.HORIZONTAL);
56
+ /** 垂直参考线 */
57
+ public verticalGuidelines: number[] = getGuideLineFromCache(GuidesType.VERTICAL);
58
+ /** 对齐元素集合 */
59
+ public elementGuidelines: HTMLElement[] = [];
60
+ /** 布局方式:流式布局、绝对定位、固定定位 */
61
+ public mode: Mode = Mode.ABSOLUTE;
47
62
 
48
63
  private moveableOptions: MoveableOptions = {};
64
+ /** 拖动状态 */
49
65
  private dragStatus: ActionStatus = ActionStatus.END;
66
+ /** 流式布局下,目标节点的镜像节点 */
50
67
  private ghostEl: HTMLElement | undefined;
51
- private mode: Mode = Mode.ABSOLUTE;
68
+ private moveableHelper?: MoveableHelper;
52
69
 
53
70
  constructor(config: StageDragResizeConfig) {
54
71
  super();
55
72
 
56
73
  this.core = config.core;
57
74
  this.container = config.container;
75
+
76
+ this.dragEl = globalThis.document.createElement('div');
77
+ this.container.append(this.dragEl);
58
78
  }
59
79
 
60
80
  /**
@@ -63,23 +83,23 @@ export default class StageDragResize extends EventEmitter {
63
83
  * @param el 选中组件的Dom节点元素
64
84
  * @param event 鼠标事件
65
85
  */
66
- public async select(el: HTMLElement, event?: MouseEvent): Promise<void> {
86
+ public select(el: HTMLElement, event?: MouseEvent): void {
87
+ const oldTarget = this.target;
67
88
  this.target = el;
68
- // 如果有滚动条会导致resize时获取到width,height不准确
69
- if (/(auto|scroll)/.test(this.target.style.overflow)) {
70
- this.target.style.overflow = 'hidden';
71
- }
72
- this.mode = getMode(el);
73
- this.destroyDragEl();
74
- this.destroyGhostEl();
75
89
 
76
- this.dragEl = this.generateDragEl(el);
77
-
78
- this.moveableOptions = await this.getOptions({
79
- target: this.dragEl || this.target,
80
- });
90
+ // 从不能拖动到能拖动的节点之间切换,要重新创建moveable,不然dragStart不生效
91
+ if (!this.moveable || this.target !== oldTarget) {
92
+ this.init(el);
93
+ this.moveableHelper = MoveableHelper.create({
94
+ useBeforeRender: true,
95
+ useRender: false,
96
+ createAuto: true,
97
+ });
81
98
 
82
- this.initMoveable();
99
+ this.initMoveable();
100
+ } else {
101
+ this.updateMoveable();
102
+ }
83
103
 
84
104
  if (event) {
85
105
  this.moveable?.dragStart(event);
@@ -89,11 +109,15 @@ export default class StageDragResize extends EventEmitter {
89
109
  /**
90
110
  * 初始化选中框并渲染出来
91
111
  */
92
- public async refresh() {
112
+ public updateMoveable(el = this.target): void {
93
113
  if (!this.moveable) throw new Error('未初始化moveable');
114
+ if (!el) throw new Error('为选中任何节点');
94
115
 
95
- const options = await this.getOptions();
96
- Object.entries(options).forEach(([key, value]) => {
116
+ this.target = el;
117
+
118
+ this.init(el);
119
+
120
+ Object.entries(this.moveableOptions).forEach(([key, value]) => {
97
121
  (this.moveable as any)[key] = value;
98
122
  });
99
123
  this.moveable.updateTarget();
@@ -102,17 +126,21 @@ export default class StageDragResize extends EventEmitter {
102
126
  public setGuidelines(type: GuidesType, guidelines: number[]): void {
103
127
  if (type === GuidesType.HORIZONTAL) {
104
128
  this.horizontalGuidelines = guidelines;
129
+ this.moveableOptions.horizontalGuidelines = guidelines;
105
130
  } else if (type === GuidesType.VERTICAL) {
106
131
  this.verticalGuidelines = guidelines;
132
+ this.moveableOptions.verticalGuidelines = guidelines;
107
133
  }
108
134
 
109
- this.refresh();
135
+ this.updateMoveable();
110
136
  }
111
137
 
112
138
  public clearGuides() {
113
- this.verticalGuidelines = [];
114
139
  this.horizontalGuidelines = [];
115
- this.refresh();
140
+ this.verticalGuidelines = [];
141
+ this.moveableOptions.horizontalGuidelines = [];
142
+ this.moveableOptions.verticalGuidelines = [];
143
+ this.updateMoveable();
116
144
  }
117
145
 
118
146
  /**
@@ -126,6 +154,45 @@ export default class StageDragResize extends EventEmitter {
126
154
  this.removeAllListeners();
127
155
  }
128
156
 
157
+ private init(el: HTMLElement): void {
158
+ // 如果有滚动条会导致resize时获取到width,height不准确
159
+ if (/(auto|scroll)/.test(el.style.overflow)) {
160
+ el.style.overflow = 'hidden';
161
+ }
162
+ this.mode = getMode(el);
163
+
164
+ this.destroyGhostEl();
165
+
166
+ this.updateDragEl(el);
167
+
168
+ this.moveableOptions = this.getOptions({
169
+ target: this.dragEl,
170
+ });
171
+ }
172
+
173
+ private setElementGuidelines(nodes: HTMLElement[]) {
174
+ this.elementGuidelines.forEach((node) => {
175
+ node.remove();
176
+ });
177
+ this.elementGuidelines = [];
178
+
179
+ if (this.mode === Mode.ABSOLUTE) {
180
+ const frame = document.createDocumentFragment();
181
+
182
+ for (const node of nodes) {
183
+ const { width, height } = node.getBoundingClientRect();
184
+ if (node === this.target) continue;
185
+ const { left, top } = getOffset(node as HTMLElement);
186
+ const elementGuideline = document.createElement('div');
187
+ elementGuideline.style.cssText = `position: absolute;width: ${width}px;height: ${height}px;top: ${top}px;left: ${left}px`;
188
+ this.elementGuidelines.push(elementGuideline);
189
+ frame.append(elementGuideline);
190
+ }
191
+
192
+ this.container.append(frame);
193
+ }
194
+ }
195
+
129
196
  private initMoveable() {
130
197
  this.moveable?.destroy();
131
198
 
@@ -135,49 +202,47 @@ export default class StageDragResize extends EventEmitter {
135
202
 
136
203
  this.bindResizeEvent();
137
204
  this.bindDragEvent();
205
+ this.bindRotateEvent();
206
+ this.bindScaleEvent();
138
207
  }
139
208
 
140
209
  private bindResizeEvent(): void {
141
210
  if (!this.moveable) throw new Error('moveable 为初始化');
142
211
 
143
212
  const frame = {
144
- translate: [0, 0],
213
+ left: 0,
214
+ top: 0,
145
215
  };
146
216
 
147
217
  this.moveable
148
218
  .on('resizeStart', (e) => {
149
- if (e.dragStart) {
150
- const rect = this.moveable!.getRect();
151
- const offset = getAbsolutePosition(e.target as HTMLElement, rect);
152
- e.dragStart.set([offset.left, offset.top]);
153
- }
219
+ if (!this.target) return;
220
+
221
+ this.dragStatus = ActionStatus.START;
222
+ this.moveableHelper?.onResizeStart(e);
223
+
224
+ frame.top = this.target.offsetTop;
225
+ frame.left = this.target.offsetLeft;
154
226
  })
155
- .on('resize', ({ width, height, drag }) => {
227
+ .on('resize', (e) => {
228
+ const { width, height, drag } = e;
156
229
  if (!this.moveable || !this.target || !this.dragEl) return;
157
230
 
158
231
  const { beforeTranslate } = drag;
159
- frame.translate = beforeTranslate;
160
232
  this.dragStatus = ActionStatus.ING;
161
233
 
162
- this.target.style.width = `${width}px`;
163
- this.target.style.height = `${height}px`;
164
- this.dragEl.style.width = `${width}px`;
165
- this.dragEl.style.height = `${height}px`;
234
+ this.moveableHelper?.onResize(e);
166
235
 
167
236
  // 流式布局
168
237
  if (this.mode === Mode.SORTABLE) {
169
- this.dragEl.style.top = `${beforeTranslate[1]}px`;
170
- this.target.style.top = `0px`;
171
- return;
238
+ this.target.style.top = '0px';
239
+ } else {
240
+ this.target.style.left = `${frame.left + beforeTranslate[0]}px`;
241
+ this.target.style.top = `${frame.top + beforeTranslate[1]}px`;
172
242
  }
173
243
 
174
- this.dragEl.style.left = `${beforeTranslate[0]}px`;
175
- this.dragEl.style.top = `${beforeTranslate[1]}px`;
176
-
177
- const offset = getAbsolutePosition(this.target, { left: beforeTranslate[0], top: beforeTranslate[1] });
178
-
179
- this.target.style.left = `${offset.left}px`;
180
- this.target.style.top = `${offset.top}px`;
244
+ this.target.style.width = `${width}px`;
245
+ this.target.style.height = `${height}px`;
181
246
  })
182
247
  .on('resizeEnd', () => {
183
248
  this.dragStatus = ActionStatus.END;
@@ -188,34 +253,41 @@ export default class StageDragResize extends EventEmitter {
188
253
  private bindDragEvent(): void {
189
254
  if (!this.moveable) throw new Error('moveable 为初始化');
190
255
 
256
+ const frame = {
257
+ left: 0,
258
+ top: 0,
259
+ };
260
+
191
261
  this.moveable
192
- .on('dragStart', () => {
262
+ .on('dragStart', (e) => {
193
263
  if (!this.target) throw new Error('未选中组件');
194
264
 
195
265
  this.dragStatus = ActionStatus.START;
196
266
 
267
+ this.moveableHelper?.onDragStart(e);
268
+
197
269
  if (this.mode === Mode.SORTABLE) {
198
270
  this.ghostEl = this.generateGhostEl(this.target);
199
271
  }
272
+
273
+ frame.top = this.target.offsetTop;
274
+ frame.left = this.target.offsetLeft;
200
275
  })
201
- .on('drag', ({ left, top }) => {
276
+ .on('drag', (e) => {
202
277
  if (!this.target || !this.dragEl) return;
203
- this.dragStatus = ActionStatus.ING;
204
278
 
205
- const offset = getAbsolutePosition(this.target, { left, top });
279
+ this.dragStatus = ActionStatus.ING;
206
280
 
207
281
  // 流式布局
208
282
  if (this.ghostEl) {
209
- this.dragEl.style.top = `${top}px`;
210
- this.ghostEl.style.top = `${offset.top}px`;
283
+ this.ghostEl.style.top = `${frame.top + e.beforeTranslate[1]}px`;
211
284
  return;
212
285
  }
213
286
 
214
- this.dragEl.style.left = `${left}px`;
215
- this.dragEl.style.top = `${top}px`;
287
+ this.moveableHelper?.onDrag(e);
216
288
 
217
- this.target.style.left = `${offset.left}px`;
218
- this.target.style.top = `${offset.top}px`;
289
+ this.target.style.left = `${frame.left + e.beforeTranslate[0]}px`;
290
+ this.target.style.top = `${frame.top + e.beforeTranslate[1]}px`;
219
291
  })
220
292
  .on('dragEnd', () => {
221
293
  // 点击不拖动时会触发dragStart和dragEnd,但是不会有drag事件
@@ -234,19 +306,58 @@ export default class StageDragResize extends EventEmitter {
234
306
  });
235
307
  }
236
308
 
237
- private async getSnapElements(el: HTMLElement): Promise<HTMLElement[]> {
238
- const { renderer } = this.core;
239
- const getSnapElements =
240
- (await renderer.getRuntime())?.getSnapElements ||
241
- (() => {
242
- const doc = renderer.contentWindow?.document;
243
- return doc ? Array.from(doc.querySelectorAll('[id]')) : [];
309
+ private bindRotateEvent(): void {
310
+ if (!this.moveable) throw new Error('moveable 为初始化');
311
+
312
+ this.moveable
313
+ .on('rotateStart', (e) => {
314
+ this.dragStatus = ActionStatus.START;
315
+ this.moveableHelper?.onRotateStart(e);
316
+ })
317
+ .on('rotate', (e) => {
318
+ if (!this.target || !this.dragEl) return;
319
+ this.dragStatus = ActionStatus.ING;
320
+ this.moveableHelper?.onRotate(e);
321
+ const frame = this.moveableHelper?.getFrame(e.target);
322
+ this.target.style.transform = frame?.toCSSObject().transform || '';
323
+ })
324
+ .on('rotateEnd', (e) => {
325
+ this.dragStatus = ActionStatus.END;
326
+ const frame = this.moveableHelper?.getFrame(e.target);
327
+ this.emit('update', {
328
+ el: this.target,
329
+ style: {
330
+ transform: frame?.get('transform'),
331
+ },
332
+ });
333
+ });
334
+ }
335
+
336
+ private bindScaleEvent(): void {
337
+ if (!this.moveable) throw new Error('moveable 为初始化');
338
+
339
+ this.moveable
340
+ .on('scaleStart', (e) => {
341
+ this.dragStatus = ActionStatus.START;
342
+ this.moveableHelper?.onScaleStart(e);
343
+ })
344
+ .on('scale', (e) => {
345
+ if (!this.target || !this.dragEl) return;
346
+ this.dragStatus = ActionStatus.ING;
347
+ this.moveableHelper?.onScale(e);
348
+ const frame = this.moveableHelper?.getFrame(e.target);
349
+ this.target.style.transform = frame?.toCSSObject().transform || '';
350
+ })
351
+ .on('scaleEnd', (e) => {
352
+ this.dragStatus = ActionStatus.END;
353
+ const frame = this.moveableHelper?.getFrame(e.target);
354
+ this.emit('update', {
355
+ el: this.target,
356
+ style: {
357
+ transform: frame?.get('transform'),
358
+ },
359
+ });
244
360
  });
245
- return (
246
- getSnapElements(el)
247
- // 排除掉当前组件本身
248
- .filter((element) => element !== this.target && !this.target?.contains(element))
249
- );
250
361
  }
251
362
 
252
363
  private sort(): void {
@@ -269,14 +380,15 @@ export default class StageDragResize extends EventEmitter {
269
380
  }
270
381
 
271
382
  private update(isResize = false): void {
272
- const rect = this.moveable!.getRect();
383
+ if (!this.target) return;
384
+
273
385
  const offset =
274
- this.mode === Mode.SORTABLE ? { left: 0, top: 0 } : getAbsolutePosition(this.target as HTMLElement, rect);
386
+ this.mode === Mode.SORTABLE ? { left: 0, top: 0 } : { left: this.target.offsetLeft, top: this.target.offsetTop };
275
387
 
276
388
  const left = this.calcValueByFontsize(offset.left);
277
389
  const top = this.calcValueByFontsize(offset.top);
278
- const width = this.calcValueByFontsize(rect.width);
279
- const height = this.calcValueByFontsize(rect.height);
390
+ const width = this.calcValueByFontsize(this.target.clientWidth);
391
+ const height = this.calcValueByFontsize(this.target.clientHeight);
280
392
 
281
393
  this.emit('update', {
282
394
  el: this.target,
@@ -291,8 +403,8 @@ export default class StageDragResize extends EventEmitter {
291
403
 
292
404
  const ghostEl = el.cloneNode(true) as HTMLElement;
293
405
  const { top, left } = getAbsolutePosition(el, getOffset(el));
294
- ghostEl.id = `${GHOST_EL_ID_PREFIX}${ghostEl.id}`;
295
- ghostEl.style.zIndex = '5';
406
+ ghostEl.id = `${GHOST_EL_ID_PREFIX}${el.id}`;
407
+ ghostEl.style.zIndex = ZIndex.GHOST_EL;
296
408
  ghostEl.style.opacity = '.5';
297
409
  ghostEl.style.position = 'absolute';
298
410
  ghostEl.style.left = `${left}px`;
@@ -306,34 +418,36 @@ export default class StageDragResize extends EventEmitter {
306
418
  this.ghostEl = undefined;
307
419
  }
308
420
 
309
- private generateDragEl(el: HTMLElement): HTMLElement {
310
- if (this.dragEl) {
311
- this.destroyDragEl();
312
- }
313
-
314
- const { width, height } = el.getBoundingClientRect();
421
+ private updateDragEl(el: HTMLElement) {
315
422
  const offset = getOffset(el);
316
- const dragEl = globalThis.document.createElement('div');
317
- dragEl.style.cssText = `
423
+ const { transform } = getComputedStyle(el);
424
+
425
+ this.dragEl.style.cssText = `
318
426
  position: absolute;
427
+ transform: ${transform};
319
428
  left: ${offset.left}px;
320
429
  top: ${offset.top}px;
321
- width: ${width}px;
322
- height: ${height}px;
430
+ width: ${el.clientWidth}px;
431
+ height: ${el.clientHeight}px;
432
+ z-index: ${ZIndex.DRAG_EL};
323
433
  `;
324
- this.container.append(dragEl);
325
- return dragEl;
434
+
435
+ this.dragEl.id = `${DRAG_EL_ID_PREFIX}${el.id}`;
436
+
437
+ if (typeof this.core.config.updateDragEl === 'function') {
438
+ this.core.config.updateDragEl(this.dragEl, el);
439
+ }
326
440
  }
327
441
 
328
442
  private destroyDragEl(): void {
329
443
  this.dragEl?.remove();
330
- this.dragEl = undefined;
331
444
  }
332
445
 
333
- private async getOptions(options: MoveableOptions = {}): Promise<MoveableOptions> {
446
+ private getOptions(options: MoveableOptions = {}): MoveableOptions {
334
447
  if (!this.target) return {};
335
448
 
336
449
  const isAbsolute = this.mode === Mode.ABSOLUTE;
450
+ const isFixed = this.mode === Mode.FIXED;
337
451
 
338
452
  let { moveableOptions = {} } = this.core.config;
339
453
 
@@ -341,21 +455,47 @@ export default class StageDragResize extends EventEmitter {
341
455
  moveableOptions = moveableOptions(this.core);
342
456
  }
343
457
 
458
+ const elementGuidelines: any = moveableOptions.elementGuidelines || this.target.parentElement?.children || [];
459
+
460
+ this.setElementGuidelines(elementGuidelines);
461
+
462
+ if (moveableOptions.elementGuidelines) {
463
+ delete moveableOptions.elementGuidelines;
464
+ }
465
+
344
466
  return {
345
- scrollable: true,
346
- origin: true,
467
+ origin: false,
468
+ rootContainer: this.core.container,
347
469
  zoom: 1,
348
- dragArea: true,
470
+ dragArea: false,
349
471
  draggable: true,
350
472
  resizable: true,
351
- snappable: isAbsolute,
352
- snapGap: isAbsolute,
353
- snapDirections: { center: isAbsolute, middle: isAbsolute },
354
- elementSnapDirections: { center: isAbsolute, middle: isAbsolute },
355
-
356
- elementGuidelines: isAbsolute ? await this.getSnapElements(this.target) : [],
473
+ scalable: false,
474
+ rotatable: false,
475
+ snappable: isAbsolute || isFixed,
476
+ snapGap: isAbsolute || isFixed,
477
+ snapThreshold: 5,
478
+ snapDigit: 0,
479
+ throttleDrag: 0,
480
+ isDisplaySnapDigit: isAbsolute,
481
+ snapDirections: {
482
+ top: isAbsolute,
483
+ right: isAbsolute,
484
+ bottom: isAbsolute,
485
+ left: isAbsolute,
486
+ center: isAbsolute,
487
+ middle: isAbsolute,
488
+ },
489
+ elementSnapDirections: {
490
+ top: isAbsolute,
491
+ right: isAbsolute,
492
+ bottom: isAbsolute,
493
+ left: isAbsolute,
494
+ },
495
+ isDisplayInnerSnapDigit: true,
357
496
  horizontalGuidelines: this.horizontalGuidelines,
358
497
  verticalGuidelines: this.verticalGuidelines,
498
+ elementGuidelines: this.elementGuidelines,
359
499
 
360
500
  bounds: {
361
501
  top: 0,
@@ -0,0 +1,82 @@
1
+ /*
2
+ * Tencent is pleased to support the open source community by making TMagicEditor available.
3
+ *
4
+ * Copyright (C) 2021 THL A29 Limited, a Tencent company. All rights reserved.
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this file except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ */
18
+
19
+ /* eslint-disable no-param-reassign */
20
+ import { EventEmitter } from 'events';
21
+
22
+ import Moveable from 'moveable';
23
+
24
+ import { HIGHLIGHT_EL_ID_PREFIX } from './const';
25
+ import StageCore from './StageCore';
26
+ import TargetCalibrate from './TargetCalibrate';
27
+ import type { StageHighlightConfig } from './types';
28
+ export default class StageHighlight extends EventEmitter {
29
+ public core: StageCore;
30
+ public container: HTMLElement;
31
+ public target?: HTMLElement;
32
+ public moveable?: Moveable;
33
+ public calibrationTarget: TargetCalibrate;
34
+
35
+ constructor(config: StageHighlightConfig) {
36
+ super();
37
+
38
+ this.core = config.core;
39
+ this.container = config.container;
40
+ this.calibrationTarget = new TargetCalibrate({
41
+ parent: this.core.mask.content,
42
+ mask: this.core.mask,
43
+ dr: this.core.dr,
44
+ core: this.core,
45
+ });
46
+ }
47
+
48
+ /**
49
+ * 高亮鼠标悬停的组件
50
+ * @param el 选中组件的Dom节点元素
51
+ */
52
+ public highlight(el: HTMLElement): void {
53
+ if (!el || el === this.target) return;
54
+ this.target = el;
55
+ this.moveable?.destroy();
56
+
57
+ this.moveable = new Moveable(this.container, {
58
+ target: this.calibrationTarget.update(el, HIGHLIGHT_EL_ID_PREFIX),
59
+ origin: false,
60
+ rootContainer: this.core.container,
61
+ zoom: 1,
62
+ });
63
+ }
64
+
65
+ /**
66
+ * 清空高亮
67
+ */
68
+ public clearHighlight(): void {
69
+ if (!this.moveable) return;
70
+ this.target = undefined;
71
+ this.moveable.target = null;
72
+ this.moveable.updateTarget();
73
+ }
74
+
75
+ /**
76
+ * 销毁实例
77
+ */
78
+ public destroy(): void {
79
+ this.moveable?.destroy();
80
+ this.calibrationTarget.destroy();
81
+ }
82
+ }