今年由于项目上需要给客户的流程管理系统进行升级,其中包含流程的可视化。于是在网上找一些可以用的轮子,考察了D3,js、GooFlow.js、G6-Editor等工具后,发现D3,js学习成本太高,G6-Editor功能基本够用,但是不能放大缩小图形和移动连接线是硬伤,而且GooFlow.js和G6-Editor目前已经闭源,于是就决定自己试着写一个流程编辑器。

由于之前画流程图基本使用的都是Visio,所以编辑器的操作风格基本和Visio一致,也吸收了一些G6-Editor的操作风格。

HTML内容如下:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Test Page</title>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="shortcut icon" href="assets/img/favicon.jpg">
<link rel="stylesheet" href="assets/css/bootstrap.min.css" type="text/css" />
<link rel="stylesheet" href="assets/css/font-awesome.min.css" type="text/css" />
<link rel="stylesheet" href="assets/css/editor.css" type="text/css" />
</head>
<body>
<div style="width:99%">
<div id="toolbar" class="toolbar">
<i data-command="undo" class="fa fa-mail-reply" title="撤销"></i>
<i data-command="redo" class="fa fa-mail-forward" title="重做"></i>
<i data-command="delete" class="fa fa-trash-o" title="删除"></i>
<i data-command="save" class="fa fa-save" title="保存"></i>
</div>
<div class="row" style="width:99%">
<div class="col-md-2">
<div id="itempannel" class="itempannel">
<div class="node" data-type="node" data-shape="capsule" data-size="80*40" data-label="开始" data-color="rgba(200,255,200,1)" data-edgecolor="rgb(10,255,10,1)" >
<img draggable="false" src="assets/img/start.svg" alt="" scrset="">
</div>
<div class="node" data-type="node" data-shape="rect" data-size="100*50" data-label="常规节点" data-color="#E6F7FF" data-edgecolor="#1890FF" >
<img draggable="false" src="assets/img/normal.svg" alt="" scrset="">
</div>
<div class="node" data-type="node" data-shape="rhomb" data-size="100*80" data-label="判断" data-color="#E6FFFB" data-edgecolor="#5CDBD3" >
<img draggable="false" src="assets/img/judge.svg" alt="" scrset="">
</div>
<div class="node" data-type="node" data-shape="capsule" data-size="80*40" data-label="结束" data-color="rgba(255,200,200,1)" data-edgecolor="rgb(255,50,50,1)" >
<img draggable="false" src="assets/img/end.svg" alt="" scrset="">
</div>
</div>
</div>
<div class="col-md-7">
<div id="canvas"></div>
</div>
<div class="col-md-3">
<div id="detailpannel" class="detailpannel">
<div><b>属性详情</b></div>
<div id="nodedetail" style="display:none">
<div class="row">
<label class="col-sm-4 control-label">
节点名称
</label>
<div class="col-sm-8">
<input id="nodename" type="text" class="form-control" />
</div>
</div>
<div class="row">
<label class="col-sm-4 control-label">
字体大小
</label>
<div class="col-sm-8">
<input id="fontsize_node" type="text" class="form-control" />
</div>
</div>
<div class="row">
<label class="col-sm-4 control-label">
自定义属性
</label>
<div class="col-sm-8">
<input id="nodecustom" type="text" class="form-control" />
</div>
</div>
</div>
<div id="linedetail" style="display:none">
<div class="row">
<label class="col-sm-4 control-label">
线条名称
</label>
<div class="col-sm-8">
<input id="linename" type="text" class="form-control" />
</div>
</div>
<div class="row">
<label class="col-sm-4 control-label">
字体大小
</label>
<div class="col-sm-8">
<input id="fontsize_line" type="text" class="form-control" />
</div>
</div>
<div class="row">
<label class="col-sm-4 control-label">
自定义属性
</label>
<div class="col-sm-8">
<input id="linecustom" type="text" class="form-control" />
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="assets\js\popper.min.js"></script>
<script type="text/javascript" src="assets\js\jquery.min.js"></script>
<script type="text/javascript" src="assets\js\bootstrap.min.js"></script>
<script type="text/javascript" src="assets\js\graphEditor.js"></script>
<script type="text/javascript" src="assets\js\index.js"></script>
</body>
</html>

其中graphEditor.js就是编辑器的js文件,编辑器的画布加载在id为canvas的容器中。

index.js文件内容如下:

const data = {};
//节点格式
data.nodes = [
{
color:"rgba(200,200,255,1)",
customProperties: {
nodestyle: "11"
},
edgeColor:"#1890FF",
id: "9471a484-7a2e-4ca9-949d-6ec4b495a1a2",
index: 1,
label: "测试加载节点",
shape: "rect",
size: "100*50",
type: "node",
x: 282,
y: 222
},
{
color:"rgba(200,200,255,1)",
customProperties: {
nodestyle: "22"
},
edgeColor:"#1890FF",
id: "9471a484-7a2e-4ca9-949d-6ec4b495a1a3",
index: 2,
label: "测试加载判断",
shape: "rhomb",
size: "100*80",
type: "node",
x: 282,
y: 422,
fontSize: "12px"
}
]
//线条格式
data.lines = [
{
color:"rgba(0,0,0,1)",
customProperties: {
btnstyle: "test"
},
index: 3,
label: "测试线条1",
fontSize: "12px",
fromConOrder: 3,
source: "9471a484-7a2e-4ca9-949d-6ec4b495a1a2",
type: "line",
endPoint: { x: 81, y: 210 },
inflexionPoint: [
{ x: 282, y: 272 },
{ x: 81, y: 272 }
]
},
{
color:"rgba(0,0,0,1)",
customProperties: {
btnstyle: "test"
},
index: 4,
label: "测试线条2",
fontSize: "14px",
toConOrder: 1,
startPoint: { x: 555, y: 447 },
target: "9471a484-7a2e-4ca9-949d-6ec4b495a1a3",
type: "line",
inflexionPoint: [
{ x: 555, y: 357 },
{ x: 282, y: 357 }
]
}
]
//创建编辑器
const editor = new GraphEditor();
//编辑器加载数据
//editor.load(data); //绑定元素容器
editor.itemPannel = 'itempannel';
//绑定详细属性栏
editor.detailPannel = 'detailpannel';
//绑定工具栏
editor.toolbar = 'toolbar';
const nodeDetail = $('#nodedetail');
const lineDetail = $('#linedetail');
const nodeName = $('#nodename');
const lineName = $('#linename');
const nodeCustom = $('#nodecustom');
const lineCustom = $('#linecustom');
const nodeFontSize = $('#fontsize_node');
const lineFontSize = $('#fontsize_line'); //选择元素事件
editor.on('selectedElement', function(e) {
if(e.element.isNode){
lineDetail.hide();
nodeDetail.show();
nodeName.val(e.element.label);
nodeFontSize.val(e.element.fontSize);
nodeCustom.val(e.element.customProperties.nodestyle);
} else if (e.element.isLine){
nodeDetail.hide();
lineDetail.show();
lineName.val(e.element.label);
lineFontSize.val(e.element.fontSize);
lineCustom.val(e.element.customProperties.btnstyle);
}
});
//点击画布事件
editor.on('click', function(e) {
if(!e.element){
nodeDetail.hide();
lineDetail.hide();
}
});
//撤销事件
editor.on('undo', function(e) {
nodeDetail.hide();
lineDetail.hide();
//console.log(e.data);
});
//重做事件
editor.on('redo', function(e) {
nodeDetail.hide();
lineDetail.hide();
//console.log(e.data);
});
//保存事件
editor.on('save', function(e) {console.log(e.data)}); nodeName.change(function(e){
updateEditor(e, 'label');
}); lineName.change(function(e){
updateEditor(e, 'label');
}); nodeFontSize.change(function(e){
updateEditor(e, 'fontSize');
}); lineFontSize.change(function(e){
updateEditor(e, 'fontSize');
}); nodeCustom.change(function(e){
updateEditor(e, 'nodestyle');
}); lineCustom.change(function(e){
updateEditor(e, 'btnstyle');
}); function updateEditor(e, name){
const property = {};
property.name = name;
property.value = $('#' +e.target.id).val();
if (editor.selectedElement){
editor.update(editor.selectedElement.id, property);
};
}

GraphEditor方法列表:

  • load(data):用于加载数据,data的结构参考index.js。

node节点的属性如下:

color:节点填充颜色
edgeColor:节点边框颜色
shape:“rect”为矩形,“rhomb”为菱形,“capsule”为胶囊
id:节点编号
index:节点层级
label:文字内容
fontSize:文字大小
size:节点的长和宽,例如“100*50”
type:“node”
x:节点x轴坐标
y:节点y轴坐标
customProperties:自定义属性,用于保存业务相关数据
 
line线条的属性如下:
color:线条颜色
id:线条编号
index:线条层级
label:文字内容
fontSize:文字大小
type:“line”
source:线条起始节点的编号
target:线条结束节点的编号
fromConOrder:起始节点对应的锚点编号
toConOrder:结束节点对应的锚点编号
startPoint:起始点坐标,用于无起始节点情况
endPoint:结束点坐标,用于无结束节点情况
inflexionPoint:线条拐点集合
  • update(id,property)

id:元素的编号

property:{ name: 要更新的属性名称, value:更新的值 }

GraphEditor事件列表:

  • selectedElement:选中任何canvas元素触发,事件对象的element属性存放选中的元素
  • click:点击canvas容器触发
  • undo:撤销操作触发
  • redo:重做操作触发
  • save:保存操作触发,事件对象的data属性存放画布中所有的元素,编辑器对象的data属性也包含画布所有元素

GraphEditor快捷键列表:

  • Delete:删除画布上的元素
  • Ctrl+z:撤销
  • Ctrl+y:重做
  • Ctrl+s:保存

Demo下载地址:https://files.cnblogs.com/files/lixincloud/GraphEditorDemo.rar

基于canvas的流程编辑器的更多相关文章

  1. 最好用的流程编辑器bpmn-js系列之基本使用

    最好用的流程编辑器bpmn-js系列文章 BPMN(Business Process Modeling Notation)是由业务流程管理倡议组织BPMI(The Business Process M ...

  2. 基于canvas的二维码邀请函生成插件

    去年是最忙碌的一年,实在没时间写博客了,看着互联网行业中一个又一个人的倒下,奉劝大家,健康要放在首位,保重身体.好了,言归正传,这是17年的第一篇博文,话说这天又是产品同学跑过来问我说:hi,lenn ...

  3. 基于NoCode构建简历编辑器

    基于NoCode构建简历编辑器 基于NoCode构建简历编辑器,要参加秋招了,因为各种模版用起来细节上并不是很满意,所以尝试做个简单的拖拽简历编辑器. 描述 Github | Resume DEMO ...

  4. Tridiv:基于 Web 的 CSS 编辑器,创建炫丽 3D 图形

    Tridiv 是一个基于 Web 的编辑器,使用 CSS 创建 3D 形状.它提供了一个传统的四个面板的操作界面,给出了从每个平面的视图,以及一个预览窗格中示出的最终的效果.使用 Tridiv 可以创 ...

  5. Particles.js基于Canvas画布创建粒子原子颗粒效果

    文章目录 使用方法 自定义参数 相关链接 Particles.js是一款基于HTML5 Canvas画布的轻量级粒子动画插件,可以设置粒子的形状.旋转.分布.颜色等属性,还可以动态添加粒子,效果非常炫 ...

  6. 基于Activiti的流程应用开发平台JSAAS-WF V5.3

    第1章 产品概述及体系架构 1.1.概述 红迅JSAAS-WF工作流平台V5是广州红迅软件有限公司面向合作伙伴以及有IT运维团队中大型企业提供新一代的流程管理产品,它基于流行的JAVA开源技术上构建, ...

  7. 7个华丽的基于Canvas的HTML5动画

    说起HTML5,可能让你印象更深的是其基于Canvas的动画特效,虽然Canvas在HTML5中的应用并不全都是动画制作,但其动画效果确实让人震惊.本文收集了7个最让人难忘的HTML5 Canvas动 ...

  8. 微信小程序-基于canvas画画涂鸦

    代码地址如下:http://www.demodashi.com/demo/14461.html 一.前期准备工作 软件环境:微信开发者工具 官方下载地址:https://mp.weixin.qq.co ...

  9. N个富文本编辑器/基于Web的HTML编辑器

    转自:http://www.cnblogs.com/lingyuan/archive/2010/11/15/1877447.html 基于WEB的HTML 编辑器,WYSIWYG所见即所得的编辑器,或 ...

随机推荐

  1. Django学习之文件下载

    在实际的项目中很多时候需要用到下载功能,如导excel.pdf或者文件下载,当然你可以使用web服务自己搭建可以用于下载的资源服务器,如nginx,这里我们主要介绍django中的文件下载. 我们这里 ...

  2. Executor线程池原理详解

    线程池 线程池的目的就是减少多线程创建的开销,减少资源的消耗,让系统更加的稳定.在web开发中,服务器会为了一个请求分配一个线程来处理,如果每次请求都创建一个线程,请求结束就销毁这个线程.那么在高并发 ...

  3. Linux学习资料网站汇总链接(持续更新ing)

    排名不分先后. 学海无涯苦作舟. 博客: 1.slmba:LINUX博客原创大牛 2.edsionte's TechBlog:Linuxer (他的友情链接中还有一堆Linuxer,被公司屏蔽进不去. ...

  4. 深入理解C# 委托(delegate)-戈多编程

    今天来谈谈委托,深入理解委托,本文来自各大神经验总结. 1.委托是什么? 委托类型的声明与方法签名相似. 它有一个返回值和任意数目任意类型的参数,是一种可用于封装命名方法或匿名方法的引用类型. 委托类 ...

  5. 使用FastReport报表工具生成报表PDF文档

    在我们开发某个系统的时候,客户总会提出一些特定的报表需求,固定的报表格式符合他们的业务处理需要,也贴合他们的工作场景,因此我们尽可能做出符合他们实际需要的报表,这样我们的系统会得到更好的认同感.本篇随 ...

  6. LeetCode_933-Number of Recent Calls

    求最近3000毫秒内有多少次调用请求,每一次ping的时间一定比上一次的时间高:解法可以判断最后面一个数t1与最前一个数t2的差不大于3000毫秒,如果大于就直接舍弃,t1与t2之间的个数就是请求次数 ...

  7. http服务端架构演进

    摘要 在详解http报文相关文章中我们介绍了http协议是如何工作的,那么构建一个真实的网站还需要引入组件呢?一些常见的名词到底是什么含义呢? 什么叫正向代理,什么叫反向代理 服务代理与负载均衡的差别 ...

  8. sql数据文件导入数据库

    1.首先通过xshell连接数据库服务器,执行命令mysql -u root -p 命令,按照提示输入密码.连接上数据库. 2.在连接终端上执行命令create database JD_Model; ...

  9. ‎Cocos2d-x 学习笔记(15.3) EventDispatcher DirtyFlag 脏标记

    1. 定义 用枚举定义脏标记的4种类型. enum class DirtyFlag { NONE = , FIXED_PRIORITY = << , SCENE_GRAPH_PRIORIT ...

  10. caffe中softmax源码阅读

    (1) softmax函数                                      (1) 其中,zj 是softmax层的bottom输入, f(zj)是softmax层的top输 ...