基于jsplumb的流程图展示

背景需求

目前系统中对于工艺流程的展示是纯粹的瀑布式流程,如图所示:

导致的问题是1)没有办法展示复杂的工艺关联 2)在展现上比较简陋不符合客户的审美。

基于以上问题,决定基于jsplumb开发用于复杂工艺流程展示的控件,预计效果图如下:

技术介绍

jsplumb是一款有条件开源的javascript类库,基于SVG提供页面元素的连接。之所以说有条件开源,是因为jsplumb存在两个分支:

1) Toolkit Edition商用版,基于社区版本进行封装提供更丰富的API支持

2) Community Edition社区版,基于MIT和GPL2协议进行开源,提供基础的API功能

jsplumb的核心思想是对于页面元素的连接,在这个思想上对于连接进行了抽象,从而形成了jsplumb的几个基本要素:

2  Anchor – 锚

锚点主要用来定位一个端点的位置,更多的是一个逻辑上而非实体的概念,用户不可以直接创建,而是通过内部的机制生成

2  Endpoint – 端点

作为每一个连接的终点而存在,可以通过编程来显示的创建

2  Connector – 连接器

连接器作为连接的抽象,提供了两个元素之间进行连接的方式

2  Overlay – 镀层

jsplumb通过镀层的方式给为连接器进行用户友好的展示,如通过label的方式

2  Group – 分组

通过分组可以将一组元素作为一个整理,从而进行整体的拖拽和收缩等

一般来说两个端点,一个连接器,0到多个镀层一起工作共同组成了一次连接。每一个端点都有一个关联的锚点。

 

主要难题

在熟悉了jsplumb主要的概念后,可以通过官方API了解一些主要功能。同时可以通过github(https://github.com/jsplumb/jsplumb/tree/master/demo)下载官方demo进行学习。

通过观察,我们发现官方demo中的flowchart比较符合我们的需求,于是我们下载flowchart的demo源码进行研究改造。原始的demo效果图如下:

下面是几个主要的改造点:

1, 代码合并压缩

我们发现在demo中引入了一堆js和css,我们通过合并压缩最后形成了下面三个文件

<script src="jsplumb-link.min.js"></script>

包含jsbezier.js,mottle.js,biltong.js和katavorio.js

<script src="jsplumb-lib.min.js"></script>

包含jsplumb核心类库相关的16个js文件,注意合并的顺序

<script src="jsPlumb_process.js"></script>

和process组件相关的js,基于jsplumb的封装和客户化

2,每一个连接都有个Overlay的label来展示,实际需求不需要

在connectionOverlay定义中发现了同时对于箭头和文字都做了定义,直接将对于label的定义去除即可

    ConnectionOverlays: [
            [ "Arrow", {
                location: 1,
                visible:true,
                width:11,
                length:11,
                id:"ARROW",
                events:{
                    click:function() { alert("you clicked on the arrow overlay")}
                }
            } ],
            [ "Label", {
                location: 0.1,
                id: "label",
                cssClass: "aLabel",
                events:{
                    tap:function() { alert("hey"); }
                }
            }]
        ]

3, 如何禁止新增新连接

在官方demo中可以通过鼠标拖动来新增一条连接,而API并没有对于连接的enable和disable的定义。

Github有人回复说通过设置ConnectionsDetachable 属性来实现,实际效果并不能达到目的。

最终在初始化方法中通过两个方法的组合实现了这个功能

instance.unmakeEveryTarget().unmakeEverySource();

4, 数据的加载和导出

数据导出功能在社区版不提供方法支持,不过我们可以通过一些简单的变通来实现;而导出功能和加载功能是相对应的,实现了数据的导出就可以基于现有的数据结构来实现数据的初始化加载。

以下是导出方法的实现:

function exportData(){
               var blocks=[];                          
                    $(".w").each(function(idx, elem){
                            var elem=$(elem);                           
                            blocks.push({
                                    BlockId:elem.attr('id'),
                                    BlockContent:elem.text(),
                                    BlockX:parseInt(elem.css("left"), 10),
                                    BlockY:parseInt(elem.css("top"), 10)
                            });                              
                    });
                    var serliza=JSON.stringify(blocks);
                    $("#outputText").text(serliza);
           }

主要思路是获取页面元素的id和名称以及他们和容器的相对位置,最终通过json的格式进行存储。至于元素之间的关系通过业务系统保存和维护。

相应的我们可以实现我们的导入方法:

var loadJson = function(data){
          var unpack=JSON.parse(data);
          if(!unpack){
                   return false;
          }       
          unpack.map(function(value, index, array) {
                   var _block = eval(value);
                   newNodeWithName(_block.BlockId,_block.BlockContent, _block.BlockX, _                      block.BlockY);
          });
          return true;
          }
   
var newNodeWithName = function(id, name, x, y){
    var d = document.createElement("div");
        d.className = "w";
        d.id = id;
        d.innerHTML = name.substring(0, 7) + "<div class=\"ep\"></div>";
        d.style.left = x+ "px";
        d.style.top = y+ "px";
        instance.getContainer().appendChild(d);
        initNode(d);
        return d;
}

实现思路是通过解析json获取每一个元素的id和name并通过相对位置在容器中绘制出来。

5, 自动对齐

通过页面拖动的元素不像传统的流程图工具提供自动对齐的功能,我们基于像素级别对元素对齐进行了基本的约束。

Def
 X(e) = 元素e的起始横坐标
 Y(e) = 元素e的起始纵坐标
 W(e) = 元素e的宽度
 H(e) = 元素e的高
 
If abs(Diff(X(a),X(b))) between (0, W(a)) set X(a) = X(b)
If abs(Diff(Y(a),Y(b))) between (0, H(a)) set Y(a) = Y(b)

实现代码:

window.autoAlignment = function(){
            var baseX = Number($(".w").eq(0).css("width").replace("px",""));           
            var baseY = Number($(".w").eq(0).css("height").replace("px",""));
            var thatX=0, thatY=0, thisX = 0, thisY=0, deltaX = 0, deltaY = 0;
            var index = 0;
            var eleArray = $(".w");
            for(var i =0 ; i < eleArray.length; i++){
                   thatX = Number($(eleArray[i]).css("left").replace("px",""));
                   thatY = Number($(eleArray[i]).css("top").replace("px",""));
              for(var j =i+1; j < eleArray.length; j++){
                     thisX = Number($(eleArray[j]).css("left").replace("px",""));
                     thisY = Number($(eleArray[j]).css("top").replace("px",""));
                     deltaX = Math.abs(thisX - thatX);
                     deltaY = Math.abs(thisY - thatY);
                     if(deltaX < baseX && deltaX >0 && deltaY >=baseY){
                       // 需要调整x
                           console.log("x "+ j);
             $(eleArray[j]).css("left",thatX+"px");
                     }
 
                     if(deltaY < baseY && deltaY >0 && deltaX >=baseX){
                           console.log("y "+ j);
                       $(eleArray[j]).css("top",thatY+"px");
                     }
                   }
            }
           //通过repaintEverything完成位置调整后的重绘
            instance.repaintEverything();
         }

6, Zoom

同样由于在社区版不提供zoom的接口,我们只能通过自己来实现zoom功能。

window.setZoom = function (zoom, instance0, transformOrigin, el) {
        transformOrigin = transformOrigin || [0.5, 0.5];
             instance = instance || jsPlumb;
             el = el || instance.getContainer();
             var p = ["webkit", "moz", "ms", "o"],
            s = "scale(" + zoom + ")",
            oString = (transformOrigin[0] * 100) + "% " + (transformOrigin[1] * 100) + "%";
       
             for (var i = 0; i < p.length; i++) {
                 el.style[p[i] + "Transform"] = s;
                 el.style[p[i] + "TransformOrigin"] = oString;
             }
 
             el.style["transform"] = s;
             el.style["transformOrigin"] = oString;
 
             instance.setZoom(zoom, true);
             instance.repaintEverything();
            
         };

实现思路通过监听事件来设置style属性实现滚动,最终调用重绘方法进行整体调整。需要注意的是监听事件应该绑定到容器的上一层,如下红色部分,否则缩放的是整个页面起不到zoom流程图的初衷

<div class="jtk-canvas canvas-wide process-canvas jtk-surface jtk-surface-nopan">
<div style="overflow:visible !important;" id="canvas">
                                   </div>
   </div>

反思

通过以上几点扩展基本能满足复杂流程图的展示,如下:

我们仍然需要解决的问题是流程图初始位置的计算。

目前可以通过一些简单的算法来保证现有流程图不出现重叠,但是如何帮助用户最大程度上节省拖动,还需继续研究。

原文地址:http://blog.51cto.com/luischen/2070573

时间: 2024-11-08 21:26:53

基于jsplumb的流程图展示的相关文章

项目一:第十二天 1、常见权限控制方式 2、基于shiro提供url拦截方式验证权限 3、在realm中授权 5、总结验证权限方式(四种) 6、用户注销7、基于treegrid实现菜单展示

1 课程计划 1. 常见权限控制方式 2. 基于shiro提供url拦截方式验证权限 3. 在realm中授权 4. 基于shiro提供注解方式验证权限 5. 总结验证权限方式(四种) 6. 用户注销 7. 基于treegrid实现菜单展示 2 常见的权限控制方式 2.1 url拦截实现权限控制 shiro基于过滤器实现的   2.2 注解方式实现权限控制 底层:代理技术     3 基于shiro的url拦截方式验权   <!-- 配置过滤器工厂 --> <bean id="

js 基于可视区域 创建展示区域对应的经纬度二维数组

本篇文章主要是分享下基于地图区域创建经纬度二维数组,需要的朋友可以过来参考下 接上个文章, 基于 地图区域,算出这个展示区域对应的点. 经纬度的变化关系:  XY页面展示上, 从左到右维度是增加 如: 106  -> 107 , 从上到下经度是减小  如:30 ->29 创建的时候需要确定从哪开始, 附上代码,如下: function getSizePointsData (latMin,latMax,lngMin,lngMax,lngStep,latStep){ // 区域的最小经度,最大经度

基于echarts实现图表展示

[Author]: kwu 1.引用相关的js框架 <pre name="code" class="javascript"><script type="text/javascript" src="js/esl/esl.js"></script> <script type="text/javascript" src="js/echarts.js"&

基于 bootstrap 的数据展示--bootgrid 样式修改。

bootgrid 的官网案例 http://www.jquery-bootgrid.com/Examples 官方demo 样式 基于项目需要,取消了一些不需要 的功能,修改了源码 最后样式成了这样 以下是修改的内容 修改说明 都在 jquery bootgrid.js 中修改 css 样式中 css: { dropDownMenu: "dropdown btn-group dropup", // must be a unique class name or constellation

基于 HTML5 WebGL 的 水泥工厂可视化系统

前言 如今的制造行业,基于数据进行生产策略制定与管理已经成为一种趋势,特别是 工业4.0 的浪潮下,数据战略已经成为很多制造企业的优先战略,而数据可视化以更直观的方式,帮助指导决策,成为数据分析传递信息的重要工具.通过数据可视化系统助力实现数据驱动的工业世界,为 工业4.0 提供更加灵活.敏捷.高效.个性化的数据支撑.今天就给大家带来一个采用 Hightopo 的 HT for Web 产品实现了一个水泥工厂可视化系统. 系统预览 本案例共有七个子系统: 数据概况 -- 展示全厂年月时间单位的各

根据百度的语音识别例子,展示C如何使用cJSON

前面一篇文章展示了根据百度语音识别例子如何用C调用C++的方法,这篇文章也是基于百度语音识别,展示如何使用cJSON,cJSON是一个用C写的JSON解析器,非常好用,可以用它来生成一个JSON,也可以用来解析JSON的值. 在我写的通过skey获取token的代码中 char *token = (char *)malloc(MAX_BUFFER_SIZE); char host[MAX_BUFFER_SIZE]; snprintf(host, sizeof(host), "https://op

【社交系统ThinkSNS+研发日记三】基于 Laravel Route 的 ThinkSNS+ Component

[社交系统ThinkSNS+研发日记系列] 一.<ThinkSNS+ 基于 Laravel master 分支,从 1 到 0,再到 0.1> 二.<基于 Laravel 开发 ThinkSNS+ 中前端的抉择(webpack/Vue)踩坑日记> 在前面,我介绍了拓展类型,分别有 plus-compnent 和 plus-plugin 两个,这里重点讲以下如何实现 plus-component 的. plus-component 是什么 就如同名字一样,plus 代表的是 Thin

一张优秀的流程图是什么样的?如何制作

说到流程图,想必大家都不陌生,在我们的日常办公及生活中运用的十分广泛,大家平时在绘制流程图的时候是不是觉得流程图很难绘制呢?每当看到别人绘制的流程图都是那么的好看,自己画的时间比别人多,绘制出来的还是那么难看,是不是很伤心呢?其实绘制流程图并不是我们想象的那么难,你之所以觉得难可能是你的渠道没有选对,那么,一张优秀的流程图到底是怎样绘制的呢?下面跟着小编一起走进今天的课堂! 如何制作流程图? 1.打开迅捷画图,点击页面上方[流程图]在跳转的页面点击[立即体验]进入在线绘制界面: 2.在跳转的页面

javascript开源大全

javascript开源大全 Ajax框架-jQuery 可视化HTML编辑器-CKEditor 国产jQuery-UI框架-(jUI)-DWZ 网页开发FireFox插件-Firebug 服务器端的JavaScript脚本-Node.js jQuery图表插件-jQchart HTML5-开发框架-jQuery-Mobile 跨浏览器的RIA框架-ExtJS Flash视频播放器-JW-PLAYER jQuery表单插件-jQuery.form jQuery-File-Upload 可视化HT