這周去看了兩天的羽毛球亞錦賽,工作有提前晚上加班做一些,但是技術文章卻拉下了。
這段時間一直在尋找可以實現前端元素動態連線的功能,找了好幾個庫,考慮過用d3或者原生svg和canvas來實現,最后和同項目的同事商量后決定使用jsPlumb插件庫來做。
jsPlumb是一個強大的JavaScript連線庫,它可以將html中的元素用箭頭、曲線、直線等連接起來,適用於開發Web上的圖表、建模工具等,其實jsPlumb可能主要是用來做流程圖的,它在實現這方面的功能上非常強大,我在項目中只使用了它少部分功能,來實現項目中連線的效果。

連線效果
initJSPlumb = () => { this.jsp = jsPlumb.getInstance({ //錨點位置;對任何沒有聲明描點的Endpoint設置錨點,用於source及target節點 Anchor: ["Right", "Left"], Anchors: ["Right", "Left"], //連線的source和target Anchor ConnectionsDetachable: false, //連線是否可用鼠標分離 ConnectionOverlays: [ //連線的疊加組件,如箭頭、標簽 ["Arrow", { //箭頭參數設置 location: 1, visible: true, width: 11, length: 11, id: "ARROW", events: { click: function () { } } }], ["Label", { //標簽參數設置 location: 0.1, id: "label", cssClass: "aLabel", //hover時label的樣式名 events: { tap: function () { } }, visible: true }] ], Connector: "Bezier", //連線的類型,流程圖(Flowchart)、貝塞爾曲線等 //父級元素id;假如頁面元素所在上層不同,最外層父級一定要設置 Container: "module", //如果請求不存在的Anchor、Endpoint或Connector,是否拋異常 DoNotThrowErrors: false, //通過jsPlumb.draggable拖拽元素時的默認參數設置 DragOptions: {cursor: 'pointer', zIndex: 2000}, DropOptions: {}, //target Endpoint放置時的默認參數設置 Endpoint: "Dot", //端點(錨點)的樣式聲明 //用jsPlumb.connect創建連接時,source端點和target端點的樣式設置 Endpoints: [null, null], EndpointOverlays: [], //端點的疊加物 //端點的默認樣式 EndpointStyle: {fill: 'transparent', stroke: '#1565C0', radius: 4, strokeWidth: 1}, EndpointStyles: [null, null], //連線的source和target端點的樣式 //端點hover時的樣式 EndpointHoverStyle: {fill: '#1565C0', stroke: '#1565C0', radius: 4, strokeWidth: 1}, //連線的source和target端點hover時的樣式 EndpointHoverStyles: [null, null], //連線hover時的樣式 HoverPaintStyle: {stroke: '#1565C0', strokeWidth: 3}, LabelStyle: {color: "black"}, //標簽的默認樣式,用css寫法。 LogEnabled: false, //是否開啟jsPlumb內部日志 Overlays: [], //連線和端點的疊加物 MaxConnections: 10, //端點支持的最大連接數 //連線樣式 PaintStyle: {stroke: '#1565C0', strokeWidth: 1, joinstyle: 'round'}, ReattachConnections: true, //是否重新連接使用鼠標分離的線? RenderMode: "svg", //默認渲染模式 Scope: "jsPlumb_DefaultScope", //范圍,具有相同scope的點才可連接 reattach: true, }) this.jsp.bind('beforeDrop', this.jspBeforeDrop) }
以上是初始化jsPlumb對象的函數。
接下來獲取數據,加載頁面的系統和編制模塊。
fetchDataForLeft(data) {
var jsonString_left = '[{"module_name":"crm系統","module_id":"A","nodes":[{"id":"A-1","text":"開始","position":"left"},{"id":"A-2","text":"過程","position":"left"},{"id":"A-3","text":"過程ing","position":"left"}]},{"module_name":"財務系統","module_id":"B","nodes":[{"id":"B-1","text":"開始","position":"left"}]}]'; var jsonString_right = '[{"module_name":"年度預算編制","module_id":"C","nodes":[{"id":"C-1","text":"結束","position":"right"}]},{"module_name":"營收編制","module_id":"D","nodes":[{"id":"D-1","text":"結束","position":"right"}]}]'; // var nodeData_left = JSON.parse(jsonString_left); //[{"entity":null,"id":"934D62BD0F8249B09F29EC9FA051E390","code":"1","name":"CRM????","systemType":null,"databaseType":null,"hostName":null,"databaseaName":null,"userName":null,"password":null,"created":null,"modified":null},{"entity":null,"id":"81ABCD2890E2460B90A9B8A0ACE3FABD","code":null,"name":"CRM系統","systemType":null,"databaseType":null,"hostName":null,"databaseaName":null,"userName":null,"password":null,"created":null,"modified":null},{"entity":null,"id":"9AE1B07886E54F3DB0082D46392DE774","code":null,"name":"CRM系統","systemType":null,"databaseType":null,"hostName":null,"databaseaName":null,"userName":null,"password":null,"created":null,"modified":null},{"entity":null,"id":"E6D29376AC86455DBC3518D12F28C5B0","code":null,"name":"CRM系統","systemType":null,"databaseType":null,"hostName":null,"databaseaName":null,"userName":null,"password":null,"created":null,"modified":null}] var nodeData_left = data; this.setState({ nodes_left: nodeData_left, }) //繪制左邊點 nodeData_left.map((node, index) => { this.setState({datas: node, nodes: node.fromModelList}, () => { this.initNodes(this.refs.nodes_left[index], 'left'); // this.initEdges(nodeData.edges); }); }) //繪制右邊點 // nodeData_right.map((node, index) => { // this.setState({data_right: node, node_right: node.toModelList}, () => { // this.initNodes(this.refs.nodes_right[index], 'right'); // // this.initEdges(nodeData.edges); // }); // }) } fetchDataForRight(data){ var jsonString_left = '[{"module_name":"crm系統","module_id":"A","nodes":[{"id":"A-1","text":"開始","position":"left"},{"id":"A-2","text":"過程","position":"left"},{"id":"A-3","text":"過程ing","position":"left"}]},{"module_name":"財務系統","module_id":"B","nodes":[{"id":"B-1","text":"開始","position":"left"}]}]'; var jsonString_right = '[{"module_name":"年度預算編制","module_id":"C","nodes":[{"id":"C-1","text":"結束","position":"right"}]},{"module_name":"營收編制","module_id":"D","nodes":[{"id":"D-1","text":"結束","position":"right"}]}]'; // var nodeData_left = JSON.parse(jsonString_left); //[{"entity":null,"id":"934D62BD0F8249B09F29EC9FA051E390","code":"1","name":"CRM????","systemType":null,"databaseType":null,"hostName":null,"databaseaName":null,"userName":null,"password":null,"created":null,"modified":null},{"entity":null,"id":"81ABCD2890E2460B90A9B8A0ACE3FABD","code":null,"name":"CRM系統","systemType":null,"databaseType":null,"hostName":null,"databaseaName":null,"userName":null,"password":null,"created":null,"modified":null},{"entity":null,"id":"9AE1B07886E54F3DB0082D46392DE774","code":null,"name":"CRM系統","systemType":null,"databaseType":null,"hostName":null,"databaseaName":null,"userName":null,"password":null,"created":null,"modified":null},{"entity":null,"id":"E6D29376AC86455DBC3518D12F28C5B0","code":null,"name":"CRM系統","systemType":null,"databaseType":null,"hostName":null,"databaseaName":null,"userName":null,"password":null,"created":null,"modified":null}] var nodeData_right = data; this.setState({ nodes_right: nodeData_right }) //繪制右邊點 nodeData_right.map((node, index) => { this.setState({data_right: node, node_right: node.toModelList}, () => { this.initNodes(this.refs.nodes_right[index], 'right'); // this.initEdges(nodeData.edges); }); }) }
初始化連接錨點
initNodes = (node, position) => { this.jsp.setSuspendDrawing(true); if (position === "left") { DynamicAnchors.map(anchor => this.rjsp.addEndpoint(node, anEndpoint, {anchor: "Right"})); } else { DynamicAnchors_Right.map(anchor => this.rjsp.addEndpoint(node, anEndpoint, {anchor: "Left"})); } this.rjsp.setSuspendDrawing(false, true); }
//自動連線
initEdges = (edges) => { this.rjsp.setSuspendDrawing(true); edges.map(edge => { this.jsp.connect(edge, Common); }) this.jsp.setSuspendDrawing(false, true); }