最近B/S程序中需要使用JS画流程图,找了一圈,试了几个,最终选择jsplumb来做,这里记下笔记
1、jsplumb官网地址:https://jsplumbtoolkit.com/,看下效果图:
2、官网Demo路径下的Toolkit+Community章节Flowchart试例中有关于绑定线条的几个事件:
instance.bind("click", function (conn, originalEvent) {
// if (confirm("Delete connection from " + conn.sourceId + " to " + conn.targetId + "?"))
// instance.detach(conn);
conn.toggleType("basic");
});
//从源对象新拖动一条线的事件
instance.bind("connectionDrag", function (connection) {
console.log("connection " + connection.id + " is being dragged. suspendedElement is ", connection.suspendedElement, " of type ", connection.suspendedElementType);
});
//拖动停止(不管是否拖动到目标对象或中途停止拖动)
instance.bind("connectionDragStop", function (connection) {
console.log("connection " + connection.id + " was dragged");
});
//将一条已存在的线条拖动走时的事件
instance.bind("connectionMoved", function (params) {
console.log("connection " + params.connection.id + " was moved");
});
3、DEMOS → TOOLKIT → FLOWCHART BUILDER路径的试例有关于线条删除的代码:
"dblclick": function (params) {
jsPlumbToolkit.Dialogs.show({
id: "dlgConfirm",
data: {
msg: "Delete Edge"
},
onOK: function () {
toolkit.removeEdge(params.edge);//最新版是使用此方法删除线条
}
});
}
}
4、装载的包含对象位置信息的json
{
"id": "start",
"type": "start",
"text": "Start",
"left": 50,
"top": 50,
"w": 100,
"h": 70
},
{
"id": "question1",
"type": "question",
"text": "Do Something?",
"left": 290,
"top": 79,
"w": 150,
"h": 150
},
5、源对象连接目标对象后的json
{
"id": 1,
"source": "start",
"target": "question1"
},
{
"id": 2,
"source": "question1",
"target": "decide",
"data":{ "label": "yes", "type":"connection" }
},
以上完整的json信息在:
https://jsplumbtoolkit.com/demos/toolkit/flowchart-builder/data/flowchart-1.json
6、一条线上两个箭头:
overlays: [
[ "Arrow", { location: 1, width: 10, length: 10 }],
[ "Arrow", { location: 0.3, width: 10, length: 10 }]
]
7、渲染层处理鼠标双击节点对象:
var renderer = window.renderer = toolkit.render({
container: canvasElement,
view: {
nodes: {
"start": {
template: "tmplStart"
},
"selectable": {
events: {
tap: function (params) {
toolkit.toggleSelection(params.node);//这里
}
}
},
8、app.js完整源码:
(function () {
jsPlumbToolkit.ready(function () {
// ------------------------ toolkit setup ------------------------------------
// This function is what the toolkit will use to get an ID from a node.
var idFunction = function (n) {
return n.id;
};
// This function is what the toolkit will use to get the associated type from a node.
var typeFunction = function (n) {
return n.type;
};
// get the various dom elements
var mainElement = document.querySelector("#jtk-demo-flowchart"),
canvasElement = mainElement.querySelector(".jtk-demo-canvas"),
miniviewElement = mainElement.querySelector(".miniview"),
nodePalette = mainElement.querySelector(".node-palette"),
controls = mainElement.querySelector(".controls");
// Declare an instance of the Toolkit, and supply the functions we will use to get ids and types from nodes.
var toolkit = jsPlumbToolkit.newInstance({
idFunction: idFunction,
typeFunction: typeFunction,
nodeFactory: function (type, data, callback) {
jsPlumbToolkit.Dialogs.show({
id: "dlgText",
title: "Enter " + type + " name:",
onOK: function (d) {
data.text = d.text;
// if the user entered a name...
if (data.text) {
// and it was at least 2 chars
if (data.text.length >= 2) {
// set an id and continue.
data.id = jsPlumbToolkitUtil.uuid();
callback(data);
}
else
// else advise the user.
alert(type + " names must be at least 2 characters!");
}
// else...do not proceed.
}
});
},
beforeStartConnect:function(node, edgeType) {
// limit edges from start node to 1. if any other type of node, return
return (node.data.type === "start" && node.getEdges().length > 0) ? false : { label:"..." };
}
});
// ------------------------ / toolkit setup ------------------------------------
// ------------------------- dialogs -------------------------------------
jsPlumbToolkit.Dialogs.initialize({
selector: ".dlg"
});
// ------------------------- / dialogs ----------------------------------
// ------------------------ rendering ------------------------------------
var _editLabel = function(edge, deleteOnCancel) {
jsPlumbToolkit.Dialogs.show({
id: "dlgText",
data: {
text: edge.data.label || ""
},
onOK: function (data) {
toolkit.updateEdge(edge, { label:data.text || "" });
},
onCancel:function() {
if (deleteOnCancel) {
toolkit.removeEdge(edge);
}
}
});
};
// Instruct the toolkit to render to the 'canvas' element. We pass in a view of nodes, edges and ports, which
// together define the look and feel and behaviour of this renderer. Note that we can have 0 - N renderers
// assigned to one instance of the Toolkit..
var renderer = window.renderer = toolkit.render({
container: canvasElement,
view: {
nodes: {
"start": {
template: "tmplStart"
},
"selectable": {
events: {
tap: function (params) {
toolkit.toggleSelection(params.node);
}
}
},
"question": {
parent: "selectable",
template: "tmplQuestion"
},
"action": {
parent: "selectable",
template: "tmplAction"
},
"output":{
parent:"selectable",
template:"tmplOutput"
}
},
// There are two edge types defined - 'yes' and 'no', sharing a common
// parent.
edges: {
"default": {
anchor:"AutoDefault",
endpoint:"Blank",
connector: ["Flowchart", { cornerRadius: 5 } ],
paintStyle: { strokeWidth: 2, stroke: "#f76258", outlineWidth: 3, outlineStroke: "transparent" }, // paint style for this edge type.
hoverPaintStyle: { strokeWidth: 2, stroke: "rgb(67,67,67)" }, // hover paint style for this edge type.
events: {
"dblclick": function (params) {
jsPlumbToolkit.Dialogs.show({
id: "dlgConfirm",
data: {
msg: "Delete Edge"
},
onOK: function () {
toolkit.removeEdge(params.edge);
}
});
}
},
overlays: [
[ "Arrow", { location: 1, width: 10, length: 10 }],
[ "Arrow", { location: 0.3, width: 10, length: 10 }]
]
},
"connection":{
parent:"default",
overlays:[
[
"Label", {
label: "${label}",
events:{
click:function(params) {
_editLabel(params.edge);
}
}
}
]
]
}
},
ports: {
"start": {
edgeType: "default"
},
"source": {
maxConnections: -1,
edgeType: "connection"
},
"target": {
maxConnections: -1,
isTarget: true,
dropOptions: {
hoverClass: "connection-drop"
}
}
}
},
// Layout the nodes using an absolute layout
layout: {
type: "Absolute"
},
events: {
canvasClick: function (e) {
toolkit.clearSelection();
},
edgeAdded:function(params) {
if (params.addedByMouse) {
_editLabel(params.edge, true);
}
},
nodeDropped:function(info) {
console.log("node ", info.source.id, "dropped on ", info.target.id);
}
},
miniview: {
container: miniviewElement
},
lassoInvert:true,
elementsDroppable:true,
consumeRightClick: false,
dragOptions: {
filter: ".jtk-draw-handle, .node-action, .node-action i",
magnetize:true
},
dropManager:true
});
var datasetView = new jsPlumbSyntaxHighlighter(toolkit, ".jtk-demo-dataset");
// Load the data.
toolkit.load({
url: "data/flowchart-1.json"
});
// listener for mode change on renderer.
renderer.bind("modeChanged", function (mode) {
jsPlumb.removeClass(controls.querySelectorAll("[mode]"), "selected-mode");
jsPlumb.addClass(controls.querySelectorAll("[mode='" + mode + "']"), "selected-mode");
});
// pan mode/select mode
jsPlumb.on(controls, "tap", "[mode]", function () {
renderer.setMode(this.getAttribute("mode"));
});
// on home button click, zoom content to fit.
jsPlumb.on(controls, "tap", "[reset]", function () {
toolkit.clearSelection();
renderer.zoomToFit();
});
// configure Drawing tools.
new jsPlumbToolkit.DrawingTools({
renderer: renderer
});
jsPlumb.on(canvasElement, "tap", ".node-delete", function () {
var info = renderer.getObjectInfo(this);
jsPlumbToolkit.Dialogs.show({
id: "dlgConfirm",
data: {
msg: "Delete '" + info.obj.data.text + "'"
},
onOK: function () {
toolkit.removeNode(info.obj);
}
});
});
// change a question or action's label
jsPlumb.on(canvasElement, "tap", ".node-edit", function () {
// getObjectInfo is a method that takes some DOM element (this function's `this` is
// set to the element that fired the event) and returns the toolkit data object that
// relates to the element. it ascends through parent nodes until it finds a node that is
// registered with the toolkit.
var info = renderer.getObjectInfo(this);
jsPlumbToolkit.Dialogs.show({
id: "dlgText",
data: info.obj.data,
title: "Edit " + info.obj.data.type + " name",
onOK: function (data) {
if (data.text && data.text.length > 2) {
// if name is at least 2 chars long, update the underlying data and
// update the UI.
toolkit.updateNode(info.obj, data);
}
}
});
});
// ------------------------ / rendering ------------------------------------
// ------------------------ drag and drop new tables/views -----------------
//
// Here, we are registering elements that we will want to drop onto the workspace and have
// the toolkit recognise them as new nodes.
//
// typeExtractor: this function takes an element and returns to jsPlumb the type of node represented by
// that element. In this application, that information is stored in the 'jtk-node-type' attribute.
//
// dataGenerator: this function takes a node type and returns some default data for that node type.
//
renderer.registerDroppableNodes({
droppables: nodePalette.querySelectorAll("li"),
dragOptions: {
zIndex: 50000,
cursor: "move",
clone: true
},
typeExtractor: function (el) {
return el.getAttribute("jtk-node-type");
},
dataGenerator: function (type) {
return {
w:120,
h:80
};
}
});
// ------------------------ / drag and drop new tables/views -----------------
});
})();