使用jsPlumb制作流程图设计器

2023-11-10

jsPlumb是一个比较强大的绘图组件,它提供了一种方法,主要用于连接网页上的元素。在现代浏览器中,它使用SVG或者Canvas技术,而对于IE8以下(含IE8)的古董浏览器,则使用VML技术。

项目主页:http://jsplumbtoolkit.com/

GitHub:https://github.com/sporritt/jsPlumb

作为插件,主要支持jQuery/MooTools/YUI3三种js库,目前最新版本为1.4.1。其中作为jQuery的插件需要用到jQuery、jQuery UI,建议使用最新版本的库避免一些bug。

本文主要使用jQuery 1.9.0、jQuery UI 1.9.2、jsPlumb 1.4.1来绘制流程图。

资源准备

下载jsPlumb,用到以下几个文件:

  • build/js/jquery.jsPlumb-1.4.1-all.min.js
  • build/lib/jquery-1.9.0-min.js
  • build/lib/jquery-ui-1.9.2-min.js
  • build/lib/jquery.ui.touch-punch.min.js (可选) 用于触摸支持

以及build/demo/js/demo-helper-jquery.js,主要用于绘图模式的切换,调整为如下代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
jsPlumb.bind("ready", function() {
  // chrome fix.
  document.onselectstart = function() { return false; };
  // render mode
  var resetRenderMode = function(desiredMode) {
    var newMode = jsPlumb.setRenderMode(desiredMode);
    $(".rmode").removeClass("selected");
    $(".rmode[mode='" + newMode + "']").addClass("selected");
    $(".rmode[mode='canvas']").attr("disabled", !jsPlumb.isCanvasAvailable());
    $(".rmode[mode='svg']").attr("disabled", !jsPlumb.isSVGAvailable());
    $(".rmode[mode='vml']").attr("disabled", !jsPlumb.isVMLAvailable());
    nodeFlow.init();
  };
  $(".rmode").bind("click", function() {
    var desiredMode = $(this).attr("mode");
    if (jsPlumbDemo.reset) jsPlumbDemo.reset();
    jsPlumb.reset();
    resetRenderMode(desiredMode);
  });
  resetRenderMode(jsPlumb.SVG);
});

再准备css样式(从flowchartDemo.css调整而来):

?
1
2
3
4
5
6
7
8
9
10
11
12
.node { border: 1px solid #346789; box-shadow: 2px 2px 19px #aaa; -o-box-shadow: 2px 2px 19px #aaa; -webkit-box-shadow: 2px 2px 19px #aaa; -moz-box-shadow: 2px 2px 19px #aaa; -moz-border-radius: 0.5em; border-radius: 0.5em; opacity: 0.8; filter: alpha(opacity=80); width: 7em; height: 5em; line-height: 5em; text-align: center; z-index: 20; position: absolute; background-color: #eeeeef; color: black; font-family: helvetica; padding: 0.5em; font-size: 1em; }
  .node:hover { box-shadow: 2px 2px 19px #444; -o-box-shadow: 2px 2px 19px #444; -webkit-box-shadow: 2px 2px 19px #444; -moz-box-shadow: 2px 2px 19px #444; opacity: 0.8; filter: alpha(opacity=80); }
 
._jsPlumb_connector { z-index: 4; }
._jsPlumb_endpoint { z-index: 21; cursor: pointer; }
._jsPlumb_dragging { z-index: 4000; }
 
.dragHover { border: 1px dotted red; }
 
.aLabel { background-color: white; padding: 0.4em; font: 12px sans-serif; color: #444; z-index: 21; border: 1px dotted gray; opacity: 0.8; filter: alpha(opacity=80); }
 
.ep { position: absolute; right: 5px; top: 5px; width: 1em; height: 1em; background-color: #994466; cursor: pointer; }

最终引入的资源如下:

?
1
2
3
4
5
6
7
<script src='js/jquery-1.9.0.min.js'></script>
 <script src='js/jquery-ui-1.9.2.min.js'>
<link href="css/demo.css" rel="stylesheet" />
<script src="js/jquery.jsPlumb-1.4.1-all-min.js"></script>
<script src="js/jquery.ui.touch-punch.min.js"></script>
<script src="js/demo.init.js"></script>
<script src="js/demo-helper-jquery.js"></script>

主要实现

参照例子中的Flowchart以及State Machine,实现如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
; (function() {
  window.nodeFlow = {
    init: function() {
      // 设置点、线的默认样式
      jsPlumb.importDefaults({
        DragOptions: { cursor: 'pointer', zIndex: 2000 },
        Endpoint: ["Dot", { radius: 1 }],
        HoverPaintStyle: { strokeStyle: "#42a62c", lineWidth: 2 },
        ConnectionOverlays: [
          ["Arrow", { location: -7, id: "arrow", length: 14, foldback: 0.8 }],
          ["Label", { location: 0.1, id: "label" }]
        ]
      });
      // 连接事件
      jsPlumb.bind("jsPlumbConnection", function(conn, originalEvent) {
        if (conn.connection.sourceId == conn.connection.targetId) {
          jsPlumb.detach(conn);
          alert("不能连接自己!");
        }
        $.each(jsPlumb.getEndpoints(conn.source), function(i, el) {
          if (conn.connection != el.connections[0] &&
            (el.connections[0].targetId == conn.targetId || (el.connections[0].sourceId == conn.targetId && el.connections[0].targetId == conn.sourceId))) {
            jsPlumb.detach(conn);
            alert("不能重复连接!");
            return false;
          }
        });
 
        nodeFlow.onConnectionChange && nodeFlow.onConnectionChange(conn);
        conn.connection.bind("editCompleted", function(o) {
          if (typeof console != "undefined")
            console.log("connection edited. path is now ", o.path);
        });
      });
      // 取消连接事件
      jsPlumb.bind("jsPlumbConnectionDetached", function(conn) {
        nodeFlow.onConnectionChange && nodeFlow.onConnectionChange(conn);
      });
      // 双击取消连接
      jsPlumb.bind("dblclick", function(conn, originalEvent) {
        jsPlumb.detach(conn);
      });
      // 连接的元素
      // 本例中.node既是源头又是目标
      var nodeList = $(".node");
      nodeList.each(function(i, e) {
        // 设置连接的源元素
        jsPlumb.makeSource($(e), {
          filter: ".ep", // .ep元素用于拖动连接
          anchor: "Continuous",
          connector: ["Flowchart", { curviness: 20 }], // 连接的方式为流程图
          connectorStyle: { strokeStyle: "#014ae1", lineWidth: 2 },
          maxConnections: -1 // 最大连接数不限
        });
      });
      // 设置连接目标
      jsPlumb.makeTarget(nodeList, {
        dropOptions: { hoverClass: "dragHover" },
        anchor: "Continuous"
      });
      // 初始化所有连接元素为可拖动
      jsPlumb.draggable(nodeList);
    }
  };
})();

 

保存及载入状态

创建如下html结构作为测试:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<asp:HiddenField runat="server" ID="connections" /><!--保存连接-->
<asp:HiddenField runat="server" ID="locations" /><!--保存元素位置-->
<div class="nodeWrapper" style="height:100%;">
  <div class="node" id='node1' data-id="1">
    <div class="ep"></div>
    <strong>节点1</strong>
  </div>
  <div class="node" id='node1' data-id="1">
    <div class="ep"></div>
    <strong>节点1</strong>
  </div>
  <div class="node" id='node2' data-id="2">
    <div class="ep"></div>
    <strong>节点2</strong>
  </div>
  <div class="node" id='node3' data-id="3">
    <div class="ep"></div>
    <strong>节点3</strong>
  </div>
</div>

在连接状态改变、表单提交时保存连接数据:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 连接改变时把所有的节点位置、连接以JSON格式存入到隐藏域中
nodeFlow.onConnectionChange = function() {
  var connections = [], locations = [], conns = jsPlumb.getAllConnections();
  $.each(conns, function(scopeName, scopeConnections) {
    $.each(scopeConnections, function(i, el) {
      locations.push($.extend(el.source.offset(), { nodeId: el.source.data("id") }));
      locations.push($.extend(el.target.offset(), { nodeId: el.target.data("id") }));
      connections.push({ source: el.source.data("id"), target: el.target.data("id") });
    });
  });
  $("input[id$=connections]").val(JSON.stringify(connections));
  $("input[id$=locations]").val(JSON.stringify(locations));
};
// 提交表单时更新连接数据
$(":submit").click(nodeFlow.onConnectionChange);

通过以上代码,即可以在表单提交时把流程图的状态保存到数据库。

载入数据

调整html代码如下:

?
1
2
3
4
5
6
7
8
9
10
<div class="nodeWrapper" style="height:100%;">
  <asp:Repeater runat="server" ID="nodeList">
    <ItemTemplate>
      <div class="node" id='node<%#Eval("nodeId") %>' data-id="<%#Eval("nodeId") %>" style="<%#GetLocation((int)Eval("nodeId"))%>">
        <div class="ep"></div>
        <strong><%#Eval("nodeName") %></strong>
      </div>
    </ItemTemplate>
  </asp:Repeater>
</div>

从数据库获取节点、位置、连接数据:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
/// <summary>
/// 节点信息
/// </summary>
public class NodeItem
{
    public int NodeId { get; set; }
    public string NodeName { get; set; }
}
/// <summary>
/// 节点位置信息
/// </summary>
public class NodeLocation
{
    public int NodeId { get; set; }
    public double Left { get; set; }
    public double Top { get; set; }
}
/// <summary>
/// 节点连接信息
/// </summary>
public class NodeConnection
{
    public int Source { get; set; }
    public int Target { get; set; }
}
 
List<NodeLocation> locationData;
 
protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        var nodeData = new List<NodeItem>
        {
            new NodeItem{NodeId=1, NodeName="节点1"},
            new NodeItem{NodeId=2, NodeName="节点2"},
            new NodeItem{NodeId=3, NodeName="节点3"}
        };
        nodeList.DataSource = nodeData;
        nodeList.DataBind();
         
        // 从数据库获取位置以及连接
        locationData = JsonConvert.DeserializeObject<List<NodeLocation>>(locationString);
        var connectionData = JsonConvert.DeserializeObject<List<NodeConnection>>(connectionString);
 
        // 连接所有节点
        var builder = new StringBuilder();
        builder.Append("jsPlumb.bind(\"ready\", function() {");
        connectionData.ForEach(c =>
        {
            builder.AppendFormat("jsPlumb.connect({{source: 'node{0}', target: 'node{1}'}});", c.Source.ToString(), c.Target.ToString());
        });
        builder.Append("});");
    }
}
 
/// <summary>
/// 获取位置
/// </summary>
protected string GetLocation(int nodeId)
{
    var ll = locationData.FirstOrDefault(l => l.NodeId == nodeId);
    if (ll != null)
        return "left:" + ll.Left.ToString() + "px;top:" + ll.Top.ToString() + "px;";
    return string.Empty;
}

其中,每次载入时,都需要获取所有连接的数据,并通过脚本把所有节点连接起来。

 

结尾

以上功能只用到jsPlumb少量API,实现起来都比较简单。更多的功能参考官方文档API文档进行扩展。

在使用jsPlumb之前也看过一些其他的js组件:

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用jsPlumb制作流程图设计器 的相关文章

  • 未能加载文件或程序集“Newtonsoft.Json, Version=4.5.0.0"[已解决]

    前两天升级系统架构 升级后打开网页报错了 详细信息如下 未能加载文件或程序集 Newtonsoft Json Version 4 5 0 0 Culture neutral PublicKeyToken 30ad4fe6b2a6aeed 或
  • 前端性能优化

    页面的性能指标 DCL DOMContentLoaded DOM解析完毕 FP First Paint 表示渲染出第一个像素点 FP一般在HTML解析完成或者解析一部分时候触发 FCP First Contentful Paint 表示渲染
  • 打造高大上的Canvas粒子动画

    首先来看下我们准备要做的粒子动画效果是怎么样的 是这样 或者是这样 甚至是这样 很酷炫 那如何去实现类似上面的粒子动画甚至根据自己的喜好去做更多其他轨迹的动画呢 请看下面详细的讲解 技术选择 因为粒子数量很多 而且涉及到图像像素处理 所以这
  • web前端基础:HTML文字和段落标签

    标题标签 h1 h1 h6 h6 段落标签 p p align对齐属性值 值 描述 left 左对齐 right 右对齐 center 居中对齐 justify 对行进行伸展 每行可以有相等的长度 列表标签 有序列表 ol li 列表项 l
  • vue中实现el-table点选和鼠标框选功能

    实现思路 项目有两个需求 既能在el table实现点选又能实现鼠标框选 一 点选实现思路 使用el table的cellClick方法 1 直接给点击的cell添加类名 cell classList add blue cell 然后把获取
  • css3颜色渐变:css3如何实现背景颜色渐变?

    为了开发网页的美观 css3背景颜色渐变是经常会用到的 那么 css3背景颜色渐变如何设置呢 本篇文章我们就来介绍关于css颜色渐变背景的设置方法 我们要知道的是css3渐变有两种类型 css3线性渐变和css3径向渐变 下面我们就来看一下
  • react组件中设置多个className

    错误写法
  • web基础学习(十)CSS3之 @keyframes 、animation

    css3新增属性 keyframes 关键帧 可以帮助开发者不必依赖JavaScript 只使用css代码完成动画制作 那么如何使用 keyframes呢 这里有两个重要知识点 1 keyframes 定义关键帧 语法 keyframes
  • 无法访问目标主机的原因及其和请求超时的区别

    使用ping命令时经常会遇到这两种情况 就表示网络出了问题 无法访问目标主机的原因 可以看到 无法访问目标主机 是来自一个IP的回复 实际上那个IP是一个路由器 因此 无法访问目标主机 实际上数据是发出去并且收到回复的 只不过收到的回复是别
  • 基础15:npm、yarn、pnpm

    npm2 用 node 版本管理工具把 node 版本降到 4 那 npm 版本就是 2 x 了 执行 npm init npm install express 可以看到node modules目录如下 可以看到 npm2的node mod
  • 发布npm包-简要记录

    1注册账号 注册npm账号 需要邮箱 激活npm账号 npm账号注册成功以后会收到邮件 邮件中有个链接 点进去进行激活 2创建项目 npm init 创建项目 name 命名规则 不能包含大写字母 空格及下滑线 version 创建时候默认
  • 关于2018网易游戏web前端实习生面试经历

    去年报名的网易前端面试 没想到过了3个月居然收到了面试的通知 心里也是激动 花了一天时间面试 自己总结一下面试过的问题 问题可能不全 但是这些是我所能记起来的问题 一面 1 css高度坍塌 两个盒子 一个下边据20px 一个上边据50px
  • laravel路由

    路由 在laravel中 定义路由的地方在routes web php文件中 在使用laravel前必须先定义路由 然后才能在浏览器中访问 routes文件夹中还有一个api php 用于定义api路径 最简单的路由 Route get f
  • 解决css中上下外边距(margin)在父元素中溢出的问题

    两个办法 给父元素添加overflow hidden 即可 给父元素添加透明边框border 1px solid transparent 给父元素添加伪元素 before after content display table 改变光标的颜
  • windows下配置Mysql-5.7.9服务

    第一步 从官方网站下载 mysql 5 7 9 winx64 zip 第二步 解压缩 在根目录下复制my default ini 改名为my ini 第三步 初始化mysql目录 bin mysqld initialize user mys
  • 禁止ios浏览器页面上下滚动 (橡皮筋效果)弹性滚动 微信的下拉回弹

    发现之前阻止页面滚动的代码e preventDefault代码失效了 于是自己折腾了一番 找到了解决办法 一 前言 浏览器在移动端有一个默认触摸滚动的效果 让我们感触最深的莫过于微信浏览器里面 下拉时自带橡皮筋的效果 然而在开发的时候我们经
  • uni-app项目中使用scss语法

    最近正在学习uni app开发 我先把文档浅略翻了遍 发现组件和接口几乎都是按照微信小程序走 但是视图层上的语法又是按照vue的语法走的 所以开发过程一定要注意这点 然后我想在uni app项目中使用scss语法 但是具体怎么安装呢 历经曲
  • node中间件是什么意思?

    node中间件是什么意思 2020 09 11 16 11 17分类 常见问题 Node js答疑阅读 1757 评论 0 中间件是一种独立的系统软件或服务程序 分布式应用软件借助这种软件在不同的技术之间共享资源 中间件位于客户机 服务器的
  • React 笔记 jsx

    严格约定 React 组件必须以 大写字母开头 而 HTML 标签则必须是小写字母 React JSX JSX 是由 React 推广的 JavaScript 语法扩展 用于表达组件的 特殊语法的 js 函数 要求标签必须闭合 返回的组件必
  • 第8章 多媒体嵌入

    学习目标 了解视频 音频嵌入技术 能够总结HTML5视频 音频嵌入技术的优点 了解常用的视频文件格式和音频文件格式 能够归纳HTML5支持的视频和音频格式 掌握HTML5中视频的嵌入方法 能够在HTML5页面中添加视频文件 掌握HTML5中

随机推荐

  • 复盘:第一次面向研发的技术写作培训

    上周我完成了第一次真正意义上的 面向研发人员的技术写作培训 作为一只 在公共场合讲话紧张得要死的人类 整个过程 竟然自我感觉表现得很不错 培训后也收到了研发同事积极 鼓励的反馈 朋友们 我感觉自己膨胀了啊 飘了啊 这么说 恐怕是有点臭屁啦
  • 【Python小游戏】Python实现井字棋游戏

    实现过程 类TicTacToe的构造函数 init 初始化棋盘board和当前玩家current player print board 方法用于打印当前的棋盘 make move row col 方法用于让当前玩家落子 check win
  • 电子信息工程专业毕设题目汇总100例

    文章目录 1前言 2 如何选题 2 1 嵌入式开发方向 2 2 物联网方向 2 3 移动通信方向 2 4 人工智能方向 2 5 算法研究方向 2 6 移动应用开发方向 2 7 网络通信方向 2 8 学长作品展示 4 最后 1前言 近期不少学
  • xCode运行出现“Executable Not Found“的解决办法

    这个是由于部分代码文件或plist等没有被导入 无法生成二进制执行文件导致 解决办法 请仔细检查所有代码文件有无完整导入 请仔细检查所有代码文件有无完整导入 请仔细检查所有代码文件有无完整导入
  • HTML中关于边框(border)的使用

    同时设置上下左右边框 border 宽度 样式 颜色 其中颜色可以省略 默认黑色 样式不能省略分别设置上右下左边框 1 border top 宽度 样式 颜色 顶部 border right 宽度 样式 颜色 右边 border botto
  • docker搭建的mysql8.0中文乱码问题

    手把手教你如何在mysql 中使用中文编码 1 首先在docker中拉取好一个最新的mysql镜像以后 创建一个容器 docker run d p 13306 3306 e MYSQL ROOT PASSWORD xxxxxx name M
  • Hadoop的安装与配置(非常重要)

    官方的原生配置文档Hadoop3 1 0 HDFS的组成 NameNode secondaryNameNode DataNode 这是以主从模式来运行的 前两个在maser节点上 最后一个在slave节点上 1 解压hadoop安装包 要学
  • jQuery--全选全不选功能

    function 全选不选功能模块 checkall change function console log this prop checked 让俩个全选按钮同时生效 当点击checlall时候 this prop checked 为tr
  • Bug:Unable to determine application id: com.android.tools.idea.run.ApkProvisionException: No outputs

    问题描述 Android Studio 3 6 3版本 运行之前项目时 项目可正常编译出apk文件 但无法自动安装到设备 手动通过命令行将apk安装到设备 也存在无法调试的问题 问题展示 解决方法 1 对Android Studio进行降级
  • M - Marbles Lucky Distribution(贪心)

    M Marbles Lucky Distributionhttps vjudge csgrandeur cn problem Gym 101845M Juan have N red marbles M blue marbles and K
  • 用定时器设计门铃,按下按键时蜂鸣器发出叮咚的门铃声

    叮咚 产生的方式 仿真 程序代码 include
  • 作为一个江苏人,我眼中的苏宁

    江苏人 人人都在苏宁电器的地面商店里买过东西 我也不例外 听说张近东要转让苏宁易购的股份给一家疑似国企或国有基金 作为一个地道的江苏人 感慨良多 中国有4个电商平台 天猫 京东 拼多多和苏宁易购 还有大商 国美什么 已经彻底掉队 暂且不提
  • 推荐你个软件 TDengine

    推荐你个软件 https github com taosdata TDengine 时序数据库 效率非常高 https constd com 2019 07 26 tdengine doc https www taosdata com cn
  • Linux 桌面虚拟化技术 KVM

    KVM 是 Kernel based Virtual Machine 的简称 是一个开源的系统虚拟化模块 自Linux 2 6 20之后集成在Linux的各个主要发行版本中 它使用Linux自身的调度器进行管理 所以相对于Xen 其核心源码
  • Easylogging介绍和简单使用

    一 Easylogging简介 Easyloggingpp是一个C 开源log库 其在github的地址 https github com muflihun easyloggingpp Easyloggingpp最大的特点是只需一个头文件
  • 最全面计算机英语单词列表(四)

    作为一名开发者 不管是自己写代码还是阅读英文文档 英语水平对于开发进度有很大的影响 业余时间简单整理了计算机开发中常见的英语单词 不限于前端开发 再此和朋友们分享 单词较多 为了阅读体验良好分成几个部分 下面是其他部分的链接 最全面计算机英
  • go get 与git clone的区别

    一直对go get 和git clone 区别不开 go get 的参数说明 d 只下载不安装 f 只有在你包含了 u参数的时候才有效 不让 u去验证import中的每一个都已经获取了 这对于本地fork的包特别有用 fix 在获取源码之后
  • 布局优化之ViewStub原理

    1 概述 在进行Android程序开发时 除了要实现基本功能外 还要关注应用的性能 内存占用少 程序稳定 响应速度快等 懒加载 就是为了让程序尽可能快地启动而提出的一个优化策略 即让那些对用户不重要或者不需要立即显示的布局控件做延迟加载 只
  • Linux下进程与多线程之间的共享资源

    在Linux 下 程序或可执行文件是一个静态的实体 它只是一组指令的集合 没有执行的含义 进程是一个动态的实体 有自己的生命周期 线程是操作系统进城调度器可以执行的最小执行单元 同一个进程中的各个线程都有自己独立的线程ID 用来标识线程 如
  • 使用jsPlumb制作流程图设计器

    jsPlumb是一个比较强大的绘图组件 它提供了一种方法 主要用于连接网页上的元素 在现代浏览器中 它使用SVG或者Canvas技术 而对于IE8以下 含IE8 的古董浏览器 则使用VML技术 项目主页 http jsplumbtoolki