@gedit/editor-2d 0.3.13 → 0.3.16

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 (39) hide show
  1. package/lib/browser/editor2d-contribution.d.ts.map +1 -1
  2. package/lib/browser/editor2d-contribution.js +19 -18
  3. package/lib/browser/editor2d-contribution.js.map +1 -1
  4. package/lib/browser/playground/canvas-layer.d.ts +2 -1
  5. package/lib/browser/playground/canvas-layer.d.ts.map +1 -1
  6. package/lib/browser/playground/canvas-layer.js +8 -1
  7. package/lib/browser/playground/canvas-layer.js.map +1 -1
  8. package/lib/browser/playground/path-edit/path-edit-layer-move-point.d.ts.map +1 -1
  9. package/lib/browser/playground/path-edit/path-edit-layer-move-point.js +29 -3
  10. package/lib/browser/playground/path-edit/path-edit-layer-move-point.js.map +1 -1
  11. package/lib/browser/playground/path-edit/path-edit-layer-svg-path.d.ts +9 -2
  12. package/lib/browser/playground/path-edit/path-edit-layer-svg-path.d.ts.map +1 -1
  13. package/lib/browser/playground/path-edit/path-edit-layer-svg-path.js +232 -13
  14. package/lib/browser/playground/path-edit/path-edit-layer-svg-path.js.map +1 -1
  15. package/lib/browser/playground/path-edit/utils.d.ts +15 -3
  16. package/lib/browser/playground/path-edit/utils.d.ts.map +1 -1
  17. package/lib/browser/playground/path-edit/utils.js +63 -13
  18. package/lib/browser/playground/path-edit/utils.js.map +1 -1
  19. package/lib/browser/playground/path-edit-layer.d.ts +46 -3
  20. package/lib/browser/playground/path-edit-layer.d.ts.map +1 -1
  21. package/lib/browser/playground/path-edit-layer.js +469 -110
  22. package/lib/browser/playground/path-edit-layer.js.map +1 -1
  23. package/lib/browser/playground/playground-contribution.d.ts.map +1 -1
  24. package/lib/browser/playground/playground-contribution.js +2 -0
  25. package/lib/browser/playground/playground-contribution.js.map +1 -1
  26. package/lib/browser/utils/bezier.path.utils.d.ts.map +1 -1
  27. package/lib/browser/utils/bezier.path.utils.js +3 -0
  28. package/lib/browser/utils/bezier.path.utils.js.map +1 -1
  29. package/package.json +7 -7
  30. package/src/browser/editor2d-contribution.ts +19 -18
  31. package/src/browser/playground/canvas-layer.ts +6 -1
  32. package/src/browser/playground/path-edit/path-edit-layer-move-point.tsx +38 -4
  33. package/src/browser/playground/path-edit/path-edit-layer-svg-path.tsx +303 -26
  34. package/src/browser/playground/path-edit/utils.tsx +80 -17
  35. package/src/browser/playground/path-edit-layer.tsx +526 -122
  36. package/src/browser/playground/playground-contribution.ts +2 -0
  37. package/src/browser/style/path-edit-layer.less +17 -5
  38. package/src/browser/svg/drag_path.svg +17 -0
  39. package/src/browser/utils/bezier.path.utils.ts +3 -0
@@ -39,6 +39,10 @@ import {
39
39
  transformToData,
40
40
  } from './path-edit';
41
41
 
42
+ import { Command } from '@gedit/command';
43
+ import { isOSX } from '@gedit/application-common';
44
+ import { getParallelPositionByPoint } from '../utils/bezier.path.utils';
45
+
42
46
  export interface PathEditLayerEventContextProps {
43
47
  onPointMouseDown: (e: React.MouseEvent, p: PathChild) => void;
44
48
  onClosePath: () => void;
@@ -53,11 +57,52 @@ export interface PathEditLayerEventContextProps {
53
57
  key: string
54
58
  ) => void;
55
59
  onPathAddPoint: (index: number, e: React.MouseEvent) => void;
60
+ isShiftKey?: boolean;
61
+ isCtrlKey?: boolean;
56
62
  }
57
63
  export const PathEditLayerEventContext = React.createContext<PathEditLayerEventContextProps>(
58
64
  {} as PathEditLayerEventContextProps
59
65
  );
60
66
 
67
+ export namespace Editor2dPathKeyCommands {
68
+ const EDITOR2D_PATH_CATEGORY = 'Editor2dPath';
69
+ export const TAB_PATH: Command = {
70
+ id: 'editor2d.path.tab',
71
+ category: EDITOR2D_PATH_CATEGORY,
72
+ label: '下一个点',
73
+ };
74
+ // export const SHOW_ALL_BEZIER: Command = {
75
+ // id: 'editor2d.path.showAllBezier',
76
+ // category: EDITOR2D_PATH_CATEGORY,
77
+ // label: '显示全部贝塞尔点',
78
+ // };
79
+ export const SELECT_All: Command = {
80
+ id: 'editor2d.path.selectAll',
81
+ category: EDITOR2D_PATH_CATEGORY,
82
+ label: '选择全部',
83
+ };
84
+ export const BEZIER_STRAIGHT: Command = {
85
+ id: 'editor2d.path.bezierStraight',
86
+ category: EDITOR2D_PATH_CATEGORY,
87
+ label: '无贝塞尔',
88
+ };
89
+ export const BEZIER_EQUAL: Command = {
90
+ id: 'editor2d.path.bezierEqual',
91
+ category: EDITOR2D_PATH_CATEGORY,
92
+ label: '左右相等',
93
+ };
94
+ export const BEZIER_BREAK: Command = {
95
+ id: 'editor2d.path.bezierBreak',
96
+ category: EDITOR2D_PATH_CATEGORY,
97
+ label: '左右断开',
98
+ };
99
+ export const BEZIER_UNEQUAL: Command = {
100
+ id: 'editor2d.path.bezierUnequal',
101
+ category: EDITOR2D_PATH_CATEGORY,
102
+ label: '左右不相等',
103
+ };
104
+ }
105
+
61
106
  /**
62
107
  * 动态绘制层
63
108
  */
@@ -75,6 +120,34 @@ export class PathEditLayer extends Layer<PlaygroundContext2d> {
75
120
  currentPathNode?: Editor2dPathNode;
76
121
  startPos?: PathChild;
77
122
 
123
+ // 显示全部贝塞尔点
124
+ protected altKey = false;
125
+
126
+ // 按住 shit 键
127
+ protected shiftKey = false;
128
+
129
+ // 按住 ctrl 或 ⌘ 键;
130
+ protected ctrlKey = false;
131
+
132
+ // 历史记录
133
+ // protected history:
134
+
135
+ protected historyUri?: string;
136
+
137
+ protected _historyIndex = 0;
138
+
139
+ protected noHistory = false;
140
+
141
+ get historyIndex(): number {
142
+ const h = this._historyIndex;
143
+ this._historyIndex++;
144
+ return h;
145
+ }
146
+
147
+ set historyIndex(h: number) {
148
+ this._historyIndex = h;
149
+ }
150
+
78
151
  // 访录当前图层在 timeline 里的监听事件
79
152
  layerDisposeMap = new Map<string, Disposable>();
80
153
  protected bezierStartPos?: {
@@ -105,13 +178,14 @@ export class PathEditLayer extends Layer<PlaygroundContext2d> {
105
178
  }
106
179
 
107
180
  reNodeData(): void {
181
+ this.clearHistory();
108
182
  this.currentPathNode = undefined;
109
183
  this.startPos = undefined;
110
184
  this.pathPointSelection!.clearSelection();
111
185
  this.pathPointSelection!.selectMode = PathSelectMode.ADD_PATH;
112
186
  this.docDispose?.dispose();
113
187
  }
114
- updateNodeData(): void {
188
+ updateNodeData(noHistory?: boolean): void {
115
189
  if (!this.currentPathNode) {
116
190
  return;
117
191
  }
@@ -119,7 +193,69 @@ export class PathEditLayer extends Layer<PlaygroundContext2d> {
119
193
  // console.log('updateNodeData', nodeData);
120
194
  // 直接更新数据,,动画 updateAnimationNode 里会把数据删了;
121
195
  this.currentPathNode.path = nodeData.path;
196
+ this.currentPathNode.selected = true;
122
197
  this.document?.updateNode(this.currentPathNode, nodeData, true);
198
+ this.addHistoryData(noHistory);
199
+ }
200
+ reloadNodeData(content: string): void {
201
+ if (!this.currentPathNode || !this.pathPointSelection) {
202
+ return;
203
+ }
204
+ const path = JSON.parse(content) as PathSchema;
205
+ this.currentPathNode.path = path;
206
+ this.document?.updateNode(this.currentPathNode, { path }, true);
207
+ this.pathPointSelection.selection = path.paths[path.paths.length - 1]
208
+ ? [{ pointId: path.paths[path.paths.length - 1].id }]
209
+ : [];
210
+ if (!path.paths.length) {
211
+ this.pathPointSelection.enterPathMode([]);
212
+ }
213
+ this.draw();
214
+ }
215
+ clearHistory(): void {
216
+ if (this.historyUri) {
217
+ this.context.historyService.clearHistory(this.historyUri);
218
+ this.historyUri = undefined;
219
+ this.context.historyService.switchHistory(this.context.document.uri);
220
+ }
221
+ this.historyIndex = 0;
222
+ }
223
+ addHistoryData(noHistory?: boolean): void {
224
+ const nodePath = this.currentPathNode?.path;
225
+ if (!nodePath || noHistory || !this.historyUri) {
226
+ return;
227
+ }
228
+
229
+ const index = this.historyIndex;
230
+ this.context.historyService.add(this.historyUri, {
231
+ key: index,
232
+ data: {
233
+ content: JSON.stringify(nodePath),
234
+ resource: {
235
+ reload: (content: string) => {
236
+ this.reloadNodeData(content);
237
+ },
238
+ resource: {
239
+ uri: this.historyUri,
240
+ },
241
+ },
242
+ },
243
+ });
244
+ }
245
+ createHistory(): void {
246
+ if (!this.currentPathNode) {
247
+ return;
248
+ }
249
+ this.historyUri = `${this.context.document.uri}?${this.currentPathNode?.id}`;
250
+ if (this.context.historyService.currentUri !== this.historyUri) {
251
+ this.context.historyService.switchHistory(this.historyUri);
252
+ if (
253
+ !this.context.historyService.getHistoryNavigator(this.historyUri)
254
+ ?.length
255
+ ) {
256
+ this.addHistoryData();
257
+ }
258
+ }
123
259
  }
124
260
  // updateNodePathData(): void {
125
261
  // if (!this.currentPathNode) {
@@ -237,14 +373,199 @@ export class PathEditLayer extends Layer<PlaygroundContext2d> {
237
373
  }
238
374
  }
239
375
 
376
+ protected updateCurrentPointBezierType(type: PATH_FUNC_TYPE): void {
377
+ if (!this.currentPathNode || !this.pathPointSelection) {
378
+ return;
379
+ }
380
+ const { selection = [] } = this.pathPointSelection;
381
+ selection.forEach(item => {
382
+ const path = this.currentPathNode!.path;
383
+ const point = path.paths.find(c => c.id === item.pointId);
384
+ if (point) {
385
+ point.type = type;
386
+ switch (type) {
387
+ case PATH_FUNC_TYPE.STRAIGHT:
388
+ delete point.x1;
389
+ delete point.y1;
390
+ delete point.x2;
391
+ delete point.y2;
392
+ break;
393
+ case PATH_FUNC_TYPE.EQUAL:
394
+ case PATH_FUNC_TYPE.UNEQUAL:
395
+ case PATH_FUNC_TYPE.BREAK: {
396
+ const inP1 = 'x1' in point && 'y1' in point;
397
+ const inP2 = 'x2' in point && 'y2' in point;
398
+ if (type === PATH_FUNC_TYPE.BREAK && inP1 && inP2) {
399
+ break;
400
+ }
401
+ const index = path.paths.findIndex(c => c.id === point.id);
402
+ const prevPoint =
403
+ path.paths[index - 1] || path.paths[path.paths.length - 1];
404
+ const nextPoint = path.paths[index + 1] || path.paths[0];
405
+ // 计算平衡线;
406
+ // 点线长度计算两个点的长的距一半;
407
+ const { p1, p2 /* isReverse */ } = getParallelPositionByPoint(
408
+ prevPoint,
409
+ nextPoint,
410
+ point
411
+ );
412
+ if (type === PATH_FUNC_TYPE.BREAK) {
413
+ if (inP1 && !inP2) {
414
+ point.x2 = p2.x;
415
+ point.y2 = p2.y;
416
+ } else if (inP2 && !inP1) {
417
+ point.x1 = p1.x;
418
+ point.y1 = p1.y;
419
+ }
420
+ }
421
+ point.x1 = p1.x;
422
+ point.y1 = p1.y;
423
+ point.x2 = p2.x;
424
+ point.y2 = p2.y;
425
+ break;
426
+ }
427
+
428
+ default:
429
+ break;
430
+ }
431
+ }
432
+ });
433
+ this.updateNodeData();
434
+ }
435
+
436
+ protected registerKeybindings(): Disposable[] {
437
+ const ctrlKey = !isOSX ? 'control' : 'meta';
438
+ return [
439
+ // alt shift 之类在 keybindings 里面不能单个做快捷键
440
+ this.listenGlobalEvent('keydown', e => {
441
+ // alt 显示全部贝赛尔点
442
+ if (this.isEnabled && e.key.toLowerCase() === 'alt') {
443
+ this.altKey = true;
444
+ this.draw();
445
+ }
446
+ if (this.isEnabled && e.key.toLowerCase() === 'shift') {
447
+ this.shiftKey = true;
448
+ this.draw();
449
+ }
450
+
451
+ if (this.isEnabled && e.key.toLowerCase() === ctrlKey) {
452
+ this.ctrlKey = true;
453
+ this.draw();
454
+ }
455
+ }),
456
+ this.listenGlobalEvent('keyup', (e: KeyboardEvent) => {
457
+ if (this.isEnabled && e.key.toLowerCase() === 'alt') {
458
+ this.altKey = false;
459
+ this.draw();
460
+ }
461
+ if (this.isEnabled && e.key.toLowerCase() === 'shift') {
462
+ this.shiftKey = false;
463
+ this.draw();
464
+ }
465
+ if (this.isEnabled && e.key.toLowerCase() === ctrlKey) {
466
+ this.ctrlKey = false;
467
+ this.draw();
468
+ }
469
+ }),
470
+ // tab 切换点
471
+ this.commands.registerCommand(Editor2dPathKeyCommands.TAB_PATH, {
472
+ execute: () => {
473
+ const currentPoint = this.pathPointSelection.selection?.[0]?.pointId;
474
+ const { paths = [] } = this.currentPathNode?.path || {};
475
+ const index = currentPoint
476
+ ? paths.findIndex(c => c.id === currentPoint)
477
+ : -1;
478
+ let nextPoint;
479
+ if (index === -1) {
480
+ nextPoint = paths[0];
481
+ } else {
482
+ nextPoint = paths[index + 1] || paths[0];
483
+ }
484
+ this.selectionChange(nextPoint);
485
+ },
486
+ isEnabled: () => !!this.isEnabled,
487
+ }),
488
+ this.keybindings.registerKeybindings({
489
+ command: Editor2dPathKeyCommands.TAB_PATH.id,
490
+ keybinding: 'tab',
491
+ when: 'editor2dWidgetFocus',
492
+ }),
493
+ // all 全选
494
+ this.commands.registerCommand(Editor2dPathKeyCommands.SELECT_All, {
495
+ execute: () => {
496
+ if (!this.currentPathNode || !this.pathPointSelection) {
497
+ return;
498
+ }
499
+ const { paths = [] } = this.currentPathNode.path;
500
+ const selection = paths.map(c => ({
501
+ pointId: c.id,
502
+ }));
503
+ this.pathPointSelection.enterSelectBezierMode(selection);
504
+ },
505
+ isEnabled: () => !!this.isEnabled,
506
+ }),
507
+ this.keybindings.registerKeybindings({
508
+ command: Editor2dPathKeyCommands.SELECT_All.id,
509
+ keybinding: isOSX ? 'cmd+a' : 'ctrl+a',
510
+ when: 'editor2dWidgetFocus',
511
+ }),
512
+ // 贝赛尔点
513
+ this.commands.registerCommand(Editor2dPathKeyCommands.BEZIER_STRAIGHT, {
514
+ execute: () => {
515
+ this.updateCurrentPointBezierType(PATH_FUNC_TYPE.STRAIGHT);
516
+ },
517
+ isEnabled: () => !!this.isEnabled,
518
+ }),
519
+ this.keybindings.registerKeybindings({
520
+ command: Editor2dPathKeyCommands.BEZIER_STRAIGHT.id,
521
+ keybinding: '1',
522
+ when: 'editor2dWidgetFocus',
523
+ }),
524
+ this.commands.registerCommand(Editor2dPathKeyCommands.BEZIER_EQUAL, {
525
+ execute: () => {
526
+ this.updateCurrentPointBezierType(PATH_FUNC_TYPE.EQUAL);
527
+ },
528
+ isEnabled: () => !!this.isEnabled,
529
+ }),
530
+ this.keybindings.registerKeybindings({
531
+ command: Editor2dPathKeyCommands.BEZIER_EQUAL.id,
532
+ keybinding: '2',
533
+ when: 'editor2dWidgetFocus',
534
+ }),
535
+ this.commands.registerCommand(Editor2dPathKeyCommands.BEZIER_BREAK, {
536
+ execute: () => {
537
+ this.updateCurrentPointBezierType(PATH_FUNC_TYPE.BREAK);
538
+ },
539
+ isEnabled: () => !!this.isEnabled,
540
+ }),
541
+ this.keybindings.registerKeybindings({
542
+ command: Editor2dPathKeyCommands.BEZIER_BREAK.id,
543
+ keybinding: '3',
544
+ when: 'editor2dWidgetFocus',
545
+ }),
546
+ this.commands.registerCommand(Editor2dPathKeyCommands.BEZIER_UNEQUAL, {
547
+ execute: () => {
548
+ this.updateCurrentPointBezierType(PATH_FUNC_TYPE.UNEQUAL);
549
+ },
550
+ isEnabled: () => !!this.isEnabled,
551
+ }),
552
+ this.keybindings.registerKeybindings({
553
+ command: Editor2dPathKeyCommands.BEZIER_UNEQUAL.id,
554
+ keybinding: '4',
555
+ when: 'editor2dWidgetFocus',
556
+ }),
557
+ ];
558
+ }
559
+
240
560
  onReady(): void {
241
561
  this.addDispose([
242
- this.context.historyService.onHistoryBack(e => {
243
- this.reNodeData();
244
- this.pathPointSelection!.selectMode = PathSelectMode.ADD_PATH;
245
- this.pathPointSelection!.clearSelection();
246
- this.editorState.toDefaultState();
247
- }),
562
+ ...this.registerKeybindings(),
563
+ // this.context.historyService.onHistoryBack(e => {
564
+ // this.reNodeData();
565
+ // this.pathPointSelection!.selectMode = PathSelectMode.ADD_PATH;
566
+ // this.pathPointSelection!.clearSelection();
567
+ // this.editorState.toDefaultState();
568
+ // }),
248
569
  this.editorState.onStateChange(e => {
249
570
  /**
250
571
  * 1. 不是路径编辑状态时,清空数据
@@ -262,7 +583,6 @@ export class PathEditLayer extends Layer<PlaygroundContext2d> {
262
583
  return;
263
584
  }
264
585
  const selectNodes = this.document?.getSelectedNodes() || [];
265
-
266
586
  const pathSelectsNodes = selectNodes.filter(
267
587
  c => c.displayType === GameObjectBaseType.PATH
268
588
  );
@@ -272,6 +592,7 @@ export class PathEditLayer extends Layer<PlaygroundContext2d> {
272
592
  this.currentPathNode = deepClone(
273
593
  pathSelectsNodes[0]
274
594
  ) as Editor2dPathNode;
595
+ this.createHistory();
275
596
  if (
276
597
  this.currentPathNode.useAnimationId &&
277
598
  this.document?.timelineService?.currentTimelineData &&
@@ -282,13 +603,17 @@ export class PathEditLayer extends Layer<PlaygroundContext2d> {
282
603
  this.onRegisterTimelineEvent();
283
604
  }
284
605
 
285
- const selectionNode = [
286
- {
287
- pointId: this.currentPathNode.path.paths[
288
- this.currentPathNode.path.paths.length - 1
289
- ].id,
290
- },
291
- ];
606
+ const selectionNode = this.currentPathNode.path.paths[
607
+ this.currentPathNode.path.paths.length - 1
608
+ ]?.id
609
+ ? [
610
+ {
611
+ pointId: this.currentPathNode.path.paths[
612
+ this.currentPathNode.path.paths.length - 1
613
+ ].id,
614
+ },
615
+ ]
616
+ : [];
292
617
  this.currentPathNode.path.closed
293
618
  ? this.pathPointSelection.enterSelectBezierMode(selectionNode)
294
619
  : this.pathPointSelection.enterPathMode(selectionNode);
@@ -413,70 +738,78 @@ export class PathEditLayer extends Layer<PlaygroundContext2d> {
413
738
  }
414
739
  this.reNodeData();
415
740
  }), */
741
+ // 拦掉画布点击事件,失焦再次点击时会触发 widget 的点击事件, 会聚焦到 widget 上
742
+ this.listenPlaygroundEvent('click', (event: MouseEvent) => {
743
+ if (this.isEnabled) {
744
+ event.preventDefault();
745
+ event.stopPropagation();
746
+ this.createHistory();
747
+ }
748
+ }),
416
749
  // 画布新增点点击事件;
417
750
  this.listenPlaygroundEvent('mousedown', (event: MouseEvent) => {
418
751
  // 只在左键点击时绘制
419
- if (event.buttons !== 1) {
752
+ if (event.buttons !== 1 || !this.isEnabled) {
420
753
  return;
421
754
  }
755
+ event.preventDefault();
756
+ event.stopPropagation();
422
757
  this.startDrag(event.clientX, event.clientY, {
423
758
  onDragStart: e => {
424
- // 当前编辑模式为路径编辑
425
- if (this.editorState.is(EditorState.EDIT_PATH_STATE.id)) {
426
- const { selectMode } = this.pathPointSelection || {};
427
- /**
428
- * 拦截画布点击事件,不处理绘制;
429
- * 1. 闭合路径时;
430
- * 2. 带区域选择模式时;
431
- * 3. 多点选择时;
432
- */
433
- if (
434
- // 判断当前 select 是否 path 路径
435
- (this.currentPathNode && this.currentPathNode.path.closed) ||
436
- selectMode !== PathSelectMode.ADD_PATH ||
437
- (this.pathPointSelection?.selection &&
438
- this.pathPointSelection.selection.length > 1)
439
- ) {
440
- return;
441
- }
442
- const {
443
- position = { x: 0, y: 0 },
444
- scale = { x: 1, y: 1 },
445
- rotation = 0,
446
- } = this.currentPathNode || {};
447
- const offset = this.getBoundsOffset();
448
- let x =
449
- e.endPos.x / (this.config?.finalScale || 1) -
450
- position.x -
451
- offset.x;
452
- let y =
453
- e.endPos.y / (this.config?.finalScale || 1) -
454
- position.y -
455
- offset.y;
456
- const center = {
457
- x: -offset.x,
458
- y: -offset.y,
459
- };
460
- const { x: rx, y: ry } = inverseTransformToData(
461
- { x, y },
462
- { scale, rotation },
463
- center
464
- );
465
- x = rx;
466
- y = ry;
467
- this.startPos = {
468
- x,
469
- y,
470
- };
471
- this.startPos.id = generateUuid();
472
- this.startPos.type = PATH_FUNC_TYPE.STRAIGHT;
473
- this.addPath(this.startPos);
474
- this.pathPointSelection!.enterPathMode([
475
- {
476
- pointId: this.startPos.id,
477
- },
478
- ]);
759
+ const { selectMode } = this.pathPointSelection || {};
760
+ /**
761
+ * 拦截画布点击事件,不处理绘制;
762
+ * 1. 闭合路径时;
763
+ * 2. 带区域选择模式时;
764
+ * 3. 多点选择时;
765
+ */
766
+ if (
767
+ // 判断当前 select 是否 path 路径
768
+ (this.currentPathNode && this.currentPathNode.path.closed) ||
769
+ selectMode !== PathSelectMode.ADD_PATH ||
770
+ (this.pathPointSelection?.selection &&
771
+ this.pathPointSelection.selection.length > 1)
772
+ ) {
773
+ return;
479
774
  }
775
+ const {
776
+ position = { x: 0, y: 0 },
777
+ scale = { x: 1, y: 1 },
778
+ rotation = 0,
779
+ } = this.currentPathNode || {};
780
+ const offset = this.getBoundsOffset();
781
+ let x =
782
+ e.endPos.x / (this.config?.finalScale || 1) -
783
+ position.x -
784
+ offset.x;
785
+ let y =
786
+ e.endPos.y / (this.config?.finalScale || 1) -
787
+ position.y -
788
+ offset.y;
789
+ const center = {
790
+ x: -offset.x,
791
+ y: -offset.y,
792
+ };
793
+ const { x: rx, y: ry } = inverseTransformToData(
794
+ { x, y },
795
+ { scale, rotation },
796
+ center
797
+ );
798
+ x = rx;
799
+ y = ry;
800
+ this.startPos = {
801
+ x,
802
+ y,
803
+ };
804
+ this.startPos.id = generateUuid();
805
+ this.startPos.type = PATH_FUNC_TYPE.STRAIGHT;
806
+ this.addPath(this.startPos);
807
+ this.pathPointSelection!.enterPathMode([
808
+ {
809
+ pointId: this.startPos.id,
810
+ },
811
+ ]);
812
+ this.updateNodeData(true);
480
813
  },
481
814
  onDrag: e => {
482
815
  if (!this.startPos) {
@@ -530,15 +863,21 @@ export class PathEditLayer extends Layer<PlaygroundContext2d> {
530
863
  pathNode.y1 = toFixedValue(reverse ? y : y1, 2);
531
864
  pathNode.type = PATH_FUNC_TYPE.EQUAL;
532
865
  // console.log(this.startPos, path?.paths);
533
- this.updateNodeData();
866
+ this.updateNodeData(true);
534
867
  }
535
868
  },
536
869
  onDragEnd: () => {
537
870
  this.startPos = undefined;
871
+ this.updateNodeData();
538
872
  },
539
873
  });
540
874
  }),
541
875
  this.listenPlaygroundEvent('dblclick', (e: MouseEvent) => {
876
+ if (!this.isEnabled) {
877
+ return;
878
+ }
879
+ e.preventDefault();
880
+ e.stopPropagation();
542
881
  // 双击画布时,关闭路径
543
882
  // 1. 在路径编辑状态时,不处理;
544
883
  // 2. 选中的是 path 路径时,不处理,新增点;
@@ -546,7 +885,7 @@ export class PathEditLayer extends Layer<PlaygroundContext2d> {
546
885
  // 4. 右键时,不处理;
547
886
  // 5. 不是路径编辑状态时,不处理;
548
887
  const isExit =
549
- this.editorState.is(EditorState.EDIT_PATH_STATE.id) &&
888
+ this.isEnabled &&
550
889
  this.pathPointSelection.selectMode === PathSelectMode.SELECT_BEZIER;
551
890
  if (isExit) {
552
891
  const playgroundLayer = this.pipelineLayers.find(
@@ -574,6 +913,8 @@ export class PathEditLayer extends Layer<PlaygroundContext2d> {
574
913
  !this.currentPathNode ||
575
914
  selectNodes[0].id !== this.currentPathNode.id
576
915
  ) {
916
+ // 清空上个 path 的 history
917
+ this.clearHistory();
577
918
  // 选中的不是当前的 path 路径, 切换 currentPathNode;
578
919
  this.currentPathNode = selectNodes[0] as Editor2dPathNode;
579
920
  if (
@@ -596,6 +937,7 @@ export class PathEditLayer extends Layer<PlaygroundContext2d> {
596
937
  this.currentPathNode.path.closed
597
938
  ? pathPointSelection.enterSelectBezierMode(selectionNode)
598
939
  : pathPointSelection.enterPathMode(selectionNode);
940
+ this.createHistory();
599
941
  // this.updateNodePathData();
600
942
  }
601
943
  return;
@@ -663,13 +1005,18 @@ export class PathEditLayer extends Layer<PlaygroundContext2d> {
663
1005
  { x: 0, y: 0 }
664
1006
  );
665
1007
  this.currentPathNode.selected = true;
1008
+ this.currentPathNode.path.paths = [];
666
1009
  this.context.selection.clearSelection();
667
- this.context.selection.addSelection({
668
- node: this.currentPathNode as Readonly<SelectableTreeNode>,
669
- type: TreeSelection.SelectionType.TOGGLE,
1010
+ setTimeout(() => {
1011
+ this.context.selection.addSelection({
1012
+ node: this.currentPathNode as Readonly<SelectableTreeNode>,
1013
+ type: TreeSelection.SelectionType.TOGGLE,
1014
+ });
670
1015
  });
671
1016
  }
672
- this.currentPathNode.path.paths = this.currentPathNode.path.paths || []; // array 会被复用,复制一个,保证不会被修改
1017
+ // 把空数组先插入历史;
1018
+ this.createHistory();
1019
+ // this.currentPathNode.path.paths = this.currentPathNode.path.paths || []; // array 会被复用,复制一个,保证不会被修改
673
1020
  if (this.isOnePoint() && this.currentPathNode.path.paths.length > 1) {
674
1021
  // 第一帧
675
1022
  this.currentPathNode.path.paths.unshift(pos);
@@ -683,16 +1030,6 @@ export class PathEditLayer extends Layer<PlaygroundContext2d> {
683
1030
  // });
684
1031
  }
685
1032
 
686
- protected onBezierPointMove(pos: PointSchema): void {
687
- const { key, path: p } = this.bezierStartPos!;
688
- const { path = { paths: [] } } = this.currentPathNode || {};
689
- const pathNode = path.paths.find(c => c.id === p.id);
690
- if (!pathNode || !this.currentPathNode) {
691
- return;
692
- }
693
- setBezierMovePoint(pathNode, key, pos);
694
- this.updateNodeData();
695
- }
696
1033
  protected onBezierPointMouseDown(
697
1034
  e: React.MouseEvent,
698
1035
  p: PathChild,
@@ -723,6 +1060,7 @@ export class PathEditLayer extends Layer<PlaygroundContext2d> {
723
1060
  position = { x: 0, y: 0 },
724
1061
  scale = { x: 1, y: 1 },
725
1062
  rotation = 0,
1063
+ path = { paths: [] },
726
1064
  } = this.currentPathNode || {};
727
1065
  const offset = this.getBoundsOffset();
728
1066
  let x =
@@ -740,7 +1078,17 @@ export class PathEditLayer extends Layer<PlaygroundContext2d> {
740
1078
  );
741
1079
  x = currentX;
742
1080
  y = currentY;
743
- this.onBezierPointMove({ x, y });
1081
+ const pathNode = path.paths.find(c => c.id === p.id);
1082
+ if (!pathNode) {
1083
+ return;
1084
+ }
1085
+ setBezierMovePoint(
1086
+ pathNode,
1087
+ key,
1088
+ { x, y },
1089
+ { shiftKey: this.shiftKey, ctrlKey: this.ctrlKey }
1090
+ );
1091
+ this.updateNodeData(true);
744
1092
  }
745
1093
  },
746
1094
  onDragEnd: () => {
@@ -756,6 +1104,7 @@ export class PathEditLayer extends Layer<PlaygroundContext2d> {
756
1104
  ]);
757
1105
  }
758
1106
  }
1107
+ this.updateNodeData();
759
1108
  },
760
1109
  });
761
1110
  this.draw();
@@ -774,19 +1123,13 @@ export class PathEditLayer extends Layer<PlaygroundContext2d> {
774
1123
  return event;
775
1124
  }
776
1125
 
777
- protected onPointMouseDown(
778
- e: React.MouseEvent,
779
- currentPoint: PathChild
780
- ): void {
781
- if (e.buttons !== 1) {
782
- return;
783
- }
1126
+ protected selectionChange(currentPoint: PathChild): void {
784
1127
  // 如果在 selection 里拖动所有点, 没有则切换当前 selection 里的点
785
1128
  const { pathPointSelection } = this;
786
1129
  if (!pathPointSelection) {
787
1130
  return;
788
1131
  }
789
- let selection = pathPointSelection?.selection || [];
1132
+ const selection = pathPointSelection?.selection || [];
790
1133
  const node = this.currentPathNode as Editor2dPathNode;
791
1134
  const selectionNode = [
792
1135
  {
@@ -825,12 +1168,26 @@ export class PathEditLayer extends Layer<PlaygroundContext2d> {
825
1168
  }
826
1169
  }
827
1170
  }
1171
+ }
1172
+
1173
+ protected onPointMouseDown(
1174
+ e: React.MouseEvent,
1175
+ currentPoint: PathChild
1176
+ ): void {
1177
+ if (e.buttons !== 1) {
1178
+ return;
1179
+ }
1180
+
1181
+ this.selectionChange(currentPoint);
1182
+
1183
+ const node = this.currentPathNode as Editor2dPathNode;
828
1184
  const startNodeClone = deepClone(node.path.paths);
829
1185
 
830
1186
  this.startDrag(e.clientX, e.clientY, {
831
1187
  // 拖动
832
1188
  onDrag: dragEvent => {
833
- selection = pathPointSelection?.selection || [];
1189
+ const { pathPointSelection } = this;
1190
+ const selection = pathPointSelection?.selection || [];
834
1191
  const selectionNodes = selection
835
1192
  .map(s => node.path.paths.find(p => p.id === s.pointId))
836
1193
  .filter(c => c) as PathChild[];
@@ -880,47 +1237,50 @@ export class PathEditLayer extends Layer<PlaygroundContext2d> {
880
1237
  const d = delta[i];
881
1238
  const deltaX = x - start.x + d.x;
882
1239
  const deltaY = y - start.y + d.y;
1240
+ const max = Math.max(Math.abs(deltaX), Math.abs(deltaY));
1241
+
1242
+ const isX = max === Math.abs(deltaX);
1243
+ const isY = max === Math.abs(deltaY);
1244
+ const noX = this.shiftKey && !isX;
1245
+ const noY = this.shiftKey && !isY;
883
1246
  if (typeof p.x1 === 'number') {
884
- p.x1 = toFixedValue(start.x1 + deltaX, 2);
1247
+ p.x1 = noX ? start.x1 : toFixedValue(start.x1 + deltaX, 2);
885
1248
  }
886
1249
  if (typeof p.x2 === 'number') {
887
- p.x2 = toFixedValue(start.x2 + deltaX, 2);
1250
+ p.x2 = noX ? start.x2 : toFixedValue(start.x2 + deltaX, 2);
888
1251
  }
889
1252
  if (typeof p.y1 === 'number') {
890
- p.y1 = toFixedValue(start.y1 + deltaY, 2);
1253
+ p.y1 = noY ? start.y1 : toFixedValue(start.y1 + deltaY, 2);
891
1254
  }
892
1255
  if (typeof p.y2 === 'number') {
893
- p.y2 = toFixedValue(start.y2 + deltaY, 2);
1256
+ p.y2 = noY ? start.y2 : toFixedValue(start.y2 + deltaY, 2);
894
1257
  }
895
- p.x = toFixedValue(x + d.x, 2);
896
- p.y = toFixedValue(y + d.y, 2);
1258
+ p.x = noX ? start.x : toFixedValue(x + d.x, 2);
1259
+ p.y = noY ? start.y : toFixedValue(y + d.y, 2);
897
1260
  });
898
1261
 
1262
+ this.updateNodeData(true);
1263
+ },
1264
+ onDragEnd: () => {
899
1265
  this.updateNodeData();
900
1266
  },
901
1267
  });
902
1268
  this.draw();
903
1269
  }
904
- protected onPathAddPoint(i: number, e: React.MouseEvent): void {
905
- if (!this.currentPathNode || !this.document) {
906
- return;
907
- }
908
- const {
909
- path,
910
- position = { x: 0, y: 0 },
911
- scale = { x: 1, y: 1 },
912
- rotation = 0,
913
- } = this.currentPathNode || {};
914
- const { paths = [] } = path;
915
- const p = this.getPosFromMouseEvent(e);
1270
+ // 画布坐标转换成 path 坐标;
1271
+ protected canvasPosToPathPos(p: {
1272
+ x: number;
1273
+ y: number;
1274
+ }): { x: number; y: number } {
1275
+ const { position = { x: 0, y: 0 }, scale = { x: 1, y: 1 }, rotation = 0 } =
1276
+ this.currentPathNode || {};
916
1277
  const offset = this.getBoundsOffset();
917
- let x = p.x - position.x - offset.x;
918
- let y = p.y - position.y - offset.y;
919
-
920
1278
  const center = {
921
1279
  x: -offset.x,
922
1280
  y: -offset.y,
923
1281
  };
1282
+ let x = p.x - position.x - offset.x;
1283
+ let y = p.y - position.y - offset.y;
924
1284
  const { x: currentX, y: currentY } = inverseTransformToData(
925
1285
  { x, y },
926
1286
  { scale, rotation },
@@ -928,13 +1288,53 @@ export class PathEditLayer extends Layer<PlaygroundContext2d> {
928
1288
  );
929
1289
  x = currentX;
930
1290
  y = currentY;
931
- const pos = {
1291
+ return {
932
1292
  x,
933
1293
  y,
934
1294
  };
1295
+ }
1296
+ protected pathPosToCanvasPos(c: {
1297
+ x: number;
1298
+ y: number;
1299
+ }): {
1300
+ x: number;
1301
+ y: number;
1302
+ } {
1303
+ const { position = { x: 0, y: 0 }, scale = { x: 1, y: 1 }, rotation = 0 } =
1304
+ this.currentPathNode || {};
1305
+ const offset = this.getBoundsOffset();
1306
+ const center = {
1307
+ x: -offset.x,
1308
+ y: -offset.y,
1309
+ };
1310
+
1311
+ const { x, y } = transformToData(
1312
+ { x: c.x, y: c.y },
1313
+ { scale, rotation },
1314
+ center
1315
+ );
1316
+ return {
1317
+ x: x + position.x + offset.x,
1318
+ y: y + position.y + offset.y,
1319
+ };
1320
+ }
1321
+ protected getPathMousePos(e: React.MouseEvent): { x: number; y: number } {
1322
+ const p = this.getPosFromMouseEvent({
1323
+ clientX: e.clientX ?? 0,
1324
+ clientY: e.clientY ?? 0,
1325
+ });
1326
+ return this.canvasPosToPathPos(p);
1327
+ }
1328
+ protected onPathAddPoint(i: number, e: React.MouseEvent): void {
1329
+ if (!this.currentPathNode || !this.document) {
1330
+ return;
1331
+ }
1332
+ const { path } = this.currentPathNode || {};
1333
+ const { paths = [] } = path;
1334
+ const pos = this.getPathMousePos(e);
935
1335
  const point = paths[i];
936
1336
  const nextPoint = paths[i + 1] ? paths[i + 1] : paths[0];
937
- const newPoint = getNewPoint(point, nextPoint, pos);
1337
+ const newPoint = getNewPoint(point, nextPoint, pos, this.shiftKey);
938
1338
  paths.splice(i + 1, 0, newPoint);
939
1339
  this.pathPointSelection!.enterPathMode([
940
1340
  {
@@ -987,8 +1387,6 @@ export class PathEditLayer extends Layer<PlaygroundContext2d> {
987
1387
  const paths = [...p].map(c => {
988
1388
  const item = {
989
1389
  ...c,
990
- x: c.x,
991
- y: c.y,
992
1390
  };
993
1391
 
994
1392
  const { x, y } = transformToData(
@@ -1028,6 +1426,8 @@ export class PathEditLayer extends Layer<PlaygroundContext2d> {
1028
1426
  onBezierPointMouseDown: this.onBezierPointMouseDown.bind(this),
1029
1427
  onClosePath: this.onClosePath.bind(this),
1030
1428
  onPathAddPoint: this.onPathAddPoint.bind(this),
1429
+ isShiftKey: this.shiftKey,
1430
+ isCtrlKey: this.ctrlKey,
1031
1431
  }}
1032
1432
  >
1033
1433
  <SvgPath
@@ -1035,11 +1435,15 @@ export class PathEditLayer extends Layer<PlaygroundContext2d> {
1035
1435
  height={this.config.config.pageBounds?.height || 300}
1036
1436
  getPosFromMouseEvent={this.getPosFromMouseEvent.bind(this)}
1037
1437
  node={this.currentPathNode}
1438
+ showAllBezier={this.altKey}
1038
1439
  paths={paths}
1039
1440
  selection={this.pathPointSelection?.selection as PathPointSelection[]}
1040
1441
  selectMode={this.pathPointSelection?.selectMode}
1041
1442
  getPointIsStartOrEnd={this.getPointIsStartOrEnd.bind(this)}
1443
+ onUpdateNodeData={this.updateNodeData.bind(this)}
1042
1444
  scale={this.config.finalScale}
1445
+ getPathMousePos={this.getPathMousePos.bind(this)}
1446
+ pathPosToCanvasPos={this.pathPosToCanvasPos.bind(this)}
1043
1447
  />
1044
1448
  </PathEditLayerEventContext.Provider>
1045
1449
  );