logicFolw____文档1
# LogicFlow 使用手册
## 📖 简介
LogicFlow 是滴滴开源的一款流程图编辑框架,提供了一系列流程图交互、编辑所必需的功能和灵活的节点自定义、插件等拓展机制。LogicFlow支持前端研发自定义开发各种逻辑编排场景,如流程图、ER图、BPMN流程等。
### 🌟 核心特性
- **🎨 可视化编辑**: 提供完整的流程图编辑能力
- **🔧 高扩展性**: 支持自定义节点、连线、主题
- **⚡ 丰富插件**: 内置多种实用插件
- **📱 移动端支持**: 支持移动端操作
- **💾 数据驱动**: 基于数据驱动的设计理念
## 🚀 快速开始
### 1. 安装
```bash
# npm 安装
npm install @logicflow/core @logicflow/extension
# yarn 安装
yarn add @logicflow/core @logicflow/extension
# pnpm 安装
pnpm add @logicflow/core @logicflow/extension
```
### 2. 引入样式
```javascript
import LogicFlow from '@logicflow/core'
import '@logicflow/core/dist/index.css'
// 可选:引入扩展插件
import { Menu, DndPanel, SelectionSelect, Control, MiniMap } from '@logicflow/extension'
import '@logicflow/extension/dist/index.css'
```
### 3. 创建实例
```javascript
// HTML
<div id="container"></div>
// JavaScript
const lf = new LogicFlow({
container: document.getElementById('container'),
width: 800,
height: 600,
})
// 渲染画布
lf.render()
```
## 📋 基础配置
### 初始化参数
```javascript
const lf = new LogicFlow({
// 必需参数
container: document.getElementById('container'), // 画布容器
// 尺寸配置
width: 1000, // 画布宽度
height: 800, // 画布高度
// 背景配置
background: {
backgroundImage: 'url(./grid.svg)', // 背景图片
backgroundRepeat: 'repeat', // 背景重复方式
backgroundPosition: 'center', // 背景位置
backgroundSize: '20px 20px', // 背景大小
backgroundOpacity: 0.1, // 背景透明度
backgroundColor: '#f7f9ff', // 背景颜色
},
// 网格配置
grid: {
size: 20, // 网格大小
visible: true, // 是否显示网格
type: 'dot', // 网格类型: dot/mesh
config: {
color: '#ababab', // 网格颜色
thickness: 1, // 网格线条粗细
}
},
// 键盘快捷键
keyboard: {
enabled: true, // 是否启用
shortcuts: [
{
keys: ['cmd + c', 'ctrl + c'],
callback: () => {
// 复制操作
},
},
{
keys: ['cmd + v', 'ctrl + v'],
callback: () => {
// 粘贴操作
},
},
],
},
// 样式配置
style: {
rect: {
width: 100,
height: 80,
radius: 5,
},
circle: {
r: 50,
},
nodeText: {
fontSize: 14,
color: '#000000',
},
edgeText: {
fontSize: 12,
color: '#000000',
}
},
// 边配置
edgeType: 'polyline', // 默认边类型: line/polyline/bezier
// 插件配置
plugins: [
Menu, // 右键菜单
DndPanel, // 拖拽面板
SelectionSelect, // 框选
Control, // 控制器
MiniMap, // 小地图
],
// 其他配置
isSilentMode: false, // 静默模式
stopScrollGraph: false, // 禁止滚动画布
stopZoomGraph: false, // 禁止缩放画布
stopMoveGraph: false, // 禁止移动画布
adjustEdge: true, // 允许调整边
adjustEdgeMiddle: false,// 只允许调整边的中点
adjustEdgeStartAndEnd: false, // 只允许调整边的起点和终点
adjustNodePosition: true, // 允许拖拽节点
hideAnchors: false, // 隐藏节点锚点
hoverOutline: true, // 鼠标hover时显示节点轮廓
nodeSelectedOutline: true, // 节点被选中时显示轮廓
edgeSelectedOutline: true, // 边被选中时显示轮廓
multipleSelectKey: 'meta', // 多选按键
snapline: true, // 对齐线
autoExpand: false, // 自动扩展画布
})
```
## 🎨 基础节点
### 1. 内置节点类型
LogicFlow 内置了以下基础节点:
```javascript
// 矩形节点
lf.addNode({
type: 'rect',
x: 100,
y: 100,
text: '矩形节点',
properties: {
width: 120,
height: 80,
}
})
// 圆形节点
lf.addNode({
type: 'circle',
x: 300,
y: 100,
text: '圆形节点',
properties: {
r: 40,
}
})
// 椭圆节点
lf.addNode({
type: 'ellipse',
x: 500,
y: 100,
text: '椭圆节点',
properties: {
rx: 60,
ry: 40,
}
})
// 多边形节点
lf.addNode({
type: 'polygon',
x: 700,
y: 100,
text: '多边形',
properties: {
points: [[50, 0], [100, 25], [100, 75], [50, 100], [0, 75], [0, 25]]
}
})
// 菱形节点
lf.addNode({
type: 'diamond',
x: 900,
y: 100,
text: '菱形节点',
properties: {
rx: 50,
ry: 30,
}
})
// 文本节点
lf.addNode({
type: 'text',
x: 100,
y: 300,
text: '纯文本节点',
})
// HTML节点
lf.addNode({
type: 'html',
x: 300,
y: 300,
text: 'HTML节点',
properties: {
width: 120,
height: 80,
innerHTML: '<div style="background: #f0f0f0; padding: 10px;">自定义HTML</div>'
}
})
```
### 2. 基础连线类型
```javascript
// 直线
lf.addEdge({
type: 'line',
sourceNodeId: 'node1',
targetNodeId: 'node2',
text: '直线连接',
})
// 折线
lf.addEdge({
type: 'polyline',
sourceNodeId: 'node2',
targetNodeId: 'node3',
text: '折线连接',
})
// 贝塞尔曲线
lf.addEdge({
type: 'bezier',
sourceNodeId: 'node3',
targetNodeId: 'node4',
text: '曲线连接',
})
```
## 🔧 自定义节点
### 1. 简单自定义节点
```javascript
import { RectNode, RectNodeModel } from '@logicflow/core'
// 自定义节点模型
class UserTaskModel extends RectNodeModel {
setAttributes() {
this.width = 120
this.height = 80
this.radius = 5
}
getNodeStyle() {
const style = super.getNodeStyle()
style.fill = '#e8f4fd'
style.stroke = '#1890ff'
style.strokeWidth = 2
return style
}
getTextStyle() {
const style = super.getTextStyle()
style.fontSize = 14
style.fill = '#1890ff'
return style
}
}
// 自定义节点视图
class UserTaskView extends RectNode {
getShape() {
const { model } = this.props
const { x, y, width, height, radius } = model
const style = model.getNodeStyle()
return h('g', {}, [
h('rect', {
...style,
x: x - width / 2,
y: y - height / 2,
rx: radius,
ry: radius,
width,
height,
}),
// 添加图标
h('path', {
d: 'M10,10 L20,20 M20,10 L10,20',
stroke: style.stroke,
strokeWidth: 2,
transform: `translate(${x - 10}, ${y - 10})`
})
])
}
}
// 注册自定义节点
lf.register({
type: 'user-task',
view: UserTaskView,
model: UserTaskModel,
})
// 使用自定义节点
lf.addNode({
type: 'user-task',
x: 200,
y: 100,
text: '用户任务',
})
```
### 2. 复杂自定义节点
```javascript
import { h, BaseNode, BaseNodeModel } from '@logicflow/core'
// 复杂自定义节点模型
class FlowChartNodeModel extends BaseNodeModel {
setAttributes() {
this.width = 200
this.height = 120
this.anchorsOffset = [
[0, -60], // 上
[100, 0], // 右
[0, 60], // 下
[-100, 0], // 左
]
}
getNodeStyle() {
return {
fill: '#ffffff',
stroke: '#000000',
strokeWidth: 2,
}
}
getAnchorStyle() {
return {
stroke: '#000000',
fill: '#ffffff',
r: 4,
}
}
// 自定义锚点形状
getDefaultAnchor() {
const { width, height, x, y, id } = this
return [
{
x: x,
y: y - height / 2,
name: 'top',
id: `${id}_top`,
},
{
x: x + width / 2,
y: y,
name: 'right',
id: `${id}_right`,
},
{
x: x,
y: y + height / 2,
name: 'bottom',
id: `${id}_bottom`,
},
{
x: x - width / 2,
y: y,
name: 'left',
id: `${id}_left`,
},
]
}
}
// 复杂自定义节点视图
class FlowChartNodeView extends BaseNode {
getShape() {
const { model } = this.props
const { x, y, width, height } = model
const style = model.getNodeStyle()
return h('g', {}, [
// 主体矩形
h('rect', {
...style,
x: x - width / 2,
y: y - height / 2,
width,
height,
rx: 8,
ry: 8,
}),
// 标题栏
h('rect', {
fill: '#f0f0f0',
stroke: style.stroke,
strokeWidth: 1,
x: x - width / 2,
y: y - height / 2,
width,
height: 30,
rx: 8,
ry: 8,
}),
// 图标
h('circle', {
fill: '#1890ff',
cx: x - width / 2 + 15,
cy: y - height / 2 + 15,
r: 6,
}),
// 操作按钮
h('circle', {
fill: '#ff4d4f',
cx: x + width / 2 - 15,
cy: y - height / 2 + 15,
r: 6,
cursor: 'pointer',
onClick: () => {
// 删除节点
lf.deleteNode(model.id)
}
})
])
}
getText() {
const { model } = this.props
const { x, y, width, height, text } = model
return h('g', {}, [
// 标题
h('text', {
fill: '#000000',
fontSize: 12,
x: x - width / 2 + 30,
y: y - height / 2 + 20,
textAnchor: 'start',
dominantBaseline: 'middle',
}, text.value || ''),
// 描述文本
h('text', {
fill: '#666666',
fontSize: 10,
x: x,
y: y + 10,
textAnchor: 'middle',
dominantBaseline: 'middle',
}, model.properties.description || ''),
])
}
}
// 注册复杂节点
lf.register({
type: 'flowchart-node',
view: FlowChartNodeView,
model: FlowChartNodeModel,
})
```
## 🔗 自定义连线
### 1. 自定义直线
```javascript
import { LineEdge, LineEdgeModel } from '@logicflow/core'
class CustomLineModel extends LineEdgeModel {
setAttributes() {
this.isAnimation = true
}
getEdgeStyle() {
const style = super.getEdgeStyle()
const { properties } = this
// 根据状态设置不同颜色
if (properties.status === 'success') {
style.stroke = '#52c41a'
} else if (properties.status === 'error') {
style.stroke = '#ff4d4f'
} else if (properties.status === 'warning') {
style.stroke = '#faad14'
}
style.strokeWidth = 2
return style
}
getAnimation() {
return {
stroke: this.stroke,
strokeDasharray: '5,5',
strokeDashoffset: '-10',
animationName: 'lf-animate-dash',
animationDuration: '1s',
animationIterationCount: 'infinite',
animationTimingFunction: 'linear',
}
}
// 自定义连线规则
getConnectedSourceRules() {
const rules = super.getConnectedSourceRules()
const gateWayOnlyAsTarget = {
message: '网关节点不能作为起点',
validate: (source) => {
return source.type !== 'gateway'
},
}
rules.push(gateWayOnlyAsTarget)
return rules
}
}
class CustomLineView extends LineEdge {}
lf.register({
type: 'custom-line',
view: CustomLineView,
model: CustomLineModel,
})
```
### 2. 自定义折线
```javascript
import { PolylineEdge, PolylineEdgeModel } from '@logicflow/core'
class CustomPolylineModel extends PolylineEdgeModel {
setAttributes() {
this.radius = 5
this.offset = 20
}
getEdgeStyle() {
const style = super.getEdgeStyle()
style.stroke = '#1890ff'
style.strokeWidth = 2
style.strokeDasharray = '0'
return style
}
getTextPosition() {
// 自定义文本位置
const position = super.getTextPosition()
return {
...position,
y: position.y - 10, // 文本向上偏移10px
}
}
}
class CustomPolylineView extends PolylineEdge {
getShape() {
const { model } = this.props
const { pointsList } = model
const style = model.getEdgeStyle()
// 生成路径
const points = pointsList.map(point => `${point.x},${point.y}`).join(' ')
return h('g', {}, [
h('polyline', {
...style,
points,
fill: 'none',
}),
// 在转折点添加小圆圈
...pointsList.slice(1, -1).map(point =>
h('circle', {
cx: point.x,
cy: point.y,
r: 3,
fill: style.stroke,
})
)
])
}
}
lf.register({
type: 'custom-polyline',
view: CustomPolylineView,
model: CustomPolylineModel,
})
```
## 🎯 事件系统
### 1. 基础事件监听
```javascript
// 节点相关事件
lf.on('node:click', ({ data, e }) => {
console.log('节点被点击', data)
})
lf.on('node:dblclick', ({ data, e }) => {
console.log('节点被双击', data)
})
lf.on('node:mouseenter', ({ data, e }) => {
console.log('鼠标进入节点', data)
})
lf.on('node:mouseleave', ({ data, e }) => {
console.log('鼠标离开节点', data)
})
lf.on('node:contextmenu', ({ data, e }) => {
console.log('节点右键菜单', data)
e.preventDefault() // 阻止浏览器默认右键菜单
})
// 边相关事件
lf.on('edge:click', ({ data, e }) => {
console.log('边被点击', data)
})
lf.on('edge:dblclick', ({ data, e }) => {
console.log('边被双击', data)
})
lf.on('edge:contextmenu', ({ data, e }) => {
console.log('边右键菜单', data)
})
// 画布事件
lf.on('blank:click', ({ e }) => {
console.log('点击空白处')
})
lf.on('blank:contextmenu', ({ e }) => {
console.log('空白处右键菜单')
})
// 选择事件
lf.on('selection:selected', ({ data }) => {
console.log('选中元素', data)
})
lf.on('element:click', ({ data, e }) => {
console.log('点击元素', data)
})
```
### 2. 数据变化事件
```javascript
// 添加元素
lf.on('node:add', ({ data }) => {
console.log('添加节点', data)
})
lf.on('edge:add', ({ data }) => {
console.log('添加边', data)
})
// 删除元素
lf.on('node:delete', ({ data }) => {
console.log('删除节点', data)
})
lf.on('edge:delete', ({ data }) => {
console.log('删除边', data)
})
// 移动元素
lf.on('node:drag', ({ data }) => {
console.log('拖拽节点', data)
})
lf.on('node:drop', ({ data }) => {
console.log('拖拽结束', data)
})
// 文本编辑
lf.on('text:update', ({ data }) => {
console.log('文本更新', data)
})
// 历史记录
lf.on('history:change', ({ data }) => {
console.log('历史记录变化', data)
})
```
### 3. 自定义事件处理
```javascript
// 创建事件管理器
class EventManager {
constructor(lf) {
this.lf = lf
this.init()
}
init() {
this.bindNodeEvents()
this.bindEdgeEvents()
this.bindKeyboardEvents()
}
bindNodeEvents() {
this.lf.on('node:click', ({ data, e }) => {
this.handleNodeClick(data, e)
})
this.lf.on('node:dblclick', ({ data, e }) => {
this.handleNodeDoubleClick(data, e)
})
}
bindEdgeEvents() {
this.lf.on('edge:click', ({ data, e }) => {
this.handleEdgeClick(data, e)
})
}
bindKeyboardEvents() {
document.addEventListener('keydown', (e) => {
if (e.key === 'Delete') {
this.deleteSelected()
}
if (e.ctrlKey && e.key === 's') {
e.preventDefault()
this.save()
}
})
}
handleNodeClick(data, e) {
console.log('处理节点点击', data)
// 显示节点属性面板
this.showNodePanel(data)
}
handleNodeDoubleClick(data, e) {
console.log('处理节点双击', data)
// 进入文本编辑模式
this.lf.editText(data.id)
}
handleEdgeClick(data, e) {
console.log('处理边点击', data)
// 显示边属性面板
this.showEdgePanel(data)
}
deleteSelected() {
const selected = this.lf.getSelectElements()
selected.nodes.forEach(node => {
this.lf.deleteNode(node.id)
})
selected.edges.forEach(edge => {
this.lf.deleteEdge(edge.id)
})
}
save() {
const data = this.lf.getGraphData()
localStorage.setItem('flowData', JSON.stringify(data))
console.log('保存成功')
}
showNodePanel(data) {
// 实现节点属性面板
console.log('显示节点属性面板', data)
}
showEdgePanel(data) {
// 实现边属性面板
console.log('显示边属性面板', data)
}
}
// 使用事件管理器
const eventManager = new EventManager(lf)
```
## 🔌 插件系统
### 1. 内置插件
```javascript
import {
Menu, // 右键菜单
DndPanel, // 拖拽面板
SelectionSelect, // 框选
Control, // 控制面板
MiniMap, // 小地图
Snapshot, // 快照
BpmnElement, // BPMN元素
SelectionSelect, // 框选
Group, // 分组
} from '@logicflow/extension'
const lf = new LogicFlow({
container: document.getElementById('container'),
plugins: [
Menu,
DndPanel,
SelectionSelect,
Control,
MiniMap,
],
})
```
### 2. 插件配置
#### Menu 插件
```javascript
lf.extension.menu.setMenuConfig({
nodeMenu: [
{
text: '删除',
callback(node) {
lf.deleteNode(node.id)
},
},
{
text: '编辑',
callback(node) {
lf.editText(node.id)
},
},
{
text: '复制',
callback(node) {
lf.cloneNode(node.id)
},
},
],
edgeMenu: [
{
text: '删除',
callback(edge) {
lf.deleteEdge(edge.id)
},
},
{
text: '编辑文本',
callback(edge) {
lf.editText(edge.id)
},
},
],
graphMenu: [
{
text: '全选',
callback() {
lf.selectAll()
},
},
{
text: '粘贴',
callback() {
// 粘贴操作
},
},
],
})
```
#### DndPanel 插件
```javascript
lf.extension.dndPanel.setPatternItems([
{
type: 'start',
text: '开始',
label: '开始节点',
icon: 'data:image/png;base64,...',
className: 'import_icon',
},
{
type: 'rect',
text: '系统任务',
label: '系统任务',
icon: 'data:image/png;base64,...',
className: 'import_icon',
},
{
type: 'end',
text: '结束',
label: '结束节点',
icon: 'data:image/png;base64,...',
className: 'import_icon',
},
])
```
#### Control 插件
```javascript
// 启用控制面板功能
lf.extension.control.addItem({
key: 'zoom-in',
iconClass: 'lf-control-zoom-in',
title: '放大',
text: '放大',
onClick: () => {
lf.zoom(true)
},
})
lf.extension.control.addItem({
key: 'zoom-out',
iconClass: 'lf-control-zoom-out',
title: '缩小',
text: '缩小',
onClick: () => {
lf.zoom(false)
},
})
lf.extension.control.addItem({
key: 'reset',
iconClass: 'lf-control-fit',
title: '适应画布',
text: '适应画布',
onClick: () => {
lf.resetZoom()
},
})
```
### 3. 自定义插件
```javascript
class CustomPlugin {
static pluginName = 'customPlugin'
constructor({ lf }) {
this.lf = lf
this.init()
}
init() {
// 插件初始化逻辑
this.bindEvents()
this.createUI()
}
bindEvents() {
this.lf.on('node:click', ({ data }) => {
console.log('自定义插件处理节点点击', data)
})
}
createUI() {
// 创建插件UI
const toolbar = document.createElement('div')
toolbar.className = 'custom-toolbar'
toolbar.innerHTML = `
<button onclick="this.handleSave()">保存</button>
<button onclick="this.handleLoad()">加载</button>
`
document.body.appendChild(toolbar)
}
handleSave() {
const data = this.lf.getGraphData()
localStorage.setItem('customFlow', JSON.stringify(data))
}
handleLoad() {
const data = localStorage.getItem('customFlow')
if (data) {
this.lf.render(JSON.parse(data))
}
}
// 插件销毁
destroy() {
// 清理资源
}
}
// 注册插件
lf.register(CustomPlugin)
```
## 📊 数据管理
### 1. 数据格式
```javascript
// LogicFlow数据格式
const graphData = {
nodes: [
{
id: 'node_1',
type: 'rect',
x: 100,
y: 100,
text: {
x: 100,
y: 100,
value: '节点1'
},
properties: {
// 自定义属性
color: 'red',
size: 'large'
}
}
],
edges: [
{
id: 'edge_1',
type: 'line',
sourceNodeId: 'node_1',
targetNodeId: 'node_2',
startPoint: {
x: 150,
y: 100
},
endPoint: {
x: 250,
y: 100
},
text: {
x: 200,
y: 90,
value: '连线1'
},
properties: {
// 自定义属性
}
}
]
}
```
### 2. 数据操作
```javascript
// 渲染数据
lf.render(graphData)
// 获取数据
const data = lf.getGraphData()
// 获取原始数据(包含位置等完整信息)
const rawData = lf.getGraphRawData()
// 添加节点
const node = lf.addNode({
type: 'rect',
x: 300,
y: 200,
text: '新节点',
properties: {
custom: 'value'
}
})
// 添加边
const edge = lf.addEdge({
type: 'line',
sourceNodeId: 'node_1',
targetNodeId: 'node_2',
text: '连线'
})
// 删除节点
lf.deleteNode('node_1')
// 删除边
lf.deleteEdge('edge_1')
// 更新节点
lf.updateNodeText('node_1', '新文本')
lf.setProperties('node_1', { color: 'blue' })
// 克隆节点
const clonedNode = lf.cloneNode('node_1')
// 选择元素
lf.selectElementById('node_1')
lf.selectAll()
// 获取选中元素
const selected = lf.getSelectElements()
// 清空选择
lf.clearSelectElements()
```
### 3. 数据转换
```javascript
// 转换为其他格式
function convertToCustomFormat(graphData) {
return {
version: '1.0',
nodes: graphData.nodes.map(node => ({
id: node.id,
name: node.text?.value || '',
type: node.type,
position: {
x: node.x,
y: node.y
},
config: node.properties || {}
})),
connections: graphData.edges.map(edge => ({
id: edge.id,
from: edge.sourceNodeId,
to: edge.targetNodeId,
label: edge.text?.value || ''
}))
}
}
// 从其他格式转换
function convertFromCustomFormat(customData) {
return {
nodes: customData.nodes.map(node => ({
id: node.id,
type: node.type,
x: node.position.x,
y: node.position.y,
text: node.name,
properties: node.config
})),
edges: customData.connections.map(connection => ({
id: connection.id,
type: 'line',
sourceNodeId: connection.from,
targetNodeId: connection.to,
text: connection.label
}))
}
}
```
## 🎨 主题定制
### 1. 默认主题配置
```javascript
const theme = {
// 画布背景
background: '#f7f9ff',
// 节点样式
rect: {
width: 100,
height: 80,
radius: 5,
fill: '#ffffff',
stroke: '#000000',
strokeWidth: 2,
},
circle: {
r: 40,
fill: '#ffffff',
stroke: '#000000',
strokeWidth: 2,
},
// 边样式
line: {
stroke: '#000000',
strokeWidth: 2,
fill: 'none',
},
polyline: {
stroke: '#000000',
strokeWidth: 2,
fill: 'none',
},
// 文本样式
nodeText: {
fontSize: 14,
fill: '#000000',
textAnchor: 'middle',
dominantBaseline: 'central',
},
edgeText: {
fontSize: 12,
fill: '#000000',
textAnchor: 'middle',
dominantBaseline: 'central',
background: {
fill: '#ffffff',
stroke: '#000000',
strokeWidth: 1,
radius: 3,
}
},
// 锚点样式
anchor: {
r: 4,
fill: '#ffffff',
stroke: '#000000',
strokeWidth: 2,
},
// 选中样式
nodeSelected: {
stroke: '#1890ff',
strokeWidth: 3,
},
edgeSelected: {
stroke: '#1890ff',
strokeWidth: 3,
},
}
// 应用主题
lf.setTheme(theme)
```
### 2. 动态主题切换
```javascript
// 深色主题
const darkTheme = {
background: '#1e1e1e',
rect: {
fill: '#2d2d30',
stroke: '#3e3e42',
strokeWidth: 1,
},
nodeText: {
fill: '#ffffff',
},
line: {
stroke: '#3e3e42',
},
}
// 亮色主题
const lightTheme = {
background: '#ffffff',
rect: {
fill: '#f8f9fa',
stroke: '#dee2e6',
strokeWidth: 1,
},
nodeText: {
fill: '#212529',
},
line: {
stroke: '#6c757d',
},
}
// 主题切换函数
function switchTheme(isDark) {
const theme = isDark ? darkTheme : lightTheme
lf.setTheme(theme)
}
```
## 📱 移动端适配
### 1. 触摸事件支持
```javascript
const lf = new LogicFlow({
container: document.getElementById('container'),
// 移动端配置
isSilentMode: false,
stopScrollGraph: true, // 禁止滚动,避免与页面滚动冲突
// 触摸相关配置
touchAction: 'none', // 禁用浏览器默认触摸行为
})
// 移动端事件监听
lf.on('node:touchstart', ({ data, e }) => {
console.log('触摸开始', data)
})
lf.on('node:touchmove', ({ data, e }) => {
console.log('触摸移动', data)
})
lf.on('node:touchend', ({ data, e }) => {
console.log('触摸结束', data)
})
```
### 2. 响应式布局
```javascript
// 响应式尺寸调整
function resizeCanvas() {
const container = document.getElementById('container')
const rect = container.getBoundingClientRect()
lf.resize(rect.width, rect.height)
}
// 监听窗口大小变化
window.addEventListener('resize', resizeCanvas)
// 设备方向变化
window.addEventListener('orientationchange', () => {
setTimeout(resizeCanvas, 100)
})
```
### 3. 移动端优化配置
```css
/* 移动端样式优化 */
.lf-container {
/* 禁用文本选择 */
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
/* 禁用触摸高亮 */
-webkit-tap-highlight-color: transparent;
/* 禁用触摸操作 */
touch-action: none;
}
/* 节点在移动端的最小尺寸 */
@media (max-width: 768px) {
.lf-node {
min-width: 44px;
min-height: 44px;
}
}
```
## 🔍 调试和开发
### 1. 调试模式
```javascript
// 开启调试模式
lf.setMode('debug')
// 显示所有事件
const allEvents = [
'node:click', 'node:dblclick', 'node:mousedown', 'node:mouseup',
'node:mouseenter', 'node:mouseleave', 'node:contextmenu',
'edge:click', 'edge:dblclick', 'edge:mouseenter', 'edge:mouseleave',
'blank:click', 'selection:selected', 'text:update'
]
allEvents.forEach(eventName => {
lf.on(eventName, (data) => {
console.log(`[${eventName}]`, data)
})
})
// 性能监控
const performanceMonitor = {
times: new Map(),
start(label) {
this.times.set(label, performance.now())
},
end(label) {
const startTime = this.times.get(label)
if (startTime) {
const duration = performance.now() - startTime
console.log(`${label}: ${duration.toFixed(2)}ms`)
this.times.delete(label)
}
}
}
// 使用示例
performanceMonitor.start('render')
lf.render(data)
performanceMonitor.end('render')
```
### 2. 常见问题解决
```javascript
// 问题1: 节点重叠检测
function checkNodeOverlap() {
const nodes = lf.getGraphData().nodes
const overlaps = []
for (let i = 0; i < nodes.length; i++) {
for (let j = i + 1; j < nodes.length; j++) {
const node1 = nodes[i]
const node2 = nodes[j]
const distance = Math.sqrt(
Math.pow(node1.x - node2.x, 2) +
Math.pow(node1.y - node2.y, 2)
)
if (distance < 100) { // 假设节点最小间距为100
overlaps.push([node1.id, node2.id])
}
}
}
console.log('重叠节点:', overlaps)
return overlaps
}
// 问题2: 连线验证
function validateConnections() {
const { nodes, edges } = lf.getGraphData()
const nodeIds = nodes.map(n => n.id)
const invalidEdges = []
edges.forEach(edge => {
if (!nodeIds.includes(edge.sourceNodeId) ||
!nodeIds.includes(edge.targetNodeId)) {
invalidEdges.push(edge.id)
}
})
console.log('无效连线:', invalidEdges)
return invalidEdges
}
// 问题3: 内存泄漏检测
function checkMemoryLeaks() {
const eventCounts = {}
const originalOn = lf.on
lf.on = function(event, callback) {
eventCounts[event] = (eventCounts[event] || 0) + 1
return originalOn.call(this, event, callback)
}
// 检查事件监听器数量
setInterval(() => {
console.log('事件监听器统计:', eventCounts)
}, 10000)
}
```
## 📚 实战示例
### 1. 流程图编辑器
```javascript
class FlowChartEditor {
constructor(container) {
this.lf = new LogicFlow({
container,
width: 1200,
height: 800,
grid: { size: 20, visible: true },
plugins: [Menu, DndPanel, Control, MiniMap],
})
this.init()
}
init() {
this.registerNodes()
this.bindEvents()
this.initToolbar()
}
registerNodes() {
// 注册开始节点
this.lf.register({
type: 'start',
view: StartNodeView,
model: StartNodeModel,
})
// 注册任务节点
this.lf.register({
type: 'task',
view: TaskNodeView,
model: TaskNodeModel,
})
// 注册结束节点
this.lf.register({
type: 'end',
view: EndNodeView,
model: EndNodeModel,
})
}
bindEvents() {
this.lf.on('node:dblclick', ({ data }) => {
this.editNodeProperties(data)
})
this.lf.on('edge:click', ({ data }) => {
this.editEdgeProperties(data)
})
}
initToolbar() {
const toolbar = document.createElement('div')
toolbar.innerHTML = `
<button onclick="this.save()">保存</button>
<button onclick="this.load()">加载</button>
<button onclick="this.export()">导出</button>
<button onclick="this.validate()">验证</button>
`
document.body.insertBefore(toolbar, document.body.firstChild)
}
save() {
const data = this.lf.getGraphData()
localStorage.setItem('flowchart', JSON.stringify(data))
console.log('保存成功')
}
load() {
const data = localStorage.getItem('flowchart')
if (data) {
this.lf.render(JSON.parse(data))
console.log('加载成功')
}
}
export() {
const data = this.lf.getGraphData()
const blob = new Blob([JSON.stringify(data, null, 2)], {
type: 'application/json'
})
const url = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = url
a.download = 'flowchart.json'
a.click()
}
validate() {
const { nodes, edges } = this.lf.getGraphData()
// 检查是否有开始节点
const startNodes = nodes.filter(n => n.type === 'start')
if (startNodes.length === 0) {
alert('缺少开始节点')
return false
}
// 检查是否有结束节点
const endNodes = nodes.filter(n => n.type === 'end')
if (endNodes.length === 0) {
alert('缺少结束节点')
return false
}
console.log('验证通过')
return true
}
}
// 使用
const editor = new FlowChartEditor(document.getElementById('container'))
```
### 2. BPMN编辑器
```javascript
import { BpmnElement } from '@logicflow/extension'
class BPMNEditor {
constructor(container) {
this.lf = new LogicFlow({
container,
plugins: [BpmnElement, Menu, Control],
})
this.init()
}
init() {
// BPMN元素已通过BpmnElement插件注册
this.bindEvents()
this.setPatterns()
}
setPatterns() {
// 设置BPMN元素拖拽面板
this.lf.extension.dndPanel.setPatternItems([
{
type: 'bpmn:startEvent',
text: '开始事件',
label: '开始事件',
icon: 'data:image/svg+xml;base64,...'
},
{
type: 'bpmn:userTask',
text: '用户任务',
label: '用户任务',
icon: 'data:image/svg+xml;base64,...'
},
{
type: 'bpmn:exclusiveGateway',
text: '排他网关',
label: '排他网关',
icon: 'data:image/svg+xml;base64,...'
},
{
type: 'bpmn:endEvent',
text: '结束事件',
label: '结束事件',
icon: 'data:image/svg+xml;base64,...'
}
])
}
bindEvents() {
this.lf.on('node:dblclick', ({ data }) => {
if (data.type.startsWith('bpmn:')) {
this.editBPMNElement(data)
}
})
}
editBPMNElement(data) {
// 编辑BPMN元素属性
const form = document.createElement('div')
form.innerHTML = `
<h3>编辑${data.type}</h3>
<label>名称: <input type="text" value="${data.text?.value || ''}" /></label>
<label>执行者: <input type="text" value="${data.properties?.assignee || ''}" /></label>
<button onclick="this.saveBPMNElement()">保存</button>
`
document.body.appendChild(form)
}
exportToBPMN() {
// 导出为BPMN XML格式
const data = this.lf.getGraphData()
const xml = this.convertToBPMNXML(data)
console.log(xml)
}
}
```
## 📖 API 参考
### 核心方法
| 方法 | 说明 | 参数 | 返回值 |
|------|------|------|--------|
| `render(data)` | 渲染图形数据 | GraphData | void |
| `getGraphData()` | 获取图形数据 | - | GraphData |
| `addNode(nodeConfig)` | 添加节点 | NodeConfig | NodeData |
| `addEdge(edgeConfig)` | 添加边 | EdgeConfig | EdgeData |
| `deleteNode(nodeId)` | 删除节点 | string | boolean |
| `deleteEdge(edgeId)` | 删除边 | string | boolean |
| `updateText(id, text)` | 更新文本 | string, string | void |
| `on(event, callback)` | 监听事件 | string, function | void |
| `off(event, callback)` | 取消监听 | string, function | void |
| `zoom(isZoomIn)` | 缩放画布 | boolean | void |
| `resetZoom()` | 重置缩放 | - | void |
| `focusOn(id)` | 聚焦元素 | string | void |
### 事件列表
| 事件名 | 说明 | 回调参数 |
|--------|------|----------|
| `node:click` | 节点点击 | `{data, e}` |
| `node:dblclick` | 节点双击 | `{data, e}` |
| `edge:click` | 边点击 | `{data, e}` |
| `blank:click` | 空白点击 | `{e}` |
| `node:add` | 节点添加 | `{data}` |
| `node:delete` | 节点删除 | `{data}` |
| `edge:add` | 边添加 | `{data}` |
| `edge:delete` | 边删除 | `{data}` |
| `text:update` | 文本更新 | `{data}` |
---
*本文档基于LogicFlow v1.2.x版本编写,适用于快速上手和日常开发使用。*

浙公网安备 33010602011771号