Skip to content
索引

节点

基础节点

矩形:rect

圆形: circle

椭圆: ellipse

多边形: polygon

菱形: diamond

文本: text

HTML: html

js
import LogicFlow from "@logicflow/core";
import "@logicflow/core/dist/style/index.css";

const lf = new LogicFlow({
  container: document.querySelector("#app"),
  grid: true
});

lf.render({
  nodes: [
    {
      id: "1",
      type: "rect",
      x: 100,
      y: 100,
      text: "矩形"
    },
    {
      id: "2",
      type: "circle",
      x: 300,
      y: 100,
      text: "圆形"
    },
    {
      id: "3",
      type: "ellipse",
      x: 500,
      y: 100,
      text: "椭圆"
    },
    {
      id: "4",
      type: "polygon",
      x: 100,
      y: 250,
      text: "多边形"
    },
    {
      id: "5",
      type: "diamond",
      x: 300,
      y: 250,
      text: "菱形"
    },
    {
      id: "6",
      type: "text",
      x: 500,
      y: 250,
      text: "纯文本节点"
    },
    {
      id: "7",
      type: "html",
      x: 100,
      y: 400,
      text: "html节点"
    }
  ]
});
import LogicFlow from "@logicflow/core";
import "@logicflow/core/dist/style/index.css";

const lf = new LogicFlow({
  container: document.querySelector("#app"),
  grid: true
});

lf.render({
  nodes: [
    {
      id: "1",
      type: "rect",
      x: 100,
      y: 100,
      text: "矩形"
    },
    {
      id: "2",
      type: "circle",
      x: 300,
      y: 100,
      text: "圆形"
    },
    {
      id: "3",
      type: "ellipse",
      x: 500,
      y: 100,
      text: "椭圆"
    },
    {
      id: "4",
      type: "polygon",
      x: 100,
      y: 250,
      text: "多边形"
    },
    {
      id: "5",
      type: "diamond",
      x: 300,
      y: 250,
      text: "菱形"
    },
    {
      id: "6",
      type: "text",
      x: 500,
      y: 250,
      text: "纯文本节点"
    },
    {
      id: "7",
      type: "html",
      x: 100,
      y: 400,
      text: "html节点"
    }
  ]
});

自定义节点

LogicFlow 是基于继承来实现自定义节点

开发者可以继承 LogicFlow 内置的节点,然后利用面向对象的重写机制。重写节点样式相关的方法,来达到自定义节点样式的效果

js
// 矩形
import { RectNode, RectNodeModel } from "@logicflow/core";
// 圆形
import { CircleNode, CircleNodeModel } from "@logicflow/core";
// 椭圆
import { EllipseNode, EllipseNodeModel } from "@logicflow/core";
// 多边形
import { PolygonNode, PolygonNodeModel } from "@logicflow/core";
// 菱形
import { DiamondNode, DiamondNodeModel } from "@logicflow/core";
// 文本
import { TextNode, TextNodeModel } from "@logicflow/core";
// HTML
import { HtmlNode, HtmlNodeModel } from "@logicflow/core";
// 矩形
import { RectNode, RectNodeModel } from "@logicflow/core";
// 圆形
import { CircleNode, CircleNodeModel } from "@logicflow/core";
// 椭圆
import { EllipseNode, EllipseNodeModel } from "@logicflow/core";
// 多边形
import { PolygonNode, PolygonNodeModel } from "@logicflow/core";
// 菱形
import { DiamondNode, DiamondNodeModel } from "@logicflow/core";
// 文本
import { TextNode, TextNodeModel } from "@logicflow/core";
// HTML
import { HtmlNode, HtmlNodeModel } from "@logicflow/core";

例如,自定义一个继承矩形节点的自定义节点

需要继承矩形节点的view和model类,取一个type名

js
import { RectNode, RectNodeModel } from "@logicflow/core";

class UserTaskModel extends RectNodeModel {}

class UserTaskView extends RectNode {}

export default {
  type: "UserTask",
  view: UserTaskView,
  model: UserTaskModel,
};
import { RectNode, RectNodeModel } from "@logicflow/core";

class UserTaskModel extends RectNodeModel {}

class UserTaskView extends RectNode {}

export default {
  type: "UserTask",
  view: UserTaskView,
  model: UserTaskModel,
};

lf.register中注册自定义节点,render函数的nodes数组中type名指定为自定义节点的type名

js
import UserTask from "./UserTaskNode.js";

const lf = new LogicFlow({
  container: document.querySelector("#container"),
});
lf.register(UserTask);

lf.render({
  nodes: [
    {
      type: "UserTask",
      x: 100,
      y: 100,
    },
  ],
});
import UserTask from "./UserTaskNode.js";

const lf = new LogicFlow({
  container: document.querySelector("#container"),
});
lf.register(UserTask);

lf.render({
  nodes: [
    {
      type: "UserTask",
      x: 100,
      y: 100,
    },
  ],
});

自定义节点样式

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

示例:

js
class UserTaskModel extends RectNodeModel {
  getNodeStyle() {
    const style = super.getNodeStyle();
    style.stroke = "blue";
    style.strokeDasharray = "3 3";
    return style;
  }
}
class rectRadiusModel extends RectNodeModel {
    getNodeStyle() {
      const style = super.getNodeStyle();
      const { fill } = this.properties;
      style.fill = fill;
      style.strokeWidth = 0;
      return style;
    }
  }
class UserTaskModel extends RectNodeModel {
  getNodeStyle() {
    const style = super.getNodeStyle();
    style.stroke = "blue";
    style.strokeDasharray = "3 3";
    return style;
  }
}
class rectRadiusModel extends RectNodeModel {
    getNodeStyle() {
      const style = super.getNodeStyle();
      const { fill } = this.properties;
      style.fill = fill;
      style.strokeWidth = 0;
      return style;
    }
  }

自定义节点形状

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

属性文档 https://docs.logic-flow.cn/docs/#/zh/api/nodeModelApi?id=形状属性

js
class customRectModel extends RectNodeModel {
  initNodeData(data) {
    super.initNodeData(data);
    this.width = 200;
    this.height = 80;
    this.radius = 50;
  }
}
class customRectModel extends RectNodeModel {
  initNodeData(data) {
    super.initNodeData(data);
    this.width = 200;
    this.height = 80;
    this.radius = 50;
  }
}

示例:自定义rect

js
<template>
  <div>
    <div id="container"></div>
  </div>
</template>

<script>
import LogicFlow from "@logicflow/core"
import "@logicflow/core/dist/style/index.css"
import { RectNode, RectNodeModel } from "@logicflow/core"

export default {
  name: "home",
  mounted() {
    const data = {
      nodes: [
        {
          id: 1,
          type: "rectRadiusModel",
          x: 260,
          y: 60,
          text: "蓝色",
          properties: {
            fill: "#4F87BA"
          }
        },
        {
          id: 2,
          type: "rectRadiusModel",
          x: 260,
          y: 150,
          text: "绿色",
          properties: {
            fill: "#6FAC46"
          }
        },
        {
          id: 5,
          type: "rectRadiusModel",
          x: 260,
          y: 250,
          text: "橙色",
          properties: {
            fill: "#EC7C30"
          }
        }
      ]
    }
    const lf = new LogicFlow({
      container: document.querySelector("#container"),
      width: 700,
      height: 600,
      grid: {}
    })

    // 自定义model
    class rectRadiusModel extends RectNodeModel {
      // 样式属性
      getNodeStyle() {
        const style = super.getNodeStyle()
        const { fill } = this.properties
        style.fill = fill
        style.strokeWidth = 0
        return style
      }
      // 形状属性
      initNodeData(data) {
        super.initNodeData(data)
        this.width = 150
        this.height = 50
        this.radius = 25
      }
    }
    class rectNoRadiusModel extends rectRadiusModel {
      initNodeData(data) {
        super.initNodeData(data)
        this.radius = 0
      }
    }
    lf.register({
      type: "rectRadiusModel",
      view: RectNode,
      model: rectRadiusModel
    })
    lf.register({
      type: "rectRadiusModel",
      view: RectNode,
      model: rectNoRadiusModel
    })

    lf.setTheme({
      nodeText: {
        color: "#ffffff",
        overflowMode: "autoWrap",
        fontSize: 15
      }
    })
    lf.render(data)
  }
}
</script>
<template>
  <div>
    <div id="container"></div>
  </div>
</template>

<script>
import LogicFlow from "@logicflow/core"
import "@logicflow/core/dist/style/index.css"
import { RectNode, RectNodeModel } from "@logicflow/core"

export default {
  name: "home",
  mounted() {
    const data = {
      nodes: [
        {
          id: 1,
          type: "rectRadiusModel",
          x: 260,
          y: 60,
          text: "蓝色",
          properties: {
            fill: "#4F87BA"
          }
        },
        {
          id: 2,
          type: "rectRadiusModel",
          x: 260,
          y: 150,
          text: "绿色",
          properties: {
            fill: "#6FAC46"
          }
        },
        {
          id: 5,
          type: "rectRadiusModel",
          x: 260,
          y: 250,
          text: "橙色",
          properties: {
            fill: "#EC7C30"
          }
        }
      ]
    }
    const lf = new LogicFlow({
      container: document.querySelector("#container"),
      width: 700,
      height: 600,
      grid: {}
    })

    // 自定义model
    class rectRadiusModel extends RectNodeModel {
      // 样式属性
      getNodeStyle() {
        const style = super.getNodeStyle()
        const { fill } = this.properties
        style.fill = fill
        style.strokeWidth = 0
        return style
      }
      // 形状属性
      initNodeData(data) {
        super.initNodeData(data)
        this.width = 150
        this.height = 50
        this.radius = 25
      }
    }
    class rectNoRadiusModel extends rectRadiusModel {
      initNodeData(data) {
        super.initNodeData(data)
        this.radius = 0
      }
    }
    lf.register({
      type: "rectRadiusModel",
      view: RectNode,
      model: rectRadiusModel
    })
    lf.register({
      type: "rectRadiusModel",
      view: RectNode,
      model: rectNoRadiusModel
    })

    lf.setTheme({
      nodeText: {
        color: "#ffffff",
        overflowMode: "autoWrap",
        fontSize: 15
      }
    })
    lf.render(data)
  }
}
</script>

拖拽创建节点

示例代码,给画布绑定一个mousedown事件,触发时调用lf.dnd.startDrag在画布鼠标位置创建指定类型的节点和文本

js
import LogicFlow from "@logicflow/core";
import "@logicflow/core/dist/style/index.css";
import customCircle from "./customCircle";

const lf = new LogicFlow({
  container: document.querySelector("#app"),
  grid: true
});
lf.register(customCircle);
lf.render({
  nodes: [
    {
      type: "customCircle",
      x: 100,
      y: 200
    }
  ]
});

document.querySelector("#js_custom-dnd").addEventListener("mousedown", (e) => {
  const type = e.target.getAttribute("data-type");
  if (type) {
    lf.dnd.startDrag({
      type,
      text: `${type}节点`
    });
  }
});
import LogicFlow from "@logicflow/core";
import "@logicflow/core/dist/style/index.css";
import customCircle from "./customCircle";

const lf = new LogicFlow({
  container: document.querySelector("#app"),
  grid: true
});
lf.register(customCircle);
lf.render({
  nodes: [
    {
      type: "customCircle",
      x: 100,
      y: 200
    }
  ]
});

document.querySelector("#js_custom-dnd").addEventListener("mousedown", (e) => {
  const type = e.target.getAttribute("data-type");
  if (type) {
    lf.dnd.startDrag({
      type,
      text: `${type}节点`
    });
  }
});

自定义img节点

js
import { h, RectNode } from '@logicflow/core'

// 图片-基础节点
class ImageModel extends RectNode.model {
  initNodeData(data) {
    super.initNodeData(data)
    this.width = 80
    this.height = 60
  }
}


class ImageNode extends RectNode.view {
  // 这里不用实现,继承的节点再进行实现
  getImageHref () {
    return;
  }
  getResizeShape() {
    const { x, y, width, height } = this.props.model
    const href = this.getImageHref()
    const attrs = {
      x: x- 1/2 * width,
      y: y - 1/2 * height,
      width,
      height,
      href,
      // 根据宽高缩放
      preserveAspectRatio: 'none meet'
    }
    return h('g', {}, [
       h('image', { ...attrs })
    ]
    );
  }
}

export default {
  type: 'image-node',
  view: ImageNode,
  model: ImageModel
}
import { h, RectNode } from '@logicflow/core'

// 图片-基础节点
class ImageModel extends RectNode.model {
  initNodeData(data) {
    super.initNodeData(data)
    this.width = 80
    this.height = 60
  }
}


class ImageNode extends RectNode.view {
  // 这里不用实现,继承的节点再进行实现
  getImageHref () {
    return;
  }
  getResizeShape() {
    const { x, y, width, height } = this.props.model
    const href = this.getImageHref()
    const attrs = {
      x: x- 1/2 * width,
      y: y - 1/2 * height,
      width,
      height,
      href,
      // 根据宽高缩放
      preserveAspectRatio: 'none meet'
    }
    return h('g', {}, [
       h('image', { ...attrs })
    ]
    );
  }
}

export default {
  type: 'image-node',
  view: ImageNode,
  model: ImageModel
}
js
import ImageNode from './ImageNode'

// 云形状的图片节点
class CloudNode extends ImageNode.view {
  getImageHref () {
    return 'https://dpubstatic.udache.com/static/dpubimg/0oqFX1nvbD/cloud.png';
  }
}

export default {
  type: 'image-cloud',
  view: CloudNode,
  model: ImageNode.model
}
import ImageNode from './ImageNode'

// 云形状的图片节点
class CloudNode extends ImageNode.view {
  getImageHref () {
    return 'https://dpubstatic.udache.com/static/dpubimg/0oqFX1nvbD/cloud.png';
  }
}

export default {
  type: 'image-cloud',
  view: CloudNode,
  model: ImageNode.model
}

自定义可缩放节点

https://docs.logic-flow.cn/docs/#/zh/guide/extension/extension-node-resize

js
import { h } from '@logicflow/core'
import { RectResize } from '@logicflow/extension'

class ImageModel extends RectResize.model {
  initNodeData(data) {
    super.initNodeData(data)
    this.width = 80
    this.height = 60
  }
}

class ImageNode extends RectResize.view {
  getImageHref() {
    return 'https://dpubstatic.udache.com/static/dpubimg/0oqFX1nvbD/cloud.png'
  }
  getResizeShape() {
    const { x, y, width, height } = this.props.model
    const href = this.getImageHref()
    const attrs = {
      x: x - (1 / 2) * width,
      y: y - (1 / 2) * height,
      width,
      height,
      href,
      // preserveAspectRatio: 'none meet' 是 SVG 中的一个属性,用于设置图片在视口中的对齐方式和缩放方式。在这个组件中,该属性的值是 'none meet',表示图片不按比例缩放,会被拉伸或压缩以适应视口的大小。此外,meet 还有一个对应的值 'xMaxYMax meet',表示图片会按比例缩放,但不会超出视口的范围,即保证图片完全呈现且留白最少。而在 'none' 模式下,图片会按照视口大小进行缩放,不考虑原始宽高比例。
      preserveAspectRatio: 'none meet'
    }
    return h('g', {}, [h('image', { ...attrs })])
  }
}

export default {
  type: 'image-node',
  view: ImageNode,
  model: ImageModel
}
import { h } from '@logicflow/core'
import { RectResize } from '@logicflow/extension'

class ImageModel extends RectResize.model {
  initNodeData(data) {
    super.initNodeData(data)
    this.width = 80
    this.height = 60
  }
}

class ImageNode extends RectResize.view {
  getImageHref() {
    return 'https://dpubstatic.udache.com/static/dpubimg/0oqFX1nvbD/cloud.png'
  }
  getResizeShape() {
    const { x, y, width, height } = this.props.model
    const href = this.getImageHref()
    const attrs = {
      x: x - (1 / 2) * width,
      y: y - (1 / 2) * height,
      width,
      height,
      href,
      // preserveAspectRatio: 'none meet' 是 SVG 中的一个属性,用于设置图片在视口中的对齐方式和缩放方式。在这个组件中,该属性的值是 'none meet',表示图片不按比例缩放,会被拉伸或压缩以适应视口的大小。此外,meet 还有一个对应的值 'xMaxYMax meet',表示图片会按比例缩放,但不会超出视口的范围,即保证图片完全呈现且留白最少。而在 'none' 模式下,图片会按照视口大小进行缩放,不考虑原始宽高比例。
      preserveAspectRatio: 'none meet'
    }
    return h('g', {}, [h('image', { ...attrs })])
  }
}

export default {
  type: 'image-node',
  view: ImageNode,
  model: ImageModel
}

更改节点的text位置

比如通过properties属性设置

js
import ImageNode from './ImageNode'

class CloudNode extends ImageNode.view {
  getImageHref() {
    return new URL(`/src/assets/images/3.png`, import.meta.url).href
  }
}
class CloudNodeModel extends ImageNode.model {
  initNodeData(data) {
    const { labelPosition } = this.properties
    // 设置节点text位置
    if (data.text && typeof data.text === 'string') {
      data.text = {
        value: data.text || '',
        x: data.x - (labelPosition === 'left' ? 100 : -100),
        y: data.y
      }
    }
    super.initNodeData(data)
  }
}
export default {
  type: 'image-three',
  view: CloudNode,
  model: CloudNodeModel
}
import ImageNode from './ImageNode'

class CloudNode extends ImageNode.view {
  getImageHref() {
    return new URL(`/src/assets/images/3.png`, import.meta.url).href
  }
}
class CloudNodeModel extends ImageNode.model {
  initNodeData(data) {
    const { labelPosition } = this.properties
    // 设置节点text位置
    if (data.text && typeof data.text === 'string') {
      data.text = {
        value: data.text || '',
        x: data.x - (labelPosition === 'left' ? 100 : -100),
        y: data.y
      }
    }
    super.initNodeData(data)
  }
}
export default {
  type: 'image-three',
  view: CloudNode,
  model: CloudNodeModel
}

如何使用插件

sh
npm i @logicflow/extension -s
npm i @logicflow/extension -s

注意,除了导入模块,别忘了导入插件的样式

js
import { LogicFlow } from "@logicflow/core"
import { Control, Menu, DndPanel, SelectionSelect } from "@logicflow/extension"
import '@logicflow/extension/lib/style/index.css'
import { LogicFlow } from "@logicflow/core"
import { Control, Menu, DndPanel, SelectionSelect } from "@logicflow/extension"
import '@logicflow/extension/lib/style/index.css'

方法一,全局使用

js
LogicFlow.use(Control)
LogicFlow.use(Menu)
LogicFlow.use(DndPanel)
LogicFlow.use(SelectionSelect)
LogicFlow.use(Control)
LogicFlow.use(Menu)
LogicFlow.use(DndPanel)
LogicFlow.use(SelectionSelect)

方法二,实例中使用

js
 const lf = new LogicFlow({
      container: document.querySelector("#container"),
      grid: true,
      plugins: [Control, Menu, DndPanel, SelectionSelect]
    })
 const lf = new LogicFlow({
      container: document.querySelector("#container"),
      grid: true,
      plugins: [Control, Menu, DndPanel, SelectionSelect]
    })

Released under the MIT License.