LogicFlow 学习笔记——3. LogicFlow 基础 节点 Node

news2024/11/17 13:24:42

节点 Node

LogicFlow 内置了一些基础节点,开发者在实际应用场景中,可以基于这些基础节点,定义符合自己业务逻辑的节点。

认识基础节点

LogicFlow是基于svg做的流程图编辑框架,所以我们的节点和连线都是svg基本形状,对LogicFlow节点样式的修改,也就是对svg基本形状的修改。LogicFlow内部存在7中基础节点,分别为:

  1. 矩形 —— rect
  2. 圆形 —— circle
  3. 椭圆 —— ellipse
  4. 多边形 —— polygon
  5. 菱形 —— diamond
  6. 文本 —— text
  7. HTML —— html

如下所示:
在这里插入图片描述
LogicFlow 的基础节点是比较简单的,但是在业务中对节点外观要求可能有各种情况。LogicFlow提供了非常强大的自定义节点功能,可以支持开发者自定义各种节点。下面是基于继承的自定义节点介绍。

自定义节点

LogicFlow是基于继承来实现自定义节点、边。开发者可以继承LogicFlow内置的节点,然后利用面向对象的重写机制。重写节点样式相关的方法,来达到自定义节点样式的效果。
在这里插入图片描述

注意:
LogicFlow推荐在实际应用场景中,所有的节点都使用自定义节点,将节点的type定义为符合项目业务意义的名称。而不是使用圆形、矩形这种仅表示外观的节点。

节点 modelview

在自定义一个节点的时候,我们需要定义节点的modelview。这是因为LogicFlow基于MVVM模式,需要通过重写定义model上获取样式相关的方法和重写view上的getShape来定义更复杂的节点外观。

在项目目录 src/views/Example/LogicFlow/component 下分别创建以下代码:

  1. src/views/Example/LogicFlow/component/CustomCircle/index.ts
    // 从 @logicflow/core 引入相关的类
    import {
      CircleNode, // 基础圆形节点视图
      CircleNodeModel, // 基础圆形节点模型
      GraphModel, // 图形模型,用于管理和操作图形
      NodeConfig // 节点配置类型定义
    } from '@logicflow/core'
    
    // 定义一个自定义的圆形节点模型类,继承自 CircleNodeModel
    class CustomCircleModel extends CircleNodeModel {
      // 构造函数
      constructor(data: NodeConfig, graphModel: GraphModel) {
        // 调整节点的文本配置,确保文本与节点位置保持一致
        data.text = {
          value: data.text as string, // 强制将文本内容视为字符串
          x: data.x, // 设置文本的 x 坐标为节点的 x 坐标
          y: data.y // 设置文本的 y 坐标为节点的 y 坐标
        }
        // 调用父类的构造函数,完成节点模型的初始化
        super(data, graphModel)
      }
    }
    
    // 导出自定义节点的配置,用于在 LogicFlow 中注册和使用这种节点
    export default {
      type: 'CustomCircle', // 自定义节点类型的唯一标识
      view: CircleNode, // 视图使用基础的圆形节点视图
      model: CustomCircleModel // 模型使用上面定义的自定义圆形节点模型
    }
    
  2. src/views/Example/LogicFlow/component/CustomDiamond/index.ts
    // 从 @logicflow/core 引入必要的类
    import {
      DiamondNode, // 基础菱形节点的视图
      DiamondNodeModel, // 基础菱形节点的模型
      GraphModel, // 图形模型,用于图的管理和操作
      NodeConfig // 节点配置类型定义
    } from '@logicflow/core'
    
    // 创建一个自定义的菱形节点模型类,继承自 DiamondNodeModel
    class CustomDiamondModel extends DiamondNodeModel {
      // 构造函数
      constructor(data: NodeConfig, graphModel: GraphModel) {
        // 在调用父类构造函数前,初始化文本属性
        data.text = {
          value: data.text as string, // 强制类型转换,确保文本值为字符串
          x: data.x, // 设置文本的 x 坐标与节点 x 坐标一致
          y: data.y + 40 // 设置文本的 y 坐标,使其在节点下方40像素处
        }
        // 调用父类的构造函数进行初始化
        super(data, graphModel)
    
        // 设置自定义的半径属性,控制菱形的宽高
        this.rx = 50 // 水平方向的半径(即半宽)
        this.ry = 20 // 垂直方向的半径(即半高)
      }
    }
    
    // 导出自定义节点的配置,使其可以在 LogicFlow 中使用
    export default {
      type: 'CustomDiamond', // 自定义节点类型的唯一标识
      view: DiamondNode, // 视图使用基础的菱形节点视图
      model: CustomDiamondModel // 模型使用上面定义的自定义菱形节点模型
    }
    
  3. src/views/Example/LogicFlow/component/CustomEllipse/index.ts
    // 从 @logicflow/core 库中导入所需的类
    import {
      EllipseNode,         // 基础椭圆形节点的视图类
      EllipseNodeModel,    // 基础椭圆形节点的模型类
      GraphModel,          // 用于管理图的整体模型
      NodeConfig           // 节点配置接口
    } from '@logicflow/core'
    
    // 定义一个自定义的椭圆形节点模型类,继承自 EllipseNodeModel
    class CustomEllipseModel extends EllipseNodeModel {
      // 构造函数
      constructor(data: NodeConfig, graphModel: GraphModel) {
        // 检查文本数据是否存在且为字符串,如果是,则设置文本属性
        if (data.text && typeof data.text === 'string') {
          data.text = {
            value: data.text,   // 设置文本值
            x: data.x,          // 设置文本的 x 坐标为节点的 x 坐标
            y: data.y + 40      // 设置文本的 y 坐标为节点的 y 坐标向下偏移40像素
          }
        }
        // 调用父类构造函数进行初始化
        super(data, graphModel)
    
        // 设置自定义的椭圆大小属性
        this.rx = 50  // 椭圆的水平半径
        this.ry = 20  // 椭圆的垂直半径
      }
    }
    
    // 导出自定义节点的配置
    export default {
      type: 'CustomEllipse',   // 自定义节点的类型标识
      view: EllipseNode,       // 使用基础椭圆形节点的视图
      model: CustomEllipseModel // 使用定义的自定义椭圆形节点模型
    }
    
  4. src/views/Example/LogicFlow/component/CustomPolygon/index.ts
    // 从 @logicflow/core 库导入所需的类和类型
    import { PointTuple, PolygonNode, PolygonNodeModel } from '@logicflow/core'
    
    // 定义一个自定义多边形节点模型类,继承自 PolygonNodeModel
    class CustomPolygonModel extends PolygonNodeModel {
      // 设置多边形节点的属性
      setAttributes() {
        const width = 100  // 多边形的宽度
        const height = 100 // 多边形的高度
        const x = 50       // 多边形中心的 x 坐标
        const y = 50       // 多边形中心的 y 坐标
    
        // 计算多边形的顶点列表,形成一个八边形
        const pointList: PointTuple[] = [
          [x - 0.205 * width, y - 0.5 * height],   // 左上顶点
          [x + 0.205 * width, y - 0.5 * height],   // 右上顶点
          [x + 0.5 * width, y - 0.205 * height],   // 右上角拐点
          [x + 0.5 * width, y + 0.205 * height],   // 右下角拐点
          [x + 0.205 * width, y + 0.5 * height],   // 右下顶点
          [x - 0.205 * width, y + 0.5 * height],   // 左下顶点
          [x - 0.5 * width, y + 0.205 * height],   // 左下角拐点
          [x - 0.5 * width, y - 0.205 * height]    // 左上角拐点
        ]
        // 将计算出的顶点赋值给 points 属性
        this.points = pointList
      }
    
      // 重写获取文本样式的方法
      getTextStyle() {
        const { refX = 0, refY = 0 } = this.properties // 从属性中提取参考点
        const style = super.getTextStyle() // 调用父类方法获取基本文本样式
    
        // 返回新的文本样式,通过矩阵变换调整文本位置
        return {
          ...style,
          transform: `matrix(1 0 0 1 ${refX} ${refY + 70})` // 在 y 方向上向下移动 70 像素
        }
      }
    }
    
    // 导出自定义多边形节点的配置
    export default {
      type: 'CustomPolygon',  // 自定义节点类型的唯一标识
      view: PolygonNode,      // 视图使用基础的多边形节点视图
      model: CustomPolygonModel // 模型使用上面定义的自定义多边形节点模型
    }
    
  5. src/views/Example/LogicFlow/component/CustomRect/index.ts
    // 从 @logicflow/core 库导入所需的类
    import { RectNode, RectNodeModel } from '@logicflow/core'
    // 导入文本节点的主题类型定义
    import { TextNodeTheme } from '@logicflow/core/types/constant/DefaultTheme'
    
    // 定义一个自定义的矩形节点视图类,继承自基础的矩形节点视图
    class CustomRectNode extends RectNode {}
    
    // 定义一个自定义的矩形节点模型类,继承自基础的矩形节点模型
    class CustomRectModel extends RectNodeModel {
      // 设置矩形节点的属性
      setAttributes() {
        this.width = 200    // 设置矩形的宽度为 200
        this.height = 80    // 设置矩形的高度为 80
        this.radius = 50    // 设置矩形的边角半径为 50,使边角为圆角
      }
    
      // 重写获取文本样式的方法
      getTextStyle(): TextNodeTheme {
        const { refX = 0, refY = 0 } = this.properties as Record<string, any>  // 从属性中提取参考点
        const style = super.getTextStyle()  // 调用父类方法获取基本文本样式
    
        // 返回新的文本样式,通过矩阵变换调整文本位置
        return {
          ...style,
          transform: `matrix(1 0 0 1 ${refX} ${refY + 60})` // 在 y 方向上向下移动 60 像素
        }
      }
    
      // 重写获取节点样式的方法
      getNodeStyle() {
        const style = super.getNodeStyle()  // 调用父类方法获取基本节点样式
        style.stroke = 'blue'  // 设置节点边框颜色为蓝色
        return style
      }
    }
    
    // 导出自定义矩形节点的配置
    export default {
      type: 'CustomRect',       // 自定义节点类型的唯一标识
      view: CustomRectNode,     // 视图使用定义的自定义矩形节点视图
      model: CustomRectModel    // 模型使用上面定义的自定义矩形节点模型
    }
    

上面的 5 个代码分别定义了 5 个自定义节点,节点类型为:CustomCircleCustomDiamondCustomEllipseCustomPolygonCustomRect,之后编写 src/views/Example/LogicFlow/Example06.vue,内容如下:

<script setup lang="ts">
// 导入 LogicFlow 核心库和相关样式
import LogicFlow from '@logicflow/core'
import '@logicflow/core/dist/style/index.css'
// 导入 Vue 3 的 onMounted 生命周期钩子
import { onMounted } from 'vue'
// 导入自定义的图形节点组件
import CustomCircle from './component/CustomCircle'
import CustomEllipse from './component/CustomEllipse'
import CustomPolygon from './component/CustomPolygon'
import CustomDiamond from './component/CustomDiamond'
import CustomRect from './component/CustomRect'

// 配置 LogicFlow 的静默模式和交互行为
const SilentConfig = {
  isSilentMode: true, // 启用静默模式
  stopScrollGraph: true, // 禁止滚动图形
  stopMoveGraph: true, // 禁止移动图形
  stopZoomGraph: true, // 禁止缩放图形
  adjustNodePosition: true // 调整节点位置
}

// 定义图表数据,包含自定义节点,但没有连线
const data = {
  nodes: [
    {
      id: 'node_id_1',
      type: 'CustomCircle',
      x: 100,
      y: 60,
      text: '自定义圆形'
    },
    {
      id: 'node_id_2',
      type: 'CustomEllipse',
      x: 300,
      y: 60,
      text: '自定义椭圆'
    },
    {
      id: 'node_id_4',
      type: 'CustomDiamond',
      x: 500,
      y: 60,
      text: '自定义菱形'
    },
    {
      id: 'node_id_3',
      type: 'CustomPolygon',
      x: 110,
      y: 220,
      text: '自定义多边形'
    },
    {
      id: 'node_id_5',
      type: 'CustomRect',
      x: 350,
      y: 220,
      text: '自定义矩形'
    }
  ],
  edges: []
}

// 在 Vue 组件挂载后执行
onMounted(() => {
  // 创建 LogicFlow 实例并配置容器和网格显示
  const lf = new LogicFlow({
    container: document.getElementById('container')!, // 指定显示 LogicFlow 图表的 HTML 容器
    grid: true, // 启用网格显示
    ...SilentConfig // 应用静默模式和其他配置
  })
  // 注册自定义节点
  lf.register(CustomCircle)
  lf.register(CustomEllipse)
  lf.register(CustomPolygon)
  lf.register(CustomDiamond)
  lf.register(CustomRect)
  // 渲染图表数据并将视图居中
  lf.render(data)
  lf.translateCenter()
})
</script>

<template>
  <h3>Example06</h3>
  <div id="container"></div>
  <!-- 用于显示 LogicFlow 图表的容器 -->
</template>

<style>
#container {
  /* 设置容器的宽度为整个父容器的宽度 */
  width: 100%;
  /* 设置容器的高度为 500 像素 */
  height: 500px;
}
</style>

项目启动后,页面内容如下所示:
在这里插入图片描述

代码:https://github.com/lt5227/example_code/blob/main/logicflow_example/src/views/Example/LogicFlow/Example06.vue

LogicFlow 内部存在 7 种基础节点, 自定义节点的时候可以基于需要选择任意一种来继承, 然后取一个符合自己业务意义的名字。以@logicflow/extension中提供的可缩放节点为例:LogicFlow 基础节点不支持节点缩放,于是 LogicFlow 在extension包中,基于基础节点,封装了对节点缩放的逻辑,然后发布出去。这样开发者可以直接基于extension中的可缩放节点进行自定义。

自定义节点model

LogicFlow把自定义节点外观分为了自定义节点样式属性自定义节点形状属性两种方式。更多详细定义方法,参见:NodeModelApi

1. 样式属性

在LogicFlow中,外观属性表示控制着节点边框颜色这类偏外观的属性。这些属性是可以直接通过主题配置来控制的。自定义节点样式可以看做在主题的基础上基于当前节点的类型进行再次定义。
例如:在主题中对所有rect节点都定义其边框颜色为红色stroke:red,那么可以在自定义节点UserTask的时候,重新定义UserTask边框为stroke:blue。更细粒度的节点样式控制方法,详情见API样式属性。

class UserTaskModel extends RectNodeModel {
  getNodeStyle() {
    const style = super.getNodeStyle();
    style.stroke = 'blue';
    return style;
  }
}
2. 形状属性

在LogicFlow中,形状属性表示节点的宽width、高height,矩形的圆角radius,圆形的半径r,多边形的顶点points等这些控制着节点最终形状的属性。因为LogicFlow在计算节点的锚点、连线的起点终点的时候,会基于形状属性进行计算。对于形状属性的自定义,需要在setAttributes方法或initNodeData方法中进行。

LogicFlow对于不同的基础节点,存在一些各基础节点自己特有的形状属性。详情见API形状属性。

class CustomRectModel extends RectNodeModel {
  initNodeData(data) {
    super.initNodeData(data);
    this.width = 200;
    this.height = 80;
  }
  // or
  setAttributes() {
    this.width = 200;
    this.height = 80;
  }
}

注意

如果不在model中设置形状属性,而是直接在view中直接定义生成图形的宽高这种形状属性,会出现锚点位置、outline大小不正确的情况。同时,连线的位置也可能会出现错乱。

3. 基于 properties 属性自定义节点样式

不论是节点还是边,LogicFlow都保留了properties字段,用于给开发者存放自己的业务属性。所以在自定义节点样式的时候,可以基于properties中的属性来控制节点显示不同的样式。

class UserTaskModel extends RectNodeModel {
  getNodeStyle() {
    const style = super.getNodeStyle();
    const properties = this.properties;
    if (properties.statu === 'pass') { // 业务属性statu为‘pass’时展示边框为green
      style.stroke = "green";
    } else if (properties.statu === 'reject') { // 业务属性statu为‘reject’时展示边框为red
      style.stroke = "red";
    } else {
      style.stroke = "rgb(24, 125, 255)";
    }
    return style;
  }
}

提示
如果不了解为什么 this.properties 打印出来是一个 Proxy 对象,无法看到属性。请查看 issu

自定义节点view

LogicFlow在自定义节点的model时,可以定义节点的基础形状、样式等属性。但是当开发者需要一个更加复杂的节点时,可以使用 LogicFlow 提供的自定义节点view的方式。
我们可以创建 src/views/Example/LogicFlow/component/UserTask/index.ts 代码如下:

// 从 @logicflow/core 引入必要的组件和函数
import { RectNode, RectNodeModel, h } from '@logicflow/core'

// 自定义视图类,继承自 RectNode
class UserTaskView extends RectNode {
  // 获取标签形状的方法,用于在节点中添加一个自定义的 SVG 元素
  getLabelShape() {
    const { model } = this.props // 从 props 中获取 model 数据
    const { x, y, width, height } = model // 从 model 中解构出位置和尺寸信息
    const style = model.getNodeStyle() // 获取节点的样式
    // 使用 h 函数创建 SVG 标签
    return h(
      'svg',
      {
        x: x - width / 2 + 5, // 计算 SVG 的 x 坐标
        y: y - height / 2 + 5, // 计算 SVG 的 y 坐标
        width: 25,
        height: 25,
        viewBox: '0 0 1274 1024' // 设定 SVG 的视窗
      },
      [
        // 创建 path 元素,展示自定义图形
        h('path', {
          fill: style.stroke, // 设置填充色为节点的边框色
          d: 'M690.366075 350.568358c0-98.876614-79.937349-179.048571-178.558027-179.048571-98.59935 0-178.515371 80.150629-178.515371 179.048571 0 98.833958 79.916021 178.963259 178.515371 178.963259C610.428726 529.531617 690.366075 449.380988 690.366075 350.568358M376.140632 350.568358c0-75.159877 60.72082-136.072649 135.667416-136.072649 74.989253 0 135.667416 60.912772 135.667416 136.072649 0 75.117221-60.678164 136.029993-135.667416 136.029993C436.861451 486.577022 376.140632 425.664251 376.140632 350.568358M197.284012 762.923936 197.284012 778.472049l15.526785 0 291.255186 0.127968L819.784387 778.472049l15.569441 0 0-15.548113c0-139.783721-136.413897-285.581938-311.026243-273.275681-10.002833 0.703824-24.740482 9.128385-34.658002 9.938849-8.573857 0.74648 13.692577 8.232609 14.396401 16.827793 9.021745-0.789136 6.313088 13.095393 15.505457 13.095393 150.597017 0 263.14488 103.07823 263.14488 224.62651l15.441473-15.590769-285.816546-0.042656-278.991585 1.81288 15.526785 15.612097c0-82.752645 75.095893-152.70849 136.861785-191.824044 7.25152-4.58552 8.659169-17.659585 4.862784-22.906273-6.846288-9.426977-19.877697-8.701825-28.046322-6.014496C285.262018 560.521203 197.284012 667.758394 197.284012 762.923936'
        }),
        h('path', {
          fill: style.stroke,
          d: 'M512.31992 1.535616c-282.766642 0-512.021328 228.89211-512.021328 511.210864 0 282.46805 229.254686 511.25352 512.021328 511.25352 117.431975 0 228.828126-39.606098 318.810964-111.204199 10.791969-8.488545 12.540865-24.22861 3.988336-34.99925-8.616513-10.770641-24.356578-12.540865-35.127218-3.94568-81.174373 64.538532-181.586603 100.241606-287.650754 100.241606-255.210864 0-462.028493-206.561693-462.028493-461.367325 0-254.762976 206.817629-461.303341 462.028493-461.303341 255.210864 0 462.092477 206.561693 462.092477 461.303341 0 87.380821-24.33525 171.093227-69.614596 243.651087-7.272848 11.645089-3.668416 27.086562 8.040657 34.35941 11.709073 7.272848 27.10789 3.62576 34.402066-7.976672 50.184787-80.406565 77.143381-173.247355 77.143381-270.055153C1024.383904 230.427726 795.10789 1.535616 512.31992 1.535616z'
        })
      ]
    )
  }
  // 重写 getShape 方法,定义节点的整体形状
  getShape() {
    const { model } = this.props // 获取 model 数据
    const { x, y, width, height, radius, properties } = model // 解构位置、尺寸和圆角信息
    const style = model.getNodeStyle() // 获取节点的样式
    console.log(properties)
    // 使用 h 函数组合节点的矩形和标签
    return h('g', {}, [
      h('rect', {
        ...style,
        x: x - width / 2, // 设置矩形的 x 坐标
        y: y - height / 2, // 设置矩形的 y 坐标
        rx: radius, // 矩形的 x 方向圆角
        ry: radius, // 矩形的 y 方向圆角
        width,
        height
      }),
      this.getLabelShape() // 添加标签形状
    ])
  }
}

// 自定义模型类,继承自 RectNodeModel
class UserTaskModel extends RectNodeModel {
  // 设置节点属性的方法
  setAttributes() {
    const size = this.properties.scale || 1 // 从属性中获取缩放比例,默认为 1
    this.width = 100 * size // 计算节点宽度
    this.height = 80 * size // 计算节点高度
  }
  // 获取文本样式的方法
  getTextStyle() {
    const style = super.getTextStyle() // 调用基类方法获取默认文本样式
    style.fontSize = 12 // 设置字体大小
    const properties = this.properties // 获取节点的自定义属性
    style.color = properties.disabled ? 'red' : 'rgb(24, 125, 255)' // 根据 disabled 属性调整文本颜色
    return style
  }
  // 获取节点样式的方法
  getNodeStyle() {
    const style = super.getNodeStyle() // 调用基类方法获取默认节点样式
    const properties = this.properties // 获取节点的自定义属性
    // 根据 disabled 属性调整边框颜色
    if (properties.disabled) {
      style.stroke = 'red'
    } else {
      style.stroke = 'rgb(24, 125, 255)'
    }
    return style
  }
  // 其他样式和方法根据需要实现,例如锚点样式、锚点线条样式等
  getAnchorStyle(anchorInfo: any) {
    const style = super.getAnchorStyle(anchorInfo)
    style.stroke = 'rgb(24, 125, 255)'
    style.r = 3
    style.hover.r = 8
    style.hover.fill = 'rgb(24, 125, 255)'
    style.hover.stroke = 'rgb(24, 125, 255)'
    return style
  }
  getAnchorLineStyle(anchorInfo: any) {
    const style = super.getAnchorLineStyle(anchorInfo)
    style.stroke = 'rgb(24, 125, 255)'
    return style
  }
  getOutlineStyle() {
    const style = super.getOutlineStyle()
    style.stroke = 'red'
    if (style.hover) {
      style.hover.stroke = 'red'
    }
    return style
  }
}

// 导出配置,用于在 LogicFlow 中注册和使用自定义节点
export default {
  type: 'UserTask', // 节点类型的唯一标识
  view: UserTaskView, // 使用自定义视图类
  model: UserTaskModel // 使用自定义模型类
}

再定义一个src/views/Example/LogicFlow/Example07.vue代码内容如下:

<script setup lang="ts">
// 导入 LogicFlow 核心库和相关样式
import LogicFlow from '@logicflow/core'
import '@logicflow/core/dist/style/index.css'
// 导入 Vue 3 的 onMounted 生命周期钩子
import { onMounted } from 'vue'
// 导入自定义的图形节点组件
import UserTask from './component/UserTask'

const SilentConfig = {
  stopScrollGraph: true,
  stopMoveGraph: true,
  stopZoomGraph: true
}

// 定义图表数据,包含自定义节点,但没有连线
const data = {
  nodes: [
    {
      id: 'node_id_1',
      type: 'UserTask',
      x: 100,
      y: 100,
      text: { x: 100, y: 100, value: '节点1' }
    },
    {
      id: 'node_id_2',
      type: 'circle',
      x: 200,
      y: 300,
      text: { x: 200, y: 300, value: '节点2' },
      properties: {}
    }
  ],
  edges: [
    {
      id: 'edge_id',
      type: 'polyline',
      sourceNodeId: 'node_id_1',
      targetNodeId: 'node_id_2',
      text: { x: 139, y: 200, value: '连线' },
      startPoint: { x: 100, y: 140 },
      endPoint: { x: 200, y: 250 },
      pointsList: [
        { x: 100, y: 140 },
        { x: 100, y: 200 },
        { x: 200, y: 200 },
        { x: 200, y: 250 }
      ],
      properties: {}
    }
  ]
}

// 在 Vue 组件挂载后执行
onMounted(() => {
  // 创建 LogicFlow 实例并配置容器和网格显示
  const lf = new LogicFlow({
    container: document.getElementById('container')!, // 指定显示 LogicFlow 图表的 HTML 容器
    grid: true, // 启用网格显示
    ...SilentConfig // 应用静默模式和其他配置
  })
  // 注册自定义节点
  lf.register(UserTask)
  // 渲染图表数据并将视图居中
  lf.render(data)
  lf.translateCenter()
  lf.on('node:click', ({ data }) => {
    lf.setProperties(data.id, {
      disabled: !data.properties.disabled,
      scale: 1.5
    })
  })
})
</script>

<template>
  <h3>Example07</h3>
  <div id="container"></div>
  <!-- 用于显示 LogicFlow 图表的容器 -->
</template>

<style>
#container {
  /* 设置容器的宽度为整个父容器的宽度 */
  width: 100%;
  /* 设置容器的高度为 500 像素 */
  height: 500px;
}
</style>

运行后样例效果如下
在这里插入图片描述

代码:https://github.com/lt5227/example_code/blob/main/logicflow_example/src/views/Example/LogicFlow/Example07.vue

这里对于Shape的返回用到了h函数h方法是LogicFlow对外暴露的渲染函数,其用法与reactvue的 createElement 一致。但是这里我们需要创建的是svg标签,所以需要有一定的svg基础知识。

举几个简单的例子:

h(nodeName, attributes, [...children])

// <text x="100" y="100">文本内容</text>
h('text', { x: 100, y: 100 }, ['文本内容'])

/**
 * <g>
 *   <rect x="100" y="100" stroke="#000000" strokeDasharray="3 3"></rect>
 *   <text x="100" y="100">文本内容</text>
 * </g>
 */

h('g',{}, [
  h('rect', { x: 100, y: 100, stroke: "#000000", strokeDasharray="3 3"}),
  h('text', { x: 100, y: 100 }, ['文本内容'])
])
getShape方法

此方法作用就是定义最终渲染的图形,LogicFlow内部会将其返回的内容插入到 svg DOM 上。开发者不是一定需要重写此方法,只有在期望改变最终渲染图形 svg DOM 的时候才使用此方法。以上面的例子来说,rect节点最终渲染的 svg DOM 只是一个矩形。但是当我们想要在上面加一个图标的时候,那边必定需要修改到最终渲染图形的 svg DOM 了,这个时候就需要通过重写 getShape 来实现了。

LogicFlow定义一个节点的外观有三种方式,分别为主题自定义节点model自定义节点view。这三种方式优先级为 主题 < 自定义节点model < 自定义节点view。它们的差异是:

  • 主题:定义所有此基础类型节点的通用样式,例如定义所有rect节点的边框颜色、宽度等。
  • 自定义节点model:定义此注册类型节点的样式。
  • 自定义节点view:定义此注册类型节点svg DOM。

注意
虽然自定义节点view优先级最高,功能也最完善,理论上我们可以完全通过自定义节点view实现任何我们想要的效果,但是此方式还是存在一些限制。

  1. 自定义节点view最终生成的图形的形状属性必须和model中形状属性的一致,因为节点的锚点、外边框都是基于节点model中的widthheight生成。
    2.自定义节点view最终生成的图形整体轮廓必须和继承的基础图形一致,不能继承的rect而在 getShape 的时候返回的最终图形轮廓变成了圆形。因为LogicFlow对于节点上的连线调整、锚点生成等会基于基础图形进行计算。
一些思考
1. 为什么rectxy不是直接从model中获取的xy

在LogicFlow所有的基础节点中,model里面的xy都是统一表示中心点。但是getShape方法给我们提供直接生成 svg DOM 的方式,在 svg 中,对图形位置的控制则存在差异:

  • rect:通过xy表示图形的位置,但是表示是图形左上角坐标。所以一般通过中心点,然后减去节点的宽高的一半计算出左上角坐标。
    const { x, y, width, height, radius } = this.props.model;
    // svg dom <rect x="100" y="100" width="100" height="80">
    h("rect", {
      ...style,
      x: x - width / 2,
      y: y - height / 2,
      rx: radius, // 注意这里是rx而不是radius
      ry: radius,
      width,
      height
    }),
    
  • circleellipse:通过cxcy表示图形的位置,含义为中心点的坐标。
    const { x, y, r } = this.props.model;
    // svg dom <circle cx="100", cy="100", r="20">
    h("circle", {
      ...style,
      r, // 半径保持不变
      cx: x,
      cy: y,
    })
    
    // 椭圆
    const { x, y, rx, ry } = this.props.model;
    // svg dom <ellipse cx="100", cy="100", rx="20" ry="10">
    h("ellipse", {
      ...style,
      cx: x,
      cy: y,
      rx,
      ry
    })
    
  • polygon:所有的顶点坐标已包含位置
    const { x, y, points } = this.props.model;
    const pointStr = points.map((point) => {
        return `${point[0] + x}, ${point[1] + y}`
      }).join(" ");
    // svg dom <polygon points="100,10 250,150 200,110" >
    h("polygon", {
      ...style,
      r, // 半径保持不变
      points: pointStr,
    })
    

自定义矩形的 view 时 radius 设置
model中,radius是矩形节点的形状属性。但是在自定义view时需要注意,svg里面设置矩形的圆角并不是用radius·,而是使用rx,ry。所以在自定义view的矩形时,需要将model中radius的赋值给rxry,否则圆角将不会生效。

2. props 怎么用?

LogicFlow 是基于preact开发的,我们自定义节点view的时候,可以通过this.props获取父组件传递过来的数据。this.props对象包含两个属性,分别为:

  • model:表示自定义节点的 model
  • graphModel:表示logicflow整个图的model
3. 图标的 path 如何获取?

一般情况下,图标我们可以找UI或者去iconfont.cn获得一个svg格式的文件。然后在IDE中以文本的方式打开,然后格式化,就可以看到代码。代码中一般是最外层一个 svg 标签,里面是一个或者多个path。这个时候,我们使用前面提到的 h 方法来实现 svg 文件中的代码即可。

svg 标签一般包括如下属性:

  • viewBox: viewBox属性允许指定一个给定的一组图形延展以适应特定的容器元素。一般把 svg 标签上的 viewBox 属性值复制过来就行。
  • widthheight:这个不需要使用svg标签上的widthheight,直接写成你期望的宽高就行。

path标签属性:

  • d:该属性定义了一个路径。直接复制 svg 代码过来即可,不需要去关心d具体内容表示的含义。
  • fill:路径的填充颜色,一般和节点的边框颜色一致,但是也可以按照业务需求自定义。

官方文档
样例代码


上一篇:LogicFlow 基础 实例

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1817935.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL查询优化最佳实践15条(建议收藏)

目录 1 优化方法&#xff08;15条&#xff09; 2 总结 MySQL的数据库常规查询的过程中性能的优化非常重要&#xff0c;其中很多点是和开发习惯有关&#xff0c;能熟练掌握不只能提高工作的效率&#xff0c;同时也能提高个人的技能。有一些优化的技巧同样也适合于其他的数据库…

git的ssh安装,windows通过rsa生成密钥认证问题解决

1 windows下载 官网下载可能出现下载太慢的情况&#xff0c;Git官网下载地址为&#xff1a;官网&#xff0c;推荐官网下载&#xff0c;如无法下载&#xff0c;可移步至CSDN&#xff0c;csdn下载地址&#xff1a;https://download.csdn.net/download/m0_46309087/12428308 2 Gi…

Tabby:一款革新的Mac/Win现代化终端模拟器

在信息技术日新月异的今天&#xff0c;终端操作已成为众多开发者、系统管理员和技术爱好者的日常必备工具。然而&#xff0c;传统的终端模拟器往往功能单一、界面陈旧&#xff0c;无法满足用户对于高效、便捷操作体验的追求。Tabby应运而生&#xff0c;作为一款现代化、功能强大…

6大好用的变音软件推荐,最好用的变声器免费版有哪些?

您在录制视频时&#xff0c;是否曾对自己的声音感到厌烦&#xff1f;有没有想过换一种声音让别人认不出您&#xff1f;变声软件允许你通过先进的AI算法改变声音。它可以增加所需的失真度、调整音高并改变语音的音调&#xff0c;从而将你的声音变为名人、机器人或卡通人物的声音…

C# WPF入门学习主线篇(二十一)—— 静态资源和动态资源

C# WPF入门学习主线篇&#xff08;二十一&#xff09;—— 静态资源和动态资源 欢迎来到C# WPF入门学习系列的第二十一篇。在上一章中&#xff0c;我们介绍了WPF中的资源和样式。本篇文章将深入探讨静态资源&#xff08;StaticResource&#xff09;和动态资源&#xff08;Dynam…

立创·天空星开发板-GD32F407VE-Timer

本文以 立创天空星开发板-GD32F407VET6-青春版 作为学习的板子&#xff0c;记录学习笔记。 立创天空星开发板-GD32F407VE-Timer 定时器基本定时器示例 定时器 定时器是嵌入式系统中常用的一种外设&#xff0c;它可以产生一定的时间间隔、延时、定时等功能&#xff0c;广泛应用于…

深度学习500问——Chapter11:迁移学习(2)

文章目录 11.2 迁移学习的基本思路有哪些 11.2.1 基于样本迁移 11.2.2 基于特征迁移 11.2.3 基于模型迁移 11.2.4 基于关系迁移 11.2 迁移学习的基本思路有哪些 迁移学习的基本方法可以分为四种。这四种基本方法分别是&#xff1a;基于样本的迁移&#xff0c;基于模型的迁移&a…

Three.js动效(第15辑):让前端手撕UI,拳打后端的效果。

three.js的设计效果非常复杂&#xff0c;后端提供的数据接口问题百出&#xff0c;这很容易让前端手撕UI、拳打后端&#xff0c;这种请详细该如何办呢&#xff1f; 前端 VS UI&#xff1a; 1. 沟通协调&#xff1a;UI和前端应该加强沟通&#xff0c;理解对方的工作难点和需求&…

SpringSecurity6从入门到实战之SpringSecurity6自定义认证规则

SpringSecurity6从入门到实战之SpringSecurity6自定义认证规则 Spring Security 中默认所有的 http 请求都需要先认证通过后&#xff0c;才能访问。那么&#xff0c; 如何指定不需要认证就可以直接访问的资源呢&#xff1f;比如 用户的登录页面和注册页面&#xff0c;都是不需要…

用表头设置控制表格内列的排序和显示隐藏

项目背景 : react ant 需求 : 点击表头设置弹窗 , 拖拽可控制外部表格列的排序 , 开关可控制外部表格列的显示和隐藏 实现效果如下 :注意 : 1. 拖拽效果参考了ant-table中的拖拽效果(这块代码放最后) 2. 后台反了json格式(用is_show控制显示和隐藏 , 我给他传…

小企业选PLM系统的注意事项?

在当今竞争激烈的市场中&#xff0c;小企业需要不断提高生产效率和降低成本。为了实现这一目标&#xff0c;许多小企业正在寻找一种有效的解决方案来管理其产品生命周期。这就是为什么PLM系统对于小企业来说非常重要的原因&#xff0c;接下来请华天软件说说这个话题。 一、对于…

超详解——​深入理解Python中的位运算与常用内置函数/模块——基础篇

目录 ​编辑 1.位运算 2.常用内置函数/模块 math模块 random模块 decimal模块 常用内置函数 3.深入理解和应用 位运算的实际应用 1.权限管理 2.位图 3.图像处理 2.math模块的高级应用 统计计算 几何计算 总结 1.位运算 位运算是对整数在内存中的二进制表示进行…

成功者的思维方式——逆向思维

在竞争激烈的现代社会中&#xff0c;成功者往往具备一种与众不同的思维方式——逆向思维。 1、原谅别人的过失 在大多数人的思维中&#xff0c;对于别人的过失往往持有批评和指责的态度。但是&#xff0c;每个人都有犯错的时候&#xff0c;而原谅不仅能化解矛盾&#xff0c;…

气膜馆在夏季如何控制室内温度—轻空间

气膜馆以其快速搭建、灵活使用和高效节能的特点在多种场合中得到广泛应用。然而&#xff0c;夏季的高温环境对气膜馆内的温度控制提出了巨大挑战。本文将探讨气膜馆在夏季如何有效控制室内温度&#xff0c;确保为使用者提供一个舒适的环境。 1. 优质膜材选择 隔热膜材&#xff…

【Three.js】知识梳理十五:相机控制器Controls

在 3D 场景中&#xff0c;摄像机的控制尤为重要&#xff0c;因为它决定了用户如何观察和与场景互动。Three.js 提供了多种相机控制器&#xff0c;最常用的有 OrbitControls、TrackballControls、FlyControls 和 FirstPersonControls。OrbitControls 适合用于查看和检查 3D 模型…

成都百洲文化传媒有限公司电商服务的领航者

在当今数字化浪潮席卷全球的时代&#xff0c;电商行业以其独特的魅力和无穷的潜力&#xff0c;正成为推动经济发展的重要引擎。在这一领域&#xff0c;成都百洲文化传媒有限公司以其专业的电商服务和创新的营销理念&#xff0c;成为了行业的佼佼者&#xff0c;引领着电商服务的…

简单塔防小游戏

学习目标&#xff1a;熟悉塔防游戏核心战斗 游戏画面 项目结构目录 核心代码&#xff1a; if ( Input.GetMouseButtonDown(0)){if (EventSystem.current.IsPointerOverGameObject()false){//开发炮台的建造Ray ray Camera.main.ScreenPointToRay(Input.mousePosition);Rayca…

Openstack删除虚拟机失败,状态变成ERROR,如何完成正常删除

1、问题描述 在openstack平台批量删除了几台云主机&#xff0c;但删除过程因为消息队列故障导致cinder volume服务异常&#xff0c;有两台主机状态变为ERROR,无法正常删除。 2、问题处理 使用nova reset-state --active xxxx&#xff08;uuid&#xff09;将主机状态重置为activ…

Mac vscode could not import github.com/gin-gonic/gin

问题背景&#xff1a; 第一次导入一个go的项目就报红 问题分析&#xff1a; 其实就是之前没有下载和导入gin这个web框架包 gin是一个golang的微框架&#xff0c;封装比较优雅&#xff0c;API友好&#xff0c;源码注释比较明确。 问题解决&#xff1a; 依次输入以下命令。通…

2024护眼落地灯十大品牌有哪些?揭晓年度十大护眼灯品牌!

2024护眼落地灯十大品牌有哪些&#xff1f;家里的台灯照明范围有限&#xff0c;阅读面和房间的空间光线亮暗差大&#xff0c;眼睛需要同时适应亮和暗两种光线&#xff0c;频繁收缩&#xff0c;极易眼疲劳。再加上学习时间过长&#xff0c;眼睛得不到休息&#xff0c;从而引起近…