用zrender实现工作流图形化设计(附范例代码)
来源:互联网 发布:手机电子琴软件 编辑:程序博客网 时间:2024/05/17 23:09
公司研发的管理系统有工作流图形化设计和查看功能,这个功能的开发历史比较久远。在那个暗无天日的年月里,IE几乎一统江湖,所以顺理成章地采用了当时红极一时的VML技术。
后来的事情大家都知道了,IE开始走下坡路,VML这个技术现在早已灭绝,导致原来的工作流图形化功能完全不能使用,所以需要采用新技术来重写工作流图形化功能。
多方对比之后,决定采用zrender库来实现(关于zrender库的介绍,请看http://ecomfe.github.io/zrender/),花了一天的时间,终于做出了一个大致的效果模型,如下图所示:
流程图由两类部件组成:活动部件和连接弧部件,每一类部件包含多个性状不同的部件。
以活动部件为例,圆形的是开始活动,平行四边形是自动活动,长方形是人工活动,等等。
在代码实现上,定义了Unit(部件基类),所有的部件都继承自这个基类。通过Graph类来管理整个流程图,包括所有部件、上下文菜单等等都由Graph来统一管理和调度,代码如下:
var
Libra = {};
Libra.Workflow = {};
Libra.Workflow.Graph =
function
(type, options){
var
graph =
this
,
activities = {},
transitions = {};
var
zrenderInstance,
contextMenuContainer;
this
.type = type;
this
.addActivity =
function
(activity){
activity.graph = graph;
activities[activity.id] = {object:activity};
};
this
.getActivity =
function
(id){
return
activities[id].object; };
this
.addTransition =
function
(transition){
transition.graph = graph;
transitions[transition.id] = {object:transition};
};
function
modElements(shapes){
shapes.each(
function
(shape){ zrenderInstance.modElement(shape); });
return
shapes;
}
// 当前正在拖放的节点
var
dragingActivity =
null
;
// 活动节点拖放开始
this
.onActivityDragStart =
function
(activity){ dragingActivity = activity; };
// 活动节点拖放结束
this
.onActivityDragEnd =
function
(){
if
(dragingActivity) refreshActivityTransitions(dragingActivity);
dragingActivity =
null
;
};
// 拖动过程处理
function
zrenderInstanceOnMouseMove(){
if
(dragingActivity !=
null
) refreshActivityTransitions(dragingActivity);
}
// 刷新活动相关的所有连接弧
function
refreshActivityTransitions(activity){
var
activityId = activity.id;
for
(
var
key
in
transitions){
var
transition = transitions[key].object;
if
(transition.from === activityId || transition.to == activityId){
zrenderInstance.refreshShapes(modElements(transition.refresh(graph)));
}
}
}
// 当前选中的部件
var
selectedUnit =
null
;
this
.onUnitSelect =
function
(unit){
if
(selectedUnit) zrenderInstance.refreshShapes(modElements(selectedUnit.unselect(graph)));
zrenderInstance.refreshShapes(modElements(unit.select(graph)));
selectedUnit = unit;
};
// 记录当前鼠标在哪个部件上,可以用来生成上下文相关菜单
var
currentUnit =
null
;
this
.onUnitMouseOver =
function
(unit){
currentUnit = unit;
};
this
.onUnitMouseOut =
function
(unit){
if
(currentUnit === unit) currentUnit =
null
;
};
// 上下文菜单事件响应
function
onContextMenu(event){
Event.stop(event);
if
(currentUnit) currentUnit.showContextMenu(event, contextMenuContainer, graph);
}
this
.addShape =
function
(shape){
zrenderInstance.addShape(shape);
};
// 初始化
this
.init =
function
(){
var
canvasElement = options.canvas.element;
canvasElement.empty();
canvasElement.setStyle({height: document.viewport.getHeight() +
'px'
});
zrenderInstance = graph.type.zrender.init(document.getElementById(canvasElement.identify()));
for
(
var
key
in
activities){ activities[key].object.addTo(graph); }
for
(
var
key
in
transitions){ transitions[key].object.addTo(graph); }
// 创建上下文菜单容器
contextMenuContainer =
new
Element(
'div'
, {
'class'
:
'context-menu'
});
contextMenuContainer.hide();
document.body.appendChild(contextMenuContainer);
Event.observe(contextMenuContainer,
'mouseout'
,
function
(event){
// 关闭时,应判断鼠标是否已经移出菜单容器
if
(!Position.within(contextMenuContainer, event.clientX, event.clientY)){
contextMenuContainer.hide();
}
});
// 侦听拖动过程
zrenderInstance.on(
'mousemove'
, zrenderInstanceOnMouseMove);
// 上下文菜单
Event.observe(document,
'contextmenu'
, onContextMenu);
};
// 呈现或刷新呈现
this
.render =
function
(){
var
canvasElement = options.canvas.element;
canvasElement.setStyle({height: document.viewport.getHeight() +
'px'
});
zrenderInstance.render();
};
};
/*
* 部件(包括活动和连接弧)
*/
Libra.Workflow.Unit = Class.create({
id:
null
,
title:
null
,
graph:
null
,
// 当前是否被选中
selected:
false
,
// 上下文菜单项集合
contextMenuItems: [],
initialize:
function
(options){
var
_this =
this
;
_this.id = options.id;
_this.title = options.title;
},
createShapeOptions:
function
(){
var
_this =
this
;
return
{
hoverable :
true
,
clickable :
true
,
onclick:
function
(params){
// 选中并高亮
_this.graph.onUnitSelect(_this);
},
onmouseover:
function
(params){ _this.graph.onUnitMouseOver(_this); },
onmouseout:
function
(params){ _this.graph.onUnitMouseOut(_this); }
};
},
addTo:
function
(graph){},
// 刷新显示
refresh:
function
(graph){
return
[]; },
// 选中
select:
function
(graph){
this
.selected =
true
;
return
this
.refresh(graph);
},
// 取消选中
unselect:
function
(graph){
this
.selected =
false
;
return
this
.refresh(graph);
},
// 显示上下文菜单
showContextMenu:
function
(event, container, graph){
container.hide();
container.innerHTML =
''
;
var
ul =
new
Element(
'ul'
);
container.appendChild(ul);
this
.buildContextMenuItems(ul, graph);
// 加偏移,让鼠标位于菜单内
var
offset = -5;
var
rightEdge = document.body.clientWidth - event.clientX;
var
bottomEdge = document.body.clientHeight - event.clientY;
if
(rightEdge < container.offsetWidth)
container.style.left = document.body.scrollLeft + event.clientX - container.offsetWidth + offset;
else
container.style.left = document.body.scrollLeft + event.clientX + offset;
if
(bottomEdge < container.offsetHeight)
container.style.top = document.body.scrollTop + event.clientY - container.offsetHeight + offset;
else
container.style.top = document.body.scrollTop + event.clientY + offset;
container.show();
},
// 创建上下文菜单项
buildContextMenuItems:
function
(container, graph){
var
unit =
this
;
unit.contextMenuItems.each(
function
(item){
item.addTo(container);
});
}
});
zrender默认已经支持了对图形的拖动,所以活动部件的拖动只需要设置dragable属性为真即可。不过虽然活动部件可以拖动,但活动部件上的连接线不会跟着一起动,这需要侦听拖动开始事件、拖动结束事件以及拖动过程中的鼠标移动事件,来实现连接线的实时重绘。在Graph中侦听鼠标移动事件,就是为了实现连接线等相关图形的实时重绘。
每个部件都规划了八个连接点,默认情况下,连接弧不固定与某个连接点,而是根据活动部件的位置关系,自动找出最近的连接点,所以在拖动活动部件的时候,可以看到连接弧在活动部件上的连接点在不断变化。
上面只是以最简化的方式实现了工作流图形化设计的基本功能,完善的图形化设计应包含曲线、连接点的拖放等等,如下图所示:
上面是公司产品中的工作流图形化设计功能,功能相对于上面的范例要完善许多,但基本原理不变,无非就是细节处理更多一些。
特别是在画的地方花了很多时间,中学的平面几何知识几乎都忘记了,所以做起来花了不少功夫,这部分准备以后专门写篇文章来详谈。
本文的结尾会给出前期建模测试阶段的完整代码下载,是前期代码,不是最终代码,原因你懂的,见谅。
http://files.cnblogs.com/files/rrooyy/WorkflowGraphic.zip
- 用zrender实现工作流图形化设计(附范例代码)
- zrender自定义图形
- 图形解锁验证码破解(附Python代码)
- zrender源码分析(1)
- 汉诺塔游戏的设计(附代码)
- 《大话设计模式》读书笔记,附Java代码实现
- 教你用TensorFlow实现神经网络(附代码)
- 教你用TensorFlow实现神经网络(附代码)
- 教你用TensorFlow实现神经网络(附代码)
- 用Python实现BP神经网络(附代码)
- COM设计与实现范例
- ZRender实现粒子网格动画实战
- 手把手教你用R实现标记化(附代码、学习资料、语料库)
- echarts内通过zrender添加文字,图形等
- jQuery实现翻滚图像(附代码)
- websocket消息推送实现(附代码)
- 哈希表详解(附实现代码)
- JFrame使用范例(附源代码下载)
- 操作系统内存地址
- cluster - 簇
- Codeforces Round #382 (Div. 1) C. Ostap and Tree
- Python中类和实例关系
- Android中的scaleType
- 用zrender实现工作流图形化设计(附范例代码)
- cli - 命令行界面
- 感悟
- form表单的两种提交方式,submit和button的用法
- bus - 总线
- spring boot Mybatis多数据源配置
- environment variables - 环境变量
- 高精度算法之加法
- RDD基础学习-[3]RDD聚合函数基础