Просмотр исходного кода

feat: add ctx.tools.fitView (#674)

* refactor: use-playground-tools fit-view

* feat(free-layout): add ctx.tools.fitView

* feat(fixed-layout): add ctx.tools.fitView
xiamidaxia 5 месяцев назад
Родитель
Сommit
deb4b2afce

+ 2 - 1
apps/demo-fixed-layout/src/hooks/use-editor-props.ts

@@ -226,7 +226,8 @@ export function useEditorProps(
        */
       onAllLayersRendered: (ctx) => {
         setTimeout(() => {
-          ctx.playground.config.fitView(ctx.document.root.bounds.pad(30));
+          // fitView all nodes
+          ctx.tools.fitView();
         }, 10);
         console.log(ctx.document.toString(true)); // Get the document tree
       },

+ 1 - 1
apps/demo-free-layout/src/hooks/use-editor-props.tsx

@@ -268,7 +268,7 @@ export function useEditorProps(
        */
       onAllLayersRendered(ctx) {
         // ctx.tools.autoLayout(); // init auto layout
-        ctx.document.fitView(false); // init fit view
+        ctx.tools.fitView(false);
         console.log('--- Playground rendered ---');
       },
       /**

+ 4 - 2
packages/canvas-engine/core/src/core/layer/config/playground-config-entity.ts

@@ -585,14 +585,16 @@ export class PlaygroundConfigEntity extends ConfigEntity<PlaygroundConfigEntityD
    * @param bounds 目标大小
    * @param easing 是否开启动画,默认开启
    * @param padding 边界空白
+   * @param easingDuration
    */
-  fitView(bounds: Rectangle, easing = true, padding = 0): Promise<void> {
+  fitView(bounds: Rectangle, easing = true, padding = 0, easingDuration = 300): Promise<void> {
     const viewport = this.getViewport(false);
-    const zoom = SizeSchema.fixSize(bounds.pad(padding, padding), viewport);
+    const zoom = SizeSchema.fixSize(bounds.clone().pad(padding, padding), viewport);
     return this.scrollToView({
       bounds,
       zoom,
       easing,
+      easingDuration,
       scrollToCenter: true,
     });
 

+ 3 - 3
packages/canvas-engine/free-layout-core/src/utils/fit-view.ts

@@ -3,18 +3,18 @@
  * SPDX-License-Identifier: MIT
  */
 
-import { type PlaygroundConfigEntity, TransformData } from '@flowgram.ai/core';
 import { Rectangle } from '@flowgram.ai/utils';
+import { type PlaygroundConfigEntity, TransformData } from '@flowgram.ai/core';
 
 import { type WorkflowDocument } from '../workflow-document';
 
 export const fitView = (
   doc: WorkflowDocument,
   playgroundConfig: PlaygroundConfigEntity,
-  easing = true,
+  easing = true
 ) => {
   const bounds = Rectangle.enlarge(
-    doc.getAllNodes().map(node => node.getData<TransformData>(TransformData).bounds),
+    doc.getAllNodes().map((node) => node.getData<TransformData>(TransformData).bounds)
   );
   // 留出 30 像素的边界
   return playgroundConfig.fitView(bounds, easing, 30);

+ 5 - 0
packages/canvas-engine/free-layout-core/src/workflow-document.ts

@@ -89,6 +89,11 @@ export class WorkflowDocument extends FlowDocument {
     return this._loading;
   }
 
+  /**
+   * use `ctx.tools.fitView()` instead
+   * @deprecated
+   * @param easing
+   */
   async fitView(easing?: boolean): Promise<void> {
     return fitView(this, this.playgroundConfig, easing).then(() => {
       this.linesManager.forceUpdate();

+ 16 - 1
packages/client/fixed-layout-editor/src/components/fixed-layout-editor-provider.tsx

@@ -13,10 +13,16 @@ import {
   PlaygroundReactProvider,
   ClipboardService,
   SelectionService,
+  Playground,
 } from '@flowgram.ai/editor';
 
 import { FlowOperationService } from '../types';
-import { createFixedLayoutPreset, FixedLayoutPluginContext, FixedLayoutProps } from '../preset';
+import {
+  createFixedLayoutPreset,
+  FixedLayoutPluginContext,
+  FixedLayoutPluginTools,
+  FixedLayoutProps,
+} from '../preset';
 
 export const FixedLayoutEditorProvider = forwardRef<FixedLayoutPluginContext, FixedLayoutProps>(
   function FixedLayoutEditorProvider(props: FixedLayoutProps, ref) {
@@ -41,6 +47,15 @@ export const FixedLayoutEditorProvider = forwardRef<FixedLayoutPluginContext, Fi
           get history(): HistoryService {
             return container.get<HistoryService>(HistoryService);
           },
+          get tools(): FixedLayoutPluginTools {
+            return {
+              fitView: (easing?: boolean) => {
+                const playgroundConfig = container.get<Playground>(Playground).config;
+                const doc = container.get(FlowDocument);
+                return playgroundConfig.fitView(doc.root.bounds, easing, 30);
+              },
+            };
+          },
         } as FixedLayoutPluginContext),
       []
     );

+ 12 - 30
packages/client/fixed-layout-editor/src/hooks/use-playground-tools.ts

@@ -10,8 +10,6 @@ import { HistoryService } from '@flowgram.ai/history';
 import { FlowDocument, FlowLayoutDefault, FlowNodeRenderData } from '@flowgram.ai/editor';
 import { usePlayground, usePlaygroundContainer, useService } from '@flowgram.ai/editor';
 
-import { fitView } from '../utils/fit-view';
-
 export interface PlaygroundToolsPropsType {
   /**
    * 最大缩放比,默认 2
@@ -21,10 +19,6 @@ export interface PlaygroundToolsPropsType {
    * 最小缩放比,默认 0.25
    */
   minZoom?: number;
-  /**
-   * fitView padding 边距,默认 30
-   */
-  padding?: number;
 }
 
 export interface PlaygroundTools {
@@ -48,7 +42,7 @@ export interface PlaygroundTools {
   /**
    * 自适应视区
    */
-  fitView: (easing?: boolean) => void;
+  fitView: (easing?: boolean, easingDuration?: number, padding?: number) => Promise<void>;
   /**
    * 是否垂直布局
    */
@@ -77,7 +71,7 @@ export interface PlaygroundTools {
 }
 
 export function usePlaygroundTools(props?: PlaygroundToolsPropsType): PlaygroundTools {
-  const { maxZoom, minZoom, padding = 30 } = props || {};
+  const { maxZoom, minZoom } = props || {};
   const playground = usePlayground();
   const container = usePlaygroundContainer();
   const historyService = container.isBound(HistoryService)
@@ -89,7 +83,14 @@ export function usePlaygroundTools(props?: PlaygroundToolsPropsType): Playground
   const [currentLayout, updateLayout] = useState(doc.layout);
   const [canUndo, setCanUndo] = useState(false);
   const [canRedo, setCanRedo] = useState(false);
-
+  // 获取合适视角
+  const handleFitView = useCallback(
+    (easing?: boolean, easingDuration?: number, padding?: number) => {
+      padding = padding || 30;
+      return playground.config.fitView(doc.root.bounds, easing, padding, easingDuration);
+    },
+    [doc, playground]
+  );
   const changeLayout = useCallback(
     (newLayout?: FlowLayoutDefault) => {
       const allNodes = doc.getAllNodes();
@@ -103,12 +104,7 @@ export function usePlaygroundTools(props?: PlaygroundToolsPropsType): Playground
         renderData.node.classList.add('gedit-transition-ease');
       });
       setTimeout(() => {
-        fitView(doc, playground.config, {
-          easingDuration: 300,
-          padding,
-          maxZoom: playground.config.config.maxZoom,
-          minZoom: playground.config.config.minZoom,
-        });
+        handleFitView();
       }, 10);
       setTimeout(() => {
         allNodes.map((node) => {
@@ -119,7 +115,7 @@ export function usePlaygroundTools(props?: PlaygroundToolsPropsType): Playground
       doc.setLayout(newLayout);
       updateLayout(doc.layout);
     },
-    [doc, playground, padding]
+    [doc, playground]
   );
 
   const handleZoomOut = useCallback(
@@ -136,20 +132,6 @@ export function usePlaygroundTools(props?: PlaygroundToolsPropsType): Playground
     [zoom, playground]
   );
 
-  // 获取合适视角
-  const handleFitView = useCallback(
-    (easing?: boolean, easingDuration?: number) => {
-      fitView(doc, playground.config, {
-        easing,
-        easingDuration,
-        padding,
-        maxZoom: playground.config.config.maxZoom,
-        minZoom: playground.config.config.minZoom,
-      });
-    },
-    [doc, playground, padding]
-  );
-
   const handleUpdateZoom = useCallback(
     (value: number, easing?: boolean, easingDuration?: number) => {
       playground.config.updateZoom(value, easing, easingDuration);

+ 4 - 0
packages/client/fixed-layout-editor/src/preset/fixed-layout-props.ts

@@ -24,6 +24,9 @@ import { FlowOperationService } from '../types';
 
 export const FixedLayoutPluginContext = PluginContext;
 
+export interface FixedLayoutPluginTools {
+  fitView: (easing?: boolean) => Promise<void>;
+}
 export interface FixedLayoutPluginContext extends EditorPluginContext {
   document: FlowDocument;
   /**
@@ -33,6 +36,7 @@ export interface FixedLayoutPluginContext extends EditorPluginContext {
   clipboard: ClipboardService;
   selection: SelectionService;
   history: HistoryService;
+  tools: FixedLayoutPluginTools;
 }
 
 /**

+ 0 - 48
packages/client/fixed-layout-editor/src/utils/fit-view.ts

@@ -1,48 +0,0 @@
-/**
- * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
- * SPDX-License-Identifier: MIT
- */
-
-import {
-  type FlowDocument,
-  type PlaygroundConfigEntity,
-  TransformData,
-} from '@flowgram.ai/editor';
-import { Rectangle, SizeSchema } from '@flowgram.ai/utils';
-
-export interface CustomOptions {
-  maxZoom: number;
-  minZoom: number;
-  padding: number;
-  easing?: boolean;
-  easingDuration?: number;
-}
-
-export const fitView = (
-  doc: FlowDocument,
-  playgroundConfig: PlaygroundConfigEntity,
-  customOptions: CustomOptions,
-) => {
-  const { maxZoom, minZoom, padding, easing = true, easingDuration = 50 } = customOptions;
-  const bounds = Rectangle.enlarge(
-    doc.getAllNodes().map(node => node.getData<TransformData>(TransformData).bounds),
-  ).pad(padding, padding); // 留出 padding 像素的边界
-
-  // 这里不用使用缩放后的 viewport
-  const viewport = playgroundConfig.getViewport(false);
-  let zoom = SizeSchema.fixSize(bounds, viewport);
-
-  if (zoom > maxZoom) {
-    zoom = maxZoom;
-  } else if (zoom < minZoom) {
-    zoom = minZoom;
-  }
-
-  return playgroundConfig.scrollToView({
-    bounds,
-    zoom,
-    easing,
-    scrollToCenter: true,
-    easingDuration,
-  });
-};

+ 0 - 6
packages/client/fixed-layout-editor/src/utils/index.ts

@@ -1,6 +0,0 @@
-/**
- * Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
- * SPDX-License-Identifier: MIT
- */
-
-export { fitView } from './fit-view';

+ 8 - 1
packages/client/free-layout-editor/src/components/free-layout-editor-provider.tsx

@@ -6,13 +6,14 @@
 import React, { useMemo, useCallback, forwardRef } from 'react';
 
 import { interfaces } from 'inversify';
-import { WorkflowDocument } from '@flowgram.ai/free-layout-core';
+import { WorkflowDocument, fitView } from '@flowgram.ai/free-layout-core';
 import { HistoryService } from '@flowgram.ai/free-history-plugin';
 import {
   PlaygroundReactProvider,
   createPluginContextDefault,
   ClipboardService,
   SelectionService,
+  Playground,
 } from '@flowgram.ai/editor';
 
 import { WorkflowAutoLayoutTool } from '../tools';
@@ -47,6 +48,12 @@ export const FreeLayoutEditorProvider = forwardRef<FreeLayoutPluginContext, Free
             const autoLayoutTool = container.get<WorkflowAutoLayoutTool>(WorkflowAutoLayoutTool);
             return {
               autoLayout: autoLayoutTool.handle.bind(autoLayoutTool),
+              fitView: (easing?: boolean) =>
+                fitView(
+                  container.get<WorkflowDocument>(WorkflowDocument),
+                  container.get<Playground>(Playground).config,
+                  easing
+                ),
             };
           },
         } as FreeLayoutPluginContext),

+ 1 - 0
packages/client/free-layout-editor/src/preset/free-layout-props.ts

@@ -35,6 +35,7 @@ export const FreeLayoutPluginContext = PluginContext;
 
 export interface FreeLayoutPluginTools {
   autoLayout: (options?: AutoLayoutToolOptions) => Promise<AutoLayoutResetFn>;
+  fitView: (easing?: boolean) => Promise<void>;
 }
 
 export interface FreeLayoutPluginContext extends EditorPluginContext {