Sfoglia il codice sorgente

perf(auto-layout): autolayout and fitview excute simultaneously (#840)

* perf(auto-layout): autolayout and fitview excute simultaneously

* fix: test error
Louis Young 3 mesi fa
parent
commit
4a2d168251

+ 1 - 1
packages/client/free-layout-editor/__tests__/use-playground-tools.test.ts

@@ -75,7 +75,7 @@ describe(
       const endPos = doc.getNode('end_0')!.getData(PositionData)!;
       expect(endPos.x - startPos.x).toEqual(800);
       const revert = await toolsData.current.autoLayout();
-      expect(endPos.x - startPos.x).toEqual(880);
+      expect(endPos.x - startPos.x).toEqual(810);
       revert(); // 回滚
       expect(endPos.x - startPos.x).toEqual(800);
     });

+ 3 - 15
packages/client/free-layout-editor/src/tools/auto-layout.ts

@@ -6,16 +6,14 @@
 import { injectable, inject, optional } from 'inversify';
 import { PositionSchema } from '@flowgram.ai/utils';
 import { HistoryService } from '@flowgram.ai/history';
-import { fitView, WorkflowDocument, WorkflowNodeEntity } from '@flowgram.ai/free-layout-core';
+import { WorkflowDocument, WorkflowNodeEntity } from '@flowgram.ai/free-layout-core';
 import { FreeOperationType } from '@flowgram.ai/free-history-plugin';
 import { AutoLayoutService, LayoutOptions } from '@flowgram.ai/free-auto-layout-plugin';
 import { Playground, TransformData } from '@flowgram.ai/editor';
 
 export type AutoLayoutResetFn = () => void;
 
-export type AutoLayoutToolOptions = LayoutOptions & {
-  disableFitView?: boolean;
-};
+export type AutoLayoutToolOptions = LayoutOptions;
 
 /**
  * Auto layout tool - 自动布局工具
@@ -32,13 +30,10 @@ export class WorkflowAutoLayoutTool {
   @inject(HistoryService) @optional() private historyService: HistoryService;
 
   public async handle(options: AutoLayoutToolOptions = {}): Promise<AutoLayoutResetFn> {
-    const { disableFitView = false, ...layoutOptions } = options;
     if (this.playground.config.readonly) {
       return () => {};
     }
-    this.fitView(disableFitView);
-    const resetFn = await this.autoLayout(layoutOptions);
-    this.fitView(disableFitView);
+    const resetFn = await this.autoLayout(options);
     return resetFn;
   }
 
@@ -58,13 +53,6 @@ export class WorkflowAutoLayoutTool {
     });
   }
 
-  private fitView(disable = false): void {
-    if (disable) {
-      return;
-    }
-    fitView(this.document, this.playground.config);
-  }
-
   private getNodePosition(node: WorkflowNodeEntity): PositionSchema {
     const transform = node.getData(TransformData);
     return {

+ 1 - 0
packages/plugins/free-auto-layout-plugin/src/layout/constant.ts

@@ -19,6 +19,7 @@ export const DefaultLayoutConfig: LayoutConfig = {
 
 export const DefaultLayoutOptions: LayoutOptions = {
   getFollowNode: undefined,
+  disableFitView: false,
   enableAnimation: false,
   animationDuration: 300,
 };

+ 1 - 22
packages/plugins/free-auto-layout-plugin/src/layout/layout.ts

@@ -3,9 +3,7 @@
  * SPDX-License-Identifier: MIT
  */
 
-import { Rectangle } from '@flowgram.ai/utils';
-
-import { ILayout, LayoutConfig, LayoutNode, LayoutOptions, LayoutParams, LayoutSize } from './type';
+import { ILayout, LayoutConfig, LayoutOptions, LayoutParams } from './type';
 import { LayoutStore } from './store';
 import { LayoutPosition } from './position';
 import { DagreLayout } from './dagre';
@@ -40,23 +38,4 @@ export class Layout implements ILayout {
     }
     return await this._position.position();
   }
-
-  public get size(): LayoutSize {
-    if (!this._store.initialized) {
-      return Rectangle.EMPTY;
-    }
-    const rects = this._store.nodes.map((node) => this.layoutNodeRect(node));
-    const rect = Rectangle.enlarge(rects);
-    const { padding } = this._store.container.entity.transform;
-    return {
-      width: rect.width + padding.left + padding.right,
-      height: rect.height + padding.top + padding.bottom,
-    };
-  }
-
-  private layoutNodeRect(layoutNode: LayoutNode): Rectangle {
-    const { width, height } = layoutNode.size;
-    const { x, y } = layoutNode.position;
-    return new Rectangle(x, y, width, height);
-  }
 }

+ 1 - 1
packages/plugins/free-auto-layout-plugin/src/layout/type.ts

@@ -26,7 +26,6 @@ export interface ILayout {
   init(params: LayoutParams, options: LayoutOptions): void;
   layout(): void;
   position(): Promise<void>;
-  get size(): LayoutSize;
 }
 
 export interface LayoutSize {
@@ -99,6 +98,7 @@ export interface LayoutOptions {
   getFollowNode?: GetFollowNode;
   enableAnimation?: boolean;
   animationDuration?: number;
+  disableFitView?: boolean;
 }
 
 export interface LayoutConfig {

+ 40 - 3
packages/plugins/free-auto-layout-plugin/src/services.ts

@@ -4,12 +4,14 @@
  */
 
 import { inject, injectable } from 'inversify';
+import { Rectangle } from '@flowgram.ai/utils';
 import {
   WorkflowDocument,
   WorkflowLineEntity,
   WorkflowNodeEntity,
   WorkflowNodeLinesData,
 } from '@flowgram.ai/free-layout-core';
+import { Playground } from '@flowgram.ai/core';
 
 import { AutoLayoutOptions } from './type';
 import { LayoutConfig, LayoutEdge, LayoutNode } from './layout/type';
@@ -18,6 +20,9 @@ import { DefaultLayoutConfig, Layout, type LayoutOptions } from './layout';
 
 @injectable()
 export class AutoLayoutService {
+  @inject(Playground)
+  private playground: Playground;
+
   @inject(WorkflowDocument) private readonly document: WorkflowDocument;
 
   private layoutConfig: LayoutConfig = DefaultLayoutConfig;
@@ -36,7 +41,18 @@ export class AutoLayoutService {
     };
     const root = this.createLayoutNode(this.document.root, options);
     const layouts = await this.layoutNode(root, layoutOptions);
-    await Promise.all(layouts.map((layout) => layout.position()));
+    const rect = this.getLayoutNodeRect(root);
+    const positionPromise = layouts.map((layout) => layout.position());
+    const fitViewPromise = this.fitView(layoutOptions, rect);
+    await Promise.all([...positionPromise, fitViewPromise]);
+  }
+
+  private async fitView(options: LayoutOptions, rect: Rectangle): Promise<void> {
+    if (options.disableFitView === true) {
+      return;
+    }
+    // 留出 30 像素的边界
+    return this.playground.config.fitView(rect, options.enableAnimation, 30);
   }
 
   private async layoutNode(container: LayoutNode, options: LayoutOptions): Promise<Layout[]> {
@@ -51,8 +67,11 @@ export class AutoLayoutService {
     const layout = new Layout(this.layoutConfig);
     layout.init({ container, layoutNodes, layoutEdges }, options);
     layout.layout();
-    const { size } = layout;
-    container.size = size;
+    const rect = this.getLayoutNodeRect(container);
+    container.size = {
+      width: rect.width,
+      height: rect.height,
+    };
     return [...childrenLayouts, layout];
   }
 
@@ -124,4 +143,22 @@ export class AutoLayoutService {
 
     return lines;
   }
+
+  private getLayoutNodeRect(layoutNode: LayoutNode): Rectangle {
+    const rects = layoutNode.layoutNodes.map((node) => this.layoutNodeRect(node));
+    const rect = Rectangle.enlarge(rects);
+    const { padding } = layoutNode.entity.transform;
+    const width = rect.width + padding.left + padding.right;
+    const height = rect.height + padding.top + padding.bottom;
+    const x = rect.x - padding.left;
+    const y = rect.y - padding.top;
+    return new Rectangle(x, y, width, height);
+  }
+
+  private layoutNodeRect(layoutNode: LayoutNode): Rectangle {
+    const { width, height } = layoutNode.size;
+    const x = layoutNode.position.x - width / 2;
+    const y = layoutNode.position.y - height / 2;
+    return new Rectangle(x, y, width, height);
+  }
 }