npm package: @knotx/plugins-canvas
Description: Canvas Plugin for Knotx
Author: boenfu
Latest version: 0.5.10

# @knotx/plugins-canvas

画布插件，为 KnotX 提供画布容器、变换控制和交互功能。

## 安装

```bash
npm install @knotx/plugins-canvas
```

## 概述

Canvas 插件是 KnotX 的核心插件之一，提供了画布容器、缩放、平移、滚动等基础功能。该插件基于 `react-zoom-pan-pinch` 库实现，为其他插件提供了变换状态和容器信息。

## 实现原理

Canvas 插件通过以下方式实现画布功能：

1. **变换管理**：使用 `react-zoom-pan-pinch` 库管理画布的缩放和平移
2. **事件系统**：提供画布级别的事件监听和处理
3. **边缘滚动**：支持拖拽到边缘时的自动滚动
4. **内容尺寸管理**：跟踪和管理画布内容的尺寸

## 依赖关系

### 核心依赖
- `@knotx/core`：提供基础插件架构
- `@knotx/decorators`：提供装饰器支持
- `@knotx/react-zoom-pan-pinch`：提供缩放和平移功能
- `lodash-es`：提供工具函数
- `rxjs`：提供响应式编程支持

### 可选依赖
- `@knotx/plugins-history`：提供历史记录功能
- `react`：React 框架支持

## API 文档

### 主要类

#### Canvas

画布插件的主要类，继承自 `BasePlugin`。

```typescript
export class Canvas extends BasePlugin<'canvas', CanvasConfig> {
  name = 'canvas' as const
}
```

### 配置选项

#### CanvasConfig

```typescript
export type CanvasConfig = ReactZoomPanPinchProps & {
  defaultLocated?: DefaultLocatedOptions
}
```

继承自 `react-zoom-pan-pinch` 的所有配置选项，并添加了：

- `defaultLocated`：默认定位选项

### 插件数据 (PluginData)

Canvas 插件向其他插件提供以下数据：

```typescript
interface PluginData {
  canvas: {
    transform: CanvasTransformState
    rootElement: Element | null
    contentSize: CanvasContentSize | undefined
    updateContentSize: (contentSize: CanvasContentSize) => void
    addListener: (event: CanvasEventType, listener: CanvasEventListener) => void
    removeListener: (event: CanvasEventType, listener: CanvasEventListener) => void
    edgeScroll: {
      config: EdgeScrollConfig
      setConfig: (config: Partial<EdgeScrollConfig>) => void
    }
  }
}
```

### 插件工具 (PluginTools)

Canvas 插件提供以下工具方法：

```typescript
interface PluginTools {
  canvas: {
    scrollNodeIntoView: (params: ScrollNodeIntoViewOptions) => void
    zoomIn: (params: { step?: number, animationTime?: number }) => void
    zoomOut: (params: { step?: number, animationTime?: number }) => void
    setTransform: (params: {
      positionX: number
      positionY: number
      scale: number
      animationTime?: number
    }) => void
    resetTransform: (params: { animationTime?: number }) => void
  }
}
```

### 类型定义

#### CanvasTransformState

```typescript
interface CanvasTransformState {
  positionX: number
  positionY: number
  scale: number
  previousScale: number
}
```

#### CanvasContentSize

```typescript
interface CanvasContentSize {
  width: number
  height: number
}
```

#### EdgeScrollConfig

```typescript
interface EdgeScrollConfig {
  enabled: boolean
  edgeSize: number
  maxSpeed: number
}
```

## 使用示例

### 基本用法

```typescript
import { Canvas } from '@knotx/plugins-canvas'

const engine = new Engine({
  plugins: [Canvas],
  pluginConfig: {
    canvas: {
      minScale: 0.1,
      maxScale: 3,
      limitToBounds: false,
    },
  },
})
```

### 自定义缩放配置

```typescript
const engine = new Engine({
  plugins: [Canvas],
  pluginConfig: {
    canvas: {
      minScale: 0.2,
      maxScale: 5,
      wheel: {
        step: 0.1,
        smoothStep: 0.006,
      },
      panning: {
        allowLeftClickPan: false,
        allowRightClickPan: true,
        wheelPanning: true,
      },
      zoomAnimation: {
        size: 0.1,
      },
    },
  },
})
```

### 边缘滚动配置

```typescript
const engine = new Engine({
  plugins: [Canvas],
  pluginConfig: {
    canvas: {
      // 基础配置
    },
  },
})

// 启用边缘滚动
engine.callTool('canvas', 'edgeScroll.setConfig', {
  enabled: true,
  edgeSize: 100,
  maxSpeed: 10,
})
```

### 工具方法使用

```typescript
// 放大
engine.callTool('canvas', 'zoomIn', { step: 0.2, animationTime: 300 })

// 缩小
engine.callTool('canvas', 'zoomOut', { step: 0.2, animationTime: 300 })

// 设置变换
engine.callTool('canvas', 'setTransform', {
  positionX: 100,
  positionY: 100,
  scale: 1.5,
  animationTime: 500,
})

// 重置变换
engine.callTool('canvas', 'resetTransform', { animationTime: 300 })

// 滚动节点到视野内
engine.callTool('canvas', 'scrollNodeIntoView', {
  nodeId: 'node-1',
  scale: 1.2,
  block: 'center',
  inline: 'center',
})
```

### 事件监听

```typescript
// 获取画布数据
const canvasData = engine.getPluginData('canvas')

// 添加点击事件监听
function handleClick(event: MouseEvent) {
  console.log('Canvas clicked:', event)
}

canvasData.addListener('click', handleClick)

// 移除监听
canvasData.removeListener('click', handleClick)
```

### 获取变换状态

```typescript
// 在其他插件中获取变换状态
class MyPlugin extends BasePlugin {
  @inject.canvas.transform()
  transform!: CanvasTransformState

  someMethod() {
    console.log('Current scale:', this.transform.scale)
    console.log('Position:', this.transform.positionX, this.transform.positionY)
  }
}
```

## 与其他插件的集成

### 与 History 插件集成

```typescript
import { Canvas } from '@knotx/plugins-canvas'
import { History } from '@knotx/plugins-history'

const engine = new Engine({
  plugins: [Canvas, History],
  pluginConfig: {
    canvas: {
      minScale: 0.1,
      maxScale: 3,
    },
    history: {
      maxHistory: 50,
    },
  },
})
```

### 与 Background 插件集成

```typescript
import { Background } from '@knotx/plugins-background'
import { Canvas } from '@knotx/plugins-canvas'

const engine = new Engine({
  plugins: [Canvas, Background],
  pluginConfig: {
    canvas: {
      minScale: 0.1,
      maxScale: 3,
    },
    background: {
      variant: 'dots',
      color: '#ddd',
    },
  },
})
```

## 文件目录结构

```
packages/plugins-canvas/
├── src/
│   ├── canvas.tsx              # 主要实现文件
│   ├── transform.ts            # 变换计算工具
│   ├── types.ts               # 类型定义
│   └── index.ts               # 导出文件
├── dist/                      # 构建输出目录
├── package.json              # 包配置文件
├── build.config.ts           # 构建配置
├── tsconfig.json             # TypeScript 配置
├── eslint.config.mjs         # ESLint 配置
└── CHANGELOG.md              # 更新日志
```

### 核心文件说明

- **canvas.tsx**：包含 Canvas 插件的主要实现，包括变换管理、事件处理和工具方法
- **transform.ts**：变换计算相关的工具函数
- **types.ts**：TypeScript 类型定义
- **index.ts**：导出 Canvas 类和相关类型定义

## 高级功能

### 自定义变换计算

```typescript
// 使用 transform.ts 中的工具函数
import { calculateTransform } from '@knotx/plugins-canvas/transform'

const result = calculateTransform({
  // 变换参数
})
```

### 动态内容尺寸

```typescript
// 在插件中动态更新内容尺寸
class MyPlugin extends BasePlugin {
  @inject.canvas.updateContentSize()
  updateContentSize!: (size: CanvasContentSize) => void

  updateSize() {
    this.updateContentSize({
      width: 2000,
      height: 1500,
    })
  }
}
```

## 注意事项

1. **性能优化**：Canvas 插件会频繁触发重渲染，建议在需要时才监听变换状态
2. **事件冒泡**：Canvas 事件可能会与其他事件冲突，注意事件处理的优先级
3. **内存管理**：及时移除不需要的事件监听器，避免内存泄漏
4. **React 版本**：确保 React 版本与 `react-zoom-pan-pinch` 兼容

## 许可证

MIT