@gedit/editor-2d 0.2.45 → 0.2.47

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 (95) hide show
  1. package/lib/browser/model/editor2d-document.d.ts +11 -2
  2. package/lib/browser/model/editor2d-document.d.ts.map +1 -1
  3. package/lib/browser/model/editor2d-document.js +44 -8
  4. package/lib/browser/model/editor2d-document.js.map +1 -1
  5. package/lib/browser/model/editor2d-model-container.d.ts.map +1 -1
  6. package/lib/browser/model/editor2d-model-container.js +1 -1
  7. package/lib/browser/model/editor2d-model-container.js.map +1 -1
  8. package/lib/browser/model/editor2d-model.d.ts +2 -0
  9. package/lib/browser/model/editor2d-model.d.ts.map +1 -1
  10. package/lib/browser/model/editor2d-model.js +5 -0
  11. package/lib/browser/model/editor2d-model.js.map +1 -1
  12. package/lib/browser/model/editor2d.d.ts +2 -0
  13. package/lib/browser/model/editor2d.d.ts.map +1 -1
  14. package/lib/browser/model/editor2d.js +4 -2
  15. package/lib/browser/model/editor2d.js.map +1 -1
  16. package/lib/browser/playground/canvas-draw.d.ts.map +1 -1
  17. package/lib/browser/playground/canvas-draw.js +30 -27
  18. package/lib/browser/playground/canvas-draw.js.map +1 -1
  19. package/lib/browser/playground/canvas-layer.d.ts +14 -2
  20. package/lib/browser/playground/canvas-layer.d.ts.map +1 -1
  21. package/lib/browser/playground/canvas-layer.js +96 -50
  22. package/lib/browser/playground/canvas-layer.js.map +1 -1
  23. package/lib/browser/playground/index.d.ts +2 -0
  24. package/lib/browser/playground/index.d.ts.map +1 -1
  25. package/lib/browser/playground/index.js +2 -0
  26. package/lib/browser/playground/index.js.map +1 -1
  27. package/lib/browser/playground/path-edit/index.d.ts +4 -0
  28. package/lib/browser/playground/path-edit/index.d.ts.map +1 -0
  29. package/lib/browser/playground/path-edit/index.js +20 -0
  30. package/lib/browser/playground/path-edit/index.js.map +1 -0
  31. package/lib/browser/playground/path-edit/path-edit-layer-move-point.d.ts +18 -0
  32. package/lib/browser/playground/path-edit/path-edit-layer-move-point.d.ts.map +1 -0
  33. package/lib/browser/playground/path-edit/path-edit-layer-move-point.js +52 -0
  34. package/lib/browser/playground/path-edit/path-edit-layer-move-point.js.map +1 -0
  35. package/lib/browser/playground/path-edit/path-edit-layer-svg-path.d.ts +21 -0
  36. package/lib/browser/playground/path-edit/path-edit-layer-svg-path.d.ts.map +1 -0
  37. package/lib/browser/playground/path-edit/path-edit-layer-svg-path.js +158 -0
  38. package/lib/browser/playground/path-edit/path-edit-layer-svg-path.js.map +1 -0
  39. package/lib/browser/playground/path-edit/utils.d.ts +37 -0
  40. package/lib/browser/playground/path-edit/utils.d.ts.map +1 -0
  41. package/lib/browser/playground/path-edit/utils.js +236 -0
  42. package/lib/browser/playground/path-edit/utils.js.map +1 -0
  43. package/lib/browser/playground/path-edit-layer.d.ts +32 -12
  44. package/lib/browser/playground/path-edit-layer.d.ts.map +1 -1
  45. package/lib/browser/playground/path-edit-layer.js +460 -146
  46. package/lib/browser/playground/path-edit-layer.js.map +1 -1
  47. package/lib/browser/playground/playground-context.d.ts +5 -2
  48. package/lib/browser/playground/playground-context.d.ts.map +1 -1
  49. package/lib/browser/playground/playground-context.js +13 -2
  50. package/lib/browser/playground/playground-context.js.map +1 -1
  51. package/lib/browser/playground/playground-contribution.d.ts +2 -1
  52. package/lib/browser/playground/playground-contribution.d.ts.map +1 -1
  53. package/lib/browser/playground/playground-contribution.js +4 -21
  54. package/lib/browser/playground/playground-contribution.js.map +1 -1
  55. package/lib/browser/playground/selection-entity-manager.d.ts.map +1 -1
  56. package/lib/browser/playground/selection-entity-manager.js +24 -8
  57. package/lib/browser/playground/selection-entity-manager.js.map +1 -1
  58. package/lib/browser/playground/selector-extend-renderer.d.ts +2 -1
  59. package/lib/browser/playground/selector-extend-renderer.d.ts.map +1 -1
  60. package/lib/browser/playground/selector-extend-renderer.js +50 -21
  61. package/lib/browser/playground/selector-extend-renderer.js.map +1 -1
  62. package/lib/browser/utils/snapshot.d.ts +1 -0
  63. package/lib/browser/utils/snapshot.d.ts.map +1 -1
  64. package/lib/browser/utils/snapshot.js +11 -0
  65. package/lib/browser/utils/snapshot.js.map +1 -1
  66. package/package.json +9 -7
  67. package/src/browser/model/editor2d-document.ts +44 -6
  68. package/src/browser/model/editor2d-model-container.ts +2 -0
  69. package/src/browser/model/editor2d-model.ts +2 -0
  70. package/src/browser/model/editor2d.ts +4 -1
  71. package/src/browser/playground/canvas-draw.ts +30 -25
  72. package/src/browser/playground/canvas-layer.ts +97 -52
  73. package/src/browser/playground/index.ts +2 -0
  74. package/src/browser/playground/path-edit/index.ts +3 -0
  75. package/src/browser/playground/path-edit/path-edit-layer-move-point.tsx +108 -0
  76. package/src/browser/playground/path-edit/path-edit-layer-svg-path.tsx +283 -0
  77. package/src/browser/playground/path-edit/utils.tsx +285 -0
  78. package/src/browser/playground/path-edit-layer.tsx +563 -216
  79. package/src/browser/playground/playground-context.ts +7 -1
  80. package/src/browser/playground/playground-contribution.ts +2 -21
  81. package/src/browser/playground/selection-entity-manager.tsx +34 -6
  82. package/src/browser/playground/selector-extend-renderer.tsx +69 -37
  83. package/src/browser/style/path-edit-layer.less +17 -30
  84. package/src/browser/svg/pen_close.svg +24 -0
  85. package/src/browser/utils/snapshot.ts +11 -0
  86. package/lib/browser/playground/path-edit-layer-move-point.d.ts +0 -15
  87. package/lib/browser/playground/path-edit-layer-move-point.d.ts.map +0 -1
  88. package/lib/browser/playground/path-edit-layer-move-point.js +0 -47
  89. package/lib/browser/playground/path-edit-layer-move-point.js.map +0 -1
  90. package/lib/browser/playground/path-edit-layer-svg-path.d.ts +0 -11
  91. package/lib/browser/playground/path-edit-layer-svg-path.d.ts.map +0 -1
  92. package/lib/browser/playground/path-edit-layer-svg-path.js +0 -21
  93. package/lib/browser/playground/path-edit-layer-svg-path.js.map +0 -1
  94. package/src/browser/playground/path-edit-layer-move-point.tsx +0 -71
  95. package/src/browser/playground/path-edit-layer-svg-path.tsx +0 -50
@@ -1,20 +1,59 @@
1
1
  import * as React from 'react';
2
- import { EditorStateConfigEntity, entity, Layer } from '@gedit/playground';
2
+ import {
3
+ EditorState,
4
+ EditorStateConfigEntity,
5
+ entity,
6
+ Layer,
7
+ PathPointSelection,
8
+ PathSelectMode,
9
+ SelectorBoxConfigEntity,
10
+ } from '@gedit/playground';
3
11
  import { DocumentEntity } from './entities/document-entity';
4
12
  import { domUtils } from '@gedit/utils/lib/browser';
5
13
  import { GameObjectBaseType } from '@gedit/render-engine';
6
- import {
7
- Editor2dDocument,
8
- Editor2dPathNode,
9
- } from '../model';
14
+ import { Editor2dDocument, Editor2dPathNode } from '../model';
10
15
  import { PlaygroundContext2d } from './playground-context';
11
16
  import { SelectableTreeNode, TreeSelection } from '@gedit/tree';
12
- import { asVec, getLineWidth, PathChild } from '@gedit/canvas-draw';
13
- import { generateUuid } from '@gedit/utils';
14
- import { PointDefault, PointSchema } from './path-edit-layer-move-point';
15
- import { SvgPath } from './path-edit-layer-svg-path';
17
+ import {
18
+ PathChild,
19
+ PATH_FUNC_TYPE,
20
+ toFixedValue,
21
+ PathSchema,
22
+ } from '@gedit/canvas-draw';
23
+
24
+ import {
25
+ deepClone,
26
+ Disposable,
27
+ generateUuid,
28
+ PositionSchema,
29
+ } from '@gedit/utils';
30
+ import {
31
+ PointSchema,
32
+ SvgPath,
33
+ getNewPoint,
34
+ setBezierMovePoint,
35
+ PointSelection,
36
+ updatePathNodeData,
37
+ } from './path-edit';
16
38
 
17
- const EDIT_PATH_STATE = 'ADD_PATH';
39
+ export interface PathEditLayerEventContextProps {
40
+ onPointMouseDown: (e: React.MouseEvent, p: PathChild) => void;
41
+ onClosePath: () => void;
42
+ onSceneAddEvent: (
43
+ event: keyof HTMLElementEventMap,
44
+ cb: (e: MouseEvent) => void,
45
+ toDispose?: boolean
46
+ ) => Disposable;
47
+ onBezierPointMouseDown: (
48
+ e: React.MouseEvent,
49
+ p: PathChild,
50
+ key: string
51
+ ) => void;
52
+ onPathAddPoint: (index: number, e: React.MouseEvent) => void;
53
+ }
54
+ export const PathEditLayerEventContext = React.createContext<PathEditLayerEventContextProps>(
55
+ {} as PathEditLayerEventContextProps
56
+ );
18
57
 
19
58
  /**
20
59
  * 动态绘制层
@@ -23,16 +62,25 @@ export class PathEditLayer extends Layer<PlaygroundContext2d> {
23
62
  node = domUtils.createDivWithClass('gedit-path-edit-layer');
24
63
  @entity(EditorStateConfigEntity)
25
64
  protected editorState: EditorStateConfigEntity;
65
+ @entity(SelectorBoxConfigEntity)
66
+ protected selectorConfig!: SelectorBoxConfigEntity;
26
67
  @entity(DocumentEntity) documentEntity: DocumentEntity;
27
- protected hideMovePoint: boolean;
28
68
 
29
69
  currentPathNode?: Editor2dPathNode;
30
- currentPointId?: string;
31
70
  startPos?: PathChild;
32
- selectId?: string;
71
+ protected bezierStartPos?: {
72
+ x: number;
73
+ y: number;
74
+ key: 'left' | 'right';
75
+ path: PathChild;
76
+ };
77
+
78
+ get document(): Editor2dDocument | undefined {
79
+ return this.documentEntity.config.document;
80
+ }
33
81
 
34
82
  get isEnabled(): boolean {
35
- return this.editorState.is(EDIT_PATH_STATE);
83
+ return this.editorState.is(EditorState.EDIT_PATH_STATE.id);
36
84
  }
37
85
 
38
86
  onZoom(scale: number): void {
@@ -41,11 +89,144 @@ export class PathEditLayer extends Layer<PlaygroundContext2d> {
41
89
  });
42
90
  }
43
91
 
92
+ addDispose(disposable: Disposable[]): void {
93
+ this.toDispose.pushAll(disposable);
94
+ }
95
+
96
+ reNodeData(): void {
97
+ this.currentPathNode = undefined;
98
+ this.startPos = undefined;
99
+ this.pathPointSelection!.clearSelection();
100
+ this.pathPointSelection!.selectMode = PathSelectMode.ADD_PATH;
101
+ }
102
+ updateNodeData(): void {
103
+ const nodeData = updatePathNodeData(this.currentPathNode!);
104
+ this.document?.updateNode(this.currentPathNode!, nodeData, true);
105
+ }
106
+
107
+ // 选中的是第一个点
108
+ protected isOnePoint(point?: PathChild): boolean {
109
+ const { selection = [] } = this.pathPointSelection || {};
110
+ const { paths = [] } = this.currentPathNode?.path || {};
111
+ const firstPoint = paths[0];
112
+ return point
113
+ ? point.id === firstPoint.id
114
+ : selection.length === 1 && selection[0].pointId === firstPoint.id;
115
+ }
116
+
117
+ // 选中的是最后一点
118
+ protected isLastPoint(point?: PathChild): boolean {
119
+ const { selection = [] } = this.pathPointSelection || {};
120
+ const { paths = [] } = this.currentPathNode?.path || {};
121
+ const lastPoint = paths[paths.length - 1];
122
+ return point
123
+ ? point.id === lastPoint.id
124
+ : selection.length === 1 && selection[0].pointId === lastPoint.id;
125
+ }
126
+
127
+ protected getPointIsStartOrEnd(point?: PathChild): boolean {
128
+ const { selection = [] } = this.pathPointSelection || {};
129
+ const { paths = [] } = this.currentPathNode?.path || {};
130
+ if (point) {
131
+ return (
132
+ point.id === paths[0].id || point.id === paths[paths.length - 1].id
133
+ );
134
+ }
135
+ return !!(
136
+ // 不是第一个或最后一个
137
+ (
138
+ selection.length === 1 &&
139
+ (selection[0].pointId === paths[0].id ||
140
+ selection[0].pointId === paths[paths.length - 1].id)
141
+ )
142
+ );
143
+ }
144
+
44
145
  onReady(): void {
45
146
  this.toDispose.pushAll([
147
+ this.context.historyService.onHistoryBack(e => {
148
+ this.reNodeData();
149
+ this.pathPointSelection!.selectMode = PathSelectMode.ADD_PATH;
150
+ this.pathPointSelection!.clearSelection();
151
+ this.editorState.toDefaultState();
152
+ }),
153
+ this.editorState.onStateChange(() => {
154
+ /**
155
+ * 1. 不是路径编辑状态时,清空数据
156
+ * 2. 是路径编辑状态时
157
+ * - 1.有选中节点时,设置当前节点为选中节点;选中最后一个点
158
+ * -- 1. 闭合路程径时,切换到区域与贝赛尔点选择模式;
159
+ * -- 2. 非闭合路径时,切换编辑模式;
160
+ * - 2. 未选中时节点时, 设置编辑模式;
161
+ */
162
+ if (!this.isEnabled) {
163
+ this.reNodeData();
164
+ } else {
165
+ if (!this.pathPointSelection) {
166
+ return;
167
+ }
168
+ const selectNodes = this.document?.getSelectedNodes() || [];
169
+ if (
170
+ selectNodes.length &&
171
+ selectNodes[0].displayType === GameObjectBaseType.PATH
172
+ ) {
173
+ // 选中一个 path 路径时;
174
+ this.currentPathNode = selectNodes[0] as Editor2dPathNode;
175
+ const selectionNode = [
176
+ {
177
+ pointId: this.currentPathNode.path.paths[
178
+ this.currentPathNode.path.paths.length - 1
179
+ ].id,
180
+ },
181
+ ];
182
+ this.currentPathNode.path.closed
183
+ ? this.pathPointSelection.enterSelectBezierMode(selectionNode)
184
+ : this.pathPointSelection.enterPathMode(selectionNode);
185
+ } else if (!selectNodes.length) {
186
+ // 未选中时,清空数据
187
+ this.pathPointSelection.selectMode = PathSelectMode.ADD_PATH;
188
+ }
189
+ }
190
+ }),
191
+ // 区域选择点模式
192
+ this.selectorConfig.onConfigChanged(() => {
193
+ if (!this.pathPointSelection) {
194
+ return;
195
+ }
196
+ if (this.pathPointSelection.selectMode !== PathSelectMode.ADD_PATH) {
197
+ const selectBound = this.selectorConfig.toRectangle(
198
+ this.config.finalScale
199
+ );
200
+ const { paths = [], closed } = this.currentPathNode?.path || {};
201
+ const pathSelects: PathPointSelection[] = [];
202
+ paths.forEach(p => {
203
+ const { x: $x, y: $y } = p;
204
+ const x = $x + this.currentPathNode!.position!.x;
205
+ const y = $y + this.currentPathNode!.position!.y;
206
+ const wh = (4 / this.config.finalScale) * 2;
207
+ if (
208
+ x >= selectBound.x && // 左
209
+ x + wh <= selectBound.x + selectBound.width && // 右
210
+ y >= selectBound.y && // 上
211
+ y + wh <= selectBound.y + selectBound.height // 下
212
+ ) {
213
+ pathSelects.push({
214
+ pointId: p.id,
215
+ });
216
+ }
217
+ });
218
+ this.pathPointSelection.selection = pathSelects;
219
+ // 闭合路径的,切到选择加贝赛点模式;
220
+ if (closed) {
221
+ this.pathPointSelection.selectMode = PathSelectMode.SELECT_BEZIER;
222
+ }
223
+ // this.pathPointSelection.enterSelectMode(pathSelects);
224
+ }
225
+ }),
46
226
  // 退出路径编辑
47
- this.editorState.onCancel(EDIT_PATH_STATE, () => {
48
- if (!this.currentPathNode) {
227
+ /* this.editorState.onCancel(EDIT_PATH_STATE, () => {
228
+ console.log('sdfsd');
229
+ /* if (!this.currentPathNode) {
49
230
  return;
50
231
  }
51
232
  // 如果只有一个点代表绘制不成功,则删除
@@ -55,51 +236,143 @@ export class PathEditLayer extends Layer<PlaygroundContext2d> {
55
236
  this.currentPathNode.path.paths.length <= 1)
56
237
  ) {
57
238
  this.document?.delNode(this.currentPathNode);
58
- } */
59
- this.selectId = undefined;
60
- this.currentPathNode = undefined;
61
- }),
62
- this.listenPlaygroundEvent(
63
- 'mousedown',
64
- (e: MouseEvent) => {
65
- // 当前编辑模式为路径编辑
66
- if (this.editorState.is(EDIT_PATH_STATE)) {
67
- this.startPos = this.getPosFromMouseEvent(e);
68
- this.startPos.id = generateUuid();
69
- this.currentPointId = this.startPos.id;
70
- this.drawPath(this.startPos);
71
- }
72
- },
73
- 10
74
- ),
75
- this.listenPlaygroundEvent('mousemove', (e: MouseEvent) => {
76
- if (this.startPos) {
77
- const pos = this.getPosFromMouseEvent(e);
78
- const x1 = this.startPos.x * 2 - pos.x;
79
- const y1 = this.startPos.y * 2 - pos.y;
80
- this.startPos.x2 = pos.x;
81
- this.startPos.y2 = pos.y;
82
- this.startPos.x1 = x1;
83
- this.startPos.y1 = y1;
84
- this.document?.updateNode(this.currentPathNode!, {}, true);
85
239
  }
86
- }),
87
- this.listenPlaygroundEvent('mouseup', (e: MouseEvent) => {
88
- this.startPos = undefined;
240
+ this.reNodeData();
241
+ }), */
242
+ // 画布新增点点击事件;
243
+ this.listenPlaygroundEvent('mousedown', (event: MouseEvent) => {
244
+ // 只在左键点击时绘制
245
+ if (event.buttons !== 1) {
246
+ return;
247
+ }
248
+ this.startDrag(event.clientX, event.clientY, {
249
+ onDragStart: e => {
250
+ // 当前编辑模式为路径编辑
251
+ if (this.editorState.is(EditorState.EDIT_PATH_STATE.id)) {
252
+ const { selectMode } = this.pathPointSelection || {};
253
+ /**
254
+ * 拦截画布点击事件,不处理绘制;
255
+ * 1. 闭合路径时;
256
+ * 2. 带区域选择模式时;
257
+ * 3. 多点选择时;
258
+ */
259
+ if (
260
+ // 判断当前 select 是否 path 路径
261
+ (this.currentPathNode && this.currentPathNode.path.closed) ||
262
+ selectMode !== PathSelectMode.ADD_PATH ||
263
+ (this.pathPointSelection?.selection &&
264
+ this.pathPointSelection.selection.length > 1)
265
+ ) {
266
+ return;
267
+ }
268
+ const { position = { x: 0, y: 0 } } = this.currentPathNode || {};
269
+ this.startPos = {
270
+ x: toFixedValue(
271
+ e.endPos.x / (this.config?.finalScale || 1) - position.x,
272
+ 4
273
+ ),
274
+ y: toFixedValue(
275
+ e.endPos.y / (this.config?.finalScale || 1) - position.y,
276
+ 4
277
+ ),
278
+ }; // this.getPosFromMouseEvent(e);
279
+ this.startPos.id = generateUuid();
280
+ this.startPos.type = PATH_FUNC_TYPE.STRAIGHT;
281
+ this.initPath(this.startPos);
282
+ this.pathPointSelection!.enterPathMode([
283
+ {
284
+ pointId: this.startPos.id,
285
+ },
286
+ ]);
287
+ }
288
+ },
289
+ onDrag: e => {
290
+ if (!this.startPos) {
291
+ return;
292
+ }
293
+ // const pos = this.getPosFromMouseEvent(e);
294
+ const { position = { x: 0, y: 0 }, path } =
295
+ this.currentPathNode || {};
296
+ const x = e.endPos.x / (this.config?.finalScale || 1) - position.x;
297
+ const y = e.endPos.y / (this.config?.finalScale || 1) - position.y;
298
+ const pathNode = path?.paths.find(
299
+ c => c.id === this.startPos?.id
300
+ );
301
+ // 生成贝赛尔点;
302
+ if (
303
+ pathNode &&
304
+ Math.max(
305
+ Math.abs(x - this.startPos.x),
306
+ Math.abs(y - this.startPos.y)
307
+ ) >= 5
308
+ ) {
309
+ const x1 = pathNode.x * 2 - x;
310
+ const y1 = pathNode.y * 2 - y;
311
+ const reverse = this.isOnePoint();
312
+ pathNode.x2 = toFixedValue(reverse ? x1 : x, 4);
313
+ pathNode.y2 = toFixedValue(reverse ? y1 : y, 4);
314
+ pathNode.x1 = toFixedValue(reverse ? x : x1, 4);
315
+ pathNode.y1 = toFixedValue(reverse ? y : y1, 4);
316
+ pathNode.type = PATH_FUNC_TYPE.EQUAL;
317
+ // console.log(this.startPos, path?.paths);
318
+ this.updateNodeData();
319
+ }
320
+ },
321
+ onDragEnd: () => {
322
+ this.startPos = undefined;
323
+ },
324
+ });
89
325
  }),
90
326
  // 图层发生变化时退出模式
91
327
  this.selectionService?.onSelectionChanged(e => {
92
- if (this.editorState.is(EDIT_PATH_STATE)) {
328
+ const { isEnabled, pathPointSelection } = this;
329
+ if (isEnabled) {
330
+ if (!pathPointSelection) {
331
+ return;
332
+ }
333
+ // 选中的是 path 路径时
93
334
  const selectNodes = this.document?.getSelectedNodes() || [];
335
+ if (
336
+ selectNodes.length === 1 &&
337
+ selectNodes[0].displayType === GameObjectBaseType.PATH
338
+ ) {
339
+ // 选中一个 path 路径
340
+ if (
341
+ !this.currentPathNode ||
342
+ selectNodes[0].id !== this.currentPathNode.id
343
+ ) {
344
+ // 选中的不是当前的 path 路径, 切换 currentPathNode;
345
+ this.currentPathNode = selectNodes[0] as Editor2dPathNode;
346
+ const selectionNode = [
347
+ {
348
+ pointId: this.currentPathNode.path.paths[
349
+ this.currentPathNode.path.paths.length - 1
350
+ ].id,
351
+ },
352
+ ];
353
+ // 切换时先选中最后一个点
354
+ this.currentPathNode.path.closed
355
+ ? pathPointSelection.enterSelectBezierMode(selectionNode)
356
+ : pathPointSelection.enterPathMode(selectionNode);
357
+ }
358
+ return;
359
+ }
94
360
  if (
95
361
  selectNodes.length &&
96
362
  (selectNodes[0].displayType !== GameObjectBaseType.PATH ||
97
363
  selectNodes.length !== 1)
98
364
  ) {
365
+ // 选中多个或者选中的不是 path 路径,退出路径编辑,切换到默认模式;
99
366
  this.editorState.toDefaultState();
367
+ return;
100
368
  }
101
369
  }
370
+ this.reNodeData();
102
371
  })!,
372
+ // path point 选择变化;
373
+ this.pathPointSelection!.onSelectionChanged(e => {
374
+ this.draw();
375
+ }),
103
376
  ]);
104
377
  }
105
378
 
@@ -107,164 +380,240 @@ export class PathEditLayer extends Layer<PlaygroundContext2d> {
107
380
  * 创建路径
108
381
  * @param pos - 当前鼠标点击的位置
109
382
  */
110
- drawPath(pos: PointSchema): void {
111
- if (!this.document) return;
112
- // 判断当前 select 是否 path 路径
113
- const selectedNodes = this.document?.getSelectedNodes() || [];
114
- if (selectedNodes.length === 1 && selectedNodes[0].displayType === 'path') {
115
- this.currentPathNode = selectedNodes[0] as Editor2dPathNode;
383
+ initPath(pos: PointSchema): void {
384
+ if (!this.document) {
385
+ return;
116
386
  }
117
- // 创建一个空的路径节点,用于存储数据
387
+ // 没有选中节点,点击画布时,创建一个新节点;
118
388
  if (!this.currentPathNode) {
119
389
  this.currentPathNode = this.document.createDisplayNode<Editor2dPathNode>(
120
- GameObjectBaseType.PATH
390
+ GameObjectBaseType.PATH,
391
+ undefined,
392
+ { x: 0, y: 0 }
121
393
  );
122
394
  this.currentPathNode.selected = true;
395
+ this.context.selection.clearSelection();
123
396
  this.context.selection.addSelection({
124
397
  node: this.currentPathNode as Readonly<SelectableTreeNode>,
125
398
  type: TreeSelection.SelectionType.TOGGLE,
126
399
  });
127
400
  }
128
- this.selectId = this.currentPathNode.id;
129
- this.currentPathNode.path.paths.push(pos);
130
- this.document.updateNode(this.currentPathNode, {}, true);
131
- /* this.document.updateNode(
132
- this.currentPathNode,
133
- {
134
- path: {
135
- ...this.currentPathNode.path,
136
- paths: (this.currentPathNode.path.paths || []).concat(pos),
137
- },
138
- },
139
- true
140
- ); */
141
- }
401
+ this.currentPathNode.path.paths = this.currentPathNode.path.paths || []; // array 会被复用,复制一个,保证不会被修改
402
+ if (this.isOnePoint() && this.currentPathNode.path.paths.length > 1) {
403
+ // 第一帧
404
+ this.currentPathNode.path.paths.unshift(pos);
405
+ } else {
406
+ this.currentPathNode.path.paths.push(pos);
407
+ }
142
408
 
143
- get document(): Editor2dDocument | undefined {
144
- return this.documentEntity.config.document;
409
+ // 创建时坐标是 0, 需要换线更新坐标;
410
+ setTimeout(() => {
411
+ this.updateNodeData();
412
+ });
145
413
  }
146
414
 
147
- protected onBezierPointMouseDown(e: React.MouseEvent, p: PathChild, key: string): void {
148
- e.preventDefault();
149
- e.stopPropagation();
415
+ protected onBezierPointMove(pos: PointSchema): void {
416
+ const { key, path: p } = this.bezierStartPos!;
417
+ const { path = { paths: [] } } = this.currentPathNode || {};
418
+ const pathNode = path.paths.find(c => c.id === p.id);
419
+ if (!pathNode || !this.currentPathNode) {
420
+ return;
421
+ }
422
+ setBezierMovePoint(pathNode, key, pos);
423
+ this.updateNodeData();
150
424
  }
151
-
152
- protected onPointMouseDown(
425
+ protected onBezierPointMouseDown(
153
426
  e: React.MouseEvent,
154
- node: Editor2dPathNode,
155
- p: PathChild
427
+ p: PathChild,
428
+ key: 'left' | 'right'
156
429
  ): void {
157
- const document = this.document!;
430
+ if (e.buttons !== 1) {
431
+ return;
432
+ }
433
+ e.preventDefault();
434
+ e.stopPropagation();
435
+ const pos = this.getPosFromMouseEvent(e);
436
+ this.bezierStartPos = {
437
+ x: pos.x,
438
+ y: pos.y,
439
+ key,
440
+ path: p,
441
+ };
442
+ this.pathPointSelection!.enterSelectBezierMode([
443
+ {
444
+ pointId: p.id,
445
+ bezierKey: key,
446
+ },
447
+ ]);
158
448
  this.startDrag(e.clientX, e.clientY, {
159
- // 拖动
160
- onDrag(dragEvent): void {
161
- const pos = dragEvent.endPos;
162
- const x = pos.x / (this.config?.finalScale || 1);
163
- const y = pos.y / (this.config?.finalScale || 1);
164
- if (typeof p.x1 === 'number') {
165
- p.x1 = p.x1 - p.x + x;
166
- }
167
- if (typeof p.x2 === 'number') {
168
- p.x2 = p.x2 - p.x + x;
449
+ onDrag: e => {
450
+ if (this.bezierStartPos) {
451
+ const { position = { x: 0, y: 0 } } = this.currentPathNode || {};
452
+ const x = e.endPos.x / (this.config?.finalScale || 1) - position.x;
453
+ const y = e.endPos.y / (this.config?.finalScale || 1) - position.y;
454
+ this.onBezierPointMove({ x, y });
169
455
  }
170
- if (typeof p.y1 === 'number') {
171
- p.y1 = p.y1 - p.y + y;
172
- }
173
- if (typeof p.y2 === 'number') {
174
- p.y2 = p.y2 - p.y + y;
175
- }
176
- p.x = x;
177
- p.y = y;
178
-
179
- document.updateNode(node, {}, true);
456
+ },
457
+ onDragEnd: () => {
458
+ this.bezierStartPos = undefined;
180
459
  },
181
460
  });
182
- this.currentPointId = p.id;
183
- e.preventDefault();
184
- e.stopPropagation();
185
- }
186
-
187
- protected onHideMovePoint(): void {
188
- this.hideMovePoint = true;
189
461
  this.draw();
190
462
  }
191
- protected onShowMovePoint(): void {
192
- this.hideMovePoint = false;
193
- this.draw();
463
+
464
+ // 注册 playground 事件
465
+ protected onSceneAddEvent(
466
+ e: keyof HTMLElementEventMap,
467
+ cb: (e: MouseEvent) => void,
468
+ toDispose?: boolean
469
+ ): Disposable {
470
+ const event = this.listenPlaygroundEvent(e, cb);
471
+ if (toDispose) {
472
+ this.toDispose.push(event);
473
+ }
474
+ return event;
194
475
  }
195
476
 
196
- protected getBezierPoint(node?: Editor2dPathNode): JSX.Element[] {
197
- if (!node) {
198
- return [];
477
+ protected onPointMouseDown(
478
+ e: React.MouseEvent,
479
+ currentPoint: PathChild
480
+ ): void {
481
+ if (e.buttons !== 1) {
482
+ return;
199
483
  }
200
- const { paths } = node.path;
201
- const children: JSX.Element[] = [];
202
- const points: PathChild[] = [];
203
- // 在最后点
204
- if (!this.currentPointId) {
205
- points.push(paths[paths.length - 1]);
206
- } else {
207
- const index = paths.findIndex(c => c.id === this.currentPointId);
208
- points.push(...paths.filter((_, i) => i >= index - 1 && i <= index + 1));
484
+ // 如果在 selection 里拖动所有点, 没有则切换当前 selection 里的点
485
+ const { pathPointSelection } = this;
486
+ if (!pathPointSelection) {
487
+ return;
209
488
  }
210
- points.forEach((p, i) => {
211
- if ('x1' in p && 'y1' in p) {
212
- children.push(
213
- <div
214
- key={`x1-line_${i}`}
215
- className="gedit-path-edit-layer-point-bezier-line"
216
- style={{
217
- width: getLineWidth({ x: p.x1 || 0, y: p.y1 || 0 }, p),
218
- transform: `translate(${p.x}px, ${p.y}px) rotate(${
219
- (asVec({ x: p.x1 || 0, y: p.y1 || 0 }, p).ang / Math.PI) * 180
220
- }deg)`,
221
- }}
222
- />,
223
- <div
224
- key={`x1_${i}`}
225
- className="gedit-path-edit-layer-point-bezier"
226
- style={{
227
- transform: `translate(${p.x1}px, ${p.y1}px) scale(${
228
- 1 / this.config.finalScale
229
- })`,
230
- }}
231
- onMouseDown={e => {
232
- this.onBezierPointMouseDown(e, p, '1');
233
- }}
234
- onMouseEnter={this.onHideMovePoint.bind(this)}
235
- onMouseLeave={this.onShowMovePoint.bind(this)}
236
- />
237
- );
489
+ let selection = pathPointSelection?.selection || [];
490
+ const node = this.currentPathNode as Editor2dPathNode;
491
+ const selectionNode = [
492
+ {
493
+ pointId: currentPoint.id,
494
+ },
495
+ ];
496
+ /**
497
+ * 1. 选中一个点时;更新 selection;
498
+ * - 1. 选中的是第一个点或最后一个点时,切换到编辑模式;
499
+ * - 2. 选中的是中间点时,切换到区域选择加贝赛尔点模式;
500
+ * - 3. 关闭路径时,切换到区域选择加贝赛尔点模式;
501
+ * 2. 选中多个点时;
502
+ * - 1. 在选择器里时,不更新 selection,切换到区域选择加贝赛尔点模式;
503
+ * - 2. 不在选择器里时, 更新 selection;
504
+ * -- 1. 选中的是第一个点或最后一个点时,切换到编辑模式;
505
+ * --- 1. 闭合路径时,切换到区域选择加贝赛尔点模式;
506
+ * --- 2. 非闭合路径时,切换到编辑模式;
507
+ * -- 2. 选中的是中间点时,切换到区域选择加贝赛尔点模式;
508
+ */
509
+ const isStartOrEnd = this.getPointIsStartOrEnd(currentPoint);
510
+ if (selection.length === 1) {
511
+ if (isStartOrEnd && !node.path.closed) {
512
+ pathPointSelection!.enterPathMode(selectionNode);
513
+ } else {
514
+ pathPointSelection!.enterSelectBezierMode(selectionNode);
238
515
  }
239
- if ('x2' in p && 'y2' in p) {
240
- children.push(
241
- <div
242
- key={`x2-line_${i}`}
243
- className="gedit-path-edit-layer-point-bezier-line"
244
- style={{
245
- width: getLineWidth({ x: p.x2 || 0, y: p.y2 || 0 }, p),
246
- transform: `translate(${p.x}px, ${p.y}px) rotate(${
247
- (asVec({ x: p.x2 || 0, y: p.y2 || 0 }, p).ang / Math.PI) * 180
248
- }deg)`,
249
- }}
250
- />,
251
- <div
252
- key={`x2_${i}`}
253
- className="gedit-path-edit-layer-point-bezier"
254
- style={{
255
- transform: `translate(${p.x2}px, ${p.y2}px) scale(${
256
- 1 / this.config.finalScale
257
- })`,
258
- }}
259
- onMouseEnter={this.onHideMovePoint.bind(this)}
260
- onMouseLeave={this.onShowMovePoint.bind(this)}
261
- />
262
- );
516
+ } else {
517
+ const inSelection = selection.some(c => c.pointId === currentPoint.id);
518
+ if (inSelection) {
519
+ pathPointSelection.selectMode = PathSelectMode.SELECT_BEZIER;
520
+ } else {
521
+ if ((isStartOrEnd && node.path.closed) || !isStartOrEnd) {
522
+ pathPointSelection.enterSelectBezierMode(selectionNode);
523
+ } else {
524
+ pathPointSelection.enterPathMode(selectionNode);
525
+ }
263
526
  }
527
+ }
528
+ this.config.finalScale;
529
+ const startNodeClone = deepClone(node.path.paths);
530
+
531
+ this.startDrag(e.clientX, e.clientY, {
532
+ // 拖动
533
+ onDrag: dragEvent => {
534
+ selection = pathPointSelection?.selection || [];
535
+ const { position = { x: 0, y: 0 } } = node;
536
+ const selectionNodes = selection
537
+ .map(s => node.path.paths.find(p => p.id === s.pointId))
538
+ .filter(c => c) as PathChild[];
539
+ const delta = selectionNodes.map(c => {
540
+ const point = node.path.paths.find(p => p.id === c.id)!;
541
+ const current = node.path.paths.find(p => p.id === currentPoint.id)!;
542
+ if (point.id === currentPoint.id) {
543
+ return { x: 0, y: 0 };
544
+ }
545
+ return {
546
+ x: point.x - current.x,
547
+ y: point.y - current.y,
548
+ };
549
+ });
550
+ const startNode = selectionNodes.map((s: PathChild) =>
551
+ startNodeClone.find((c: PathChild) => c.id === s.id)
552
+ );
553
+ const x =
554
+ dragEvent.endPos.x / (this.config?.finalScale || 1) - position.x;
555
+ const y =
556
+ dragEvent.endPos.y / (this.config?.finalScale || 1) - position.y;
557
+ selectionNodes.forEach((p: PathChild, i) => {
558
+ const start = startNode[i];
559
+ const d = delta[i];
560
+ const deltaX = x - start.x + d.x;
561
+ const deltaY = y - start.y + d.y;
562
+ if (typeof p.x1 === 'number') {
563
+ p.x1 = toFixedValue(start.x1 + deltaX, 4);
564
+ }
565
+ if (typeof p.x2 === 'number') {
566
+ p.x2 = toFixedValue(start.x2 + deltaX, 4);
567
+ }
568
+ if (typeof p.y1 === 'number') {
569
+ p.y1 = toFixedValue(start.y1 + deltaY, 4);
570
+ }
571
+ if (typeof p.y2 === 'number') {
572
+ p.y2 = toFixedValue(start.y2 + deltaY, 4);
573
+ }
574
+ p.x = toFixedValue(x + d.x, 4);
575
+ p.y = toFixedValue(y + d.y, 4);
576
+ });
577
+ this.updateNodeData();
578
+ },
264
579
  });
265
- return children;
266
- }
267
580
 
581
+ this.draw();
582
+ }
583
+ protected onPathAddPoint(i: number, e: React.MouseEvent): void {
584
+ if (!this.currentPathNode || !this.document) {
585
+ return;
586
+ }
587
+ const { path, position } = this.currentPathNode;
588
+ const { paths = [] } = path;
589
+ const p = this.getPosFromMouseEvent(e);
590
+ const pos = {
591
+ x: p.x - position!.x,
592
+ y: p.y - position!.y,
593
+ };
594
+ const point = paths[i];
595
+ const nextPoint = paths[i + 1] ? paths[i + 1] : paths[0];
596
+ const newPoint = getNewPoint(point, nextPoint, pos);
597
+ paths.splice(i + 1, 0, newPoint);
598
+ this.pathPointSelection!.enterPathMode([
599
+ {
600
+ pointId: newPoint.id,
601
+ },
602
+ ]);
603
+ this.updateNodeData();
604
+ }
605
+ protected onClosePath(): void {
606
+ if (!this.currentPathNode) {
607
+ return;
608
+ }
609
+ this.currentPathNode.path.closed = true;
610
+ this.pathPointSelection!.enterSelectBezierMode([
611
+ {
612
+ pointId: this.currentPathNode.path.paths[0].id,
613
+ },
614
+ ]);
615
+ this.updateNodeData();
616
+ }
268
617
  draw(): JSX.Element {
269
618
  const selectNodes = this.document?.getSelectedNodes() || [];
270
619
  if (
@@ -276,55 +625,53 @@ export class PathEditLayer extends Layer<PlaygroundContext2d> {
276
625
  // this.editorState.toDefaultState();
277
626
  return <></>;
278
627
  }
279
- const node: Editor2dPathNode | undefined = selectNodes[0] as
280
- | Editor2dPathNode
281
- | undefined;
282
-
283
- const allPoints: JSX.Element[] = [];
284
- // 只管当前点相邻的两个点的 bezier;
285
- const bezierPoint: JSX.Element[] = this.getBezierPoint(node);
286
-
287
- const { paths = [] } = node ? node.path : {};
288
- paths.forEach((p, i) => {
289
- allPoints.push(
290
- <div
291
- className="gedit-path-edit-layer-point"
292
- key={`${node?.id}.${i}`}
293
- style={{
294
- transform: `translate(${p.x}px, ${p.y}px) scale(${
295
- 1 / this.config.finalScale
296
- })`,
297
- }}
298
- onMouseDown={e => {
299
- this.onPointMouseDown(e, node!, p);
300
- }}
301
- onMouseEnter={this.onHideMovePoint.bind(this)}
302
- onMouseLeave={this.onShowMovePoint.bind(this)}
303
- />
304
- );
628
+ const {
629
+ path = {} as PathSchema,
630
+ position = { x: 0, y: 0 } as PositionSchema,
631
+ } = this.currentPathNode || ({} as Editor2dPathNode);
632
+ const { paths: p = [], closed = false } = path;
633
+ const paths = [...p].map(c => {
634
+ const item = {
635
+ ...c,
636
+ x: c.x + position.x,
637
+ y: c.y + position.y,
638
+ };
639
+ if (typeof c.x1 === 'number') {
640
+ item.x1 = c.x1 + position.x;
641
+ }
642
+ if (typeof c.x2 === 'number') {
643
+ item.x2 = c.x2 + position.x;
644
+ }
645
+ if (typeof c.y1 === 'number') {
646
+ item.y1 = c.y1 + position.y;
647
+ }
648
+ if (typeof c.y2 === 'number') {
649
+ item.y2 = c.y2 + position.y;
650
+ }
651
+ return item;
305
652
  });
306
653
  return (
307
- <>
308
- {!this.hideMovePoint && (
309
- <PointDefault
310
- getPosFromMouseEvent={this.getPosFromMouseEvent.bind(this)}
311
- paths={paths}
312
- scale={this.config.finalScale}
313
- />
314
- )}
654
+ <PathEditLayerEventContext.Provider
655
+ value={{
656
+ onPointMouseDown: this.onPointMouseDown.bind(this),
657
+ onSceneAddEvent: this.onSceneAddEvent.bind(this),
658
+ onBezierPointMouseDown: this.onBezierPointMouseDown.bind(this),
659
+ onClosePath: this.onClosePath.bind(this),
660
+ onPathAddPoint: this.onPathAddPoint.bind(this),
661
+ }}
662
+ >
315
663
  <SvgPath
664
+ getPosFromMouseEvent={this.getPosFromMouseEvent.bind(this)}
316
665
  width={this.config.config.pageBounds?.width || 300}
317
666
  height={this.config.config.pageBounds?.height || 300}
318
667
  paths={paths}
668
+ closed={closed}
669
+ selection={this.pathPointSelection?.selection as PointSelection[]}
670
+ selectMode={this.pathPointSelection?.selectMode}
671
+ getPointIsStartOrEnd={this.getPointIsStartOrEnd.bind(this)}
319
672
  scale={this.config.finalScale}
320
- pathProps={{
321
- onMouseEnter: this.onHideMovePoint.bind(this),
322
- onMouseLeave: this.onShowMovePoint.bind(this),
323
- }}
324
673
  />
325
- {allPoints}
326
- {bezierPoint}
327
- </>
674
+ </PathEditLayerEventContext.Provider>
328
675
  );
329
676
  }
330
677
  }