搜尋
首頁web前端js教程jsPlumb之流程圖經驗總結

jsPlumb之流程圖經驗總結

May 17, 2018 pm 02:03 PM
javascript流程圖

在使用jsPlumb过程中,所遇到的问题,以及解决方案,文中引用了《数据结构与算法JavaScript描述》的相关图片和一部分代
码.截图是有点多,有时比较懒,没有太多的时间去详细的编辑.

前言

首先是UML類別圖
jsPlumb之流程圖經驗總結

#然後是流程圖
jsPlumb之流程圖經驗總結

使用了jsPlumb的相關功能,初版是可以看到雛形了,差不多用了兩個月的時間,中間斷斷續續的又有其它工作穿插,但還是把基本功能做出來了.

其實做完了之後,才發現jsPlumb的功能,只用到了很少的一部分,更多的是對於內部數據結構的理解和實現,只能說做到了數據同步更新,距離數據驅動仍然有一定的距離.

這裡會總結和記錄一下專案中遇到的問題,和解決的方法,如果有更好的方法,歡迎指出.

對於連線上的多個標籤的處理

如上圖所示,一開始是認為是否是要在連線時,配置兩個overlays,

    var j = jsPlumb.getInstance();

    j.connect({
        source:source,
        target:target,
        overlays:[
            "Arrow",
            ["label",{label:"foo1",location:0.2jsPlumb之流程圖經驗總結,id:"m1"}],
            ["label",{label:"foo2",location:0.jsPlumb之流程圖經驗總結jsPlumb之流程圖經驗總結,id:"m2"}]
        ]
    })

當然,這裡也有坑,如果id重複,那麼會使用最後一個,而不會重疊,包含jsPlumb內部快取的資料都只會剩下最後的那個.

後面發現,其實也可以透過importDefaults函數來動態修改設定項.

    j.importDefaults({
        ConnectionOverlays: [
            ["Arrow", { location: 1, id: "arrow", length: jsPlumb之流程圖經驗總結, foldback: 0, width: jsPlumb之流程圖經驗總結 }],
            ["Label", { label: "n", id: "label-n", location: 0.2jsPlumb之流程圖經驗總結, cssClass: "jspl-label" }],
            ["Label", { label: "1", id: "label-1", location: 0.jsPlumb之流程圖經驗總結jsPlumb之流程圖經驗總結, cssClass: "jspl-label" }]
        ]
    })

只不過這樣,只會在運行了函數之後的連線裡,才能有兩個標籤顯示,而之前的則無法一起變化.
所以為了方便,直接在初始化裡將其給修改了.

Groups的使用

在做流程圖時,Group確實是個問題,如上圖的無限嵌套層級中,就無法使用jsPlumb提供的Groups功能.
按照文檔中來說,如果標識一個元素為組,則該組中的元素則會跟隨組的移動而移動,連線也是,但問題就是一旦一個元素成為組了,那就不能接受其它組元素了,換句話說,它所提供的的Groups方法只有一層,自然無法滿足要求.
先把總結的組的用法貼出來:

    j.addGroup({
        el:el,
        id:"one"
        constrain:true, // 子元素仅限在元素内拖动
        droppable:true, // 子元素是否可以放置其他元素
        draggable:true, // 默认为true,组是否可以拖动
        dropOverride:true ,// 组中的元素是否可以拓展到其他组,为true时表示否,这里的拓展会对dom结构进行修改,而非单纯的位置移动
        ghost:true, // 是否创建一个子元素的副本元素
        revert:true, // 元素是否可以拖到只有边框可以重合
    })

後面採用了新的方式,在節點移動時,動態刷新連線

    j.repaintEverything();

而為了不阻塞頁面,需要用到函數節流throttle()

    function throttle(fn,interval){
        var canRun = true;

        return function(){
            if(!canRun) return;
            canRun = false;
            setTimeout(function(){
                fn.apply(this,arguments);
                canRun = true;
            },interval ? interval : jsPlumb之流程圖經驗總結00);
        };
    };

這是一個簡單的實現方式,主要就是為了減少dom中事件移動時重複調用的事件,同時達到執行事件的目的(只允許一個函數在x毫秒內執行一次);
當然,也可以使用underscore.js中自帶的_.throttle()函數,同樣可以達到目的.

這裡的html結構就使用了嵌套的層級,將父級和子級使用這種層級保存到內部的資料來源裡

多層or一層資料結構解析

jsPlumb之流程圖經驗總結

#類似這種實際存在嵌套關係的資料體,有兩種方式可以進行管理,

  • 多層級嵌套:類似

        [
            {
                id:"1",
                child:{
                    id:"2",
                    child:{
                        id:"jsPlumb之流程圖經驗總結",
                        child:{}
                    }
                }
            }
        ]

    用來進行管理的話,優點是直覺,能根據層級就知道整體結構大概是多少,轉換成xml或html也很方便.
    但缺點就是進行查找和修改,並不是那麼方便.

  • #一層展示所有節點:類似

        [
            {
                id:"1",
                child:[{
                    id:"2"
                }]
            },
            {
                id:"2",
                parentId:"1",
                child:[{
                    id:"jsPlumb之流程圖經驗總結"
                }]
            },
            {
                id:"jsPlumb之流程圖經驗總結",
                parentId:"2",
                child:[]
            }
        ]

    這種結構好處就是全部在一個層級中,查找起來和修改資料非常方便,而如果想要解析成多層級的結構,只需要運用遞歸,來產生新結構:

    function mt(){
        var OBJ;
        this.root = null;
        this.Node = function(e) {
            this.id = e.id;
            this.name = e.name;
            this.parentId = e.parentId;
            this.children = [];
        };
    
        this.insert=function(e,key){
            function add(obj,e){
                if(obj.id == e.parentId){
                    obj.children.push(e);
                } else {
                    for (var i = 0; i <p>將一層的陣列透過初始化函數<code>init</code>,就可以轉為多層級<br><img src="/static/imghwm/default1.png" data-src="https://img.php.cn/upload/article/000/000/001/jsPlumb之流程圖經驗總結cdjsPlumb之流程圖經驗總結jsPlumb之流程圖經驗總結a2jsPlumb之流程圖經驗總結jsPlumb之流程圖經驗總結jsPlumb之流程圖經驗總結0c1jsPlumb之流程圖經驗總結fjsPlumb之流程圖經驗總結bjsPlumb之流程圖經驗總結djsPlumb之流程圖經驗總結e1jsPlumb之流程圖經驗總結cjsPlumb之流程圖經驗總結bjsPlumb之流程圖經驗總結jsPlumb之流程圖經驗總結fbjsPlumb之流程圖經驗總結a-jsPlumb之流程圖經驗總結.png?x-oss-process=image/resize,p_40" class="lazy" alt="jsPlumb之流程圖經驗總結"></p>

#如果想轉成html結構,只需要稍微改下函數,就可以實現了.

校驗流程是否存在死路(是否存在不能到達圖的終點的路徑的點)

這個就完全得靠演算法來實現了.首先,對於圖的理解是重點
jsPlumb之流程圖經驗總結

我也懶得打字了,直接用圖表示一下,基本的圖大致是這樣,而具體的表現形式則是
jsPlumb之流程圖經驗總結

可以看到,基礎的圖的表現形式,可以用一個鄰接表來表示;

而實現,則可以看到下列的代碼:

function Graph1(v) {
  this.vertices = v; // 总顶点
  this.edges = 0; // 图的边数
  this.adj = [];

  // 通过 for 循环为数组中的每个元素添加一个子数组来存储所有的相邻顶点,[并将所有元素初始化为空字符串。]?
  for (var i = 0; i ";
      for (var j = 0; j <p>而光建構是不夠的,所以來看下基礎的搜尋方法:<br>深度優先搜尋和廣度優先搜尋;</p><hjsplumb>#深度優先搜尋</hjsplumb>之流程圖經驗總結><p>先從初始節點開始訪問,並標記為已訪問過的狀態,再遞歸的去訪問在初始節點的鄰接表中其他沒有訪問過的節點,依次之後,就能訪問過所有的節點了<br><img src="/static/imghwm/default1.png" data-src="https://img.php.cn/upload/article/000/000/001/cjsPlumb之流程圖經驗總結cbcjsPlumb之流程圖經驗總結a2ajsPlumb之流程圖經驗總結bjsPlumb之流程圖經驗總結jsPlumb之流程圖經驗總結jsPlumb之流程圖經驗總結0fjsPlumb之流程圖經驗總結2ejsPlumb之流程圖經驗總結0jsPlumb之流程圖經驗總結abbjsPlumb之流程圖經驗總結jsPlumb之流程圖經驗總結jsPlumb之流程圖經驗總結bbjsPlumb之流程圖經驗總結e-jsPlumb之流程圖經驗總結.png?x-oss-process=image/resize,p_40" class="lazy" alt="jsPlumb之流程圖經驗總結"></p><pre class="brush:php;toolbar:false">  /**
   * 深度优先搜索算法
   * 这里不需要顶点,也就是邻接表的初始点
   */
    this.dfs = (v) {
        this.marked[v] = true;
        for (var w of this.adj[v]) {
            if (!this.marked[w]) {
                this.dfs(w);
            }
        }
    }

根據圖片和上述的程式碼,可以看出深度搜尋其實可以做很多其他的擴充

#廣度優先搜尋之流程圖經驗總結>

jsPlumb之流程圖經驗總結

#
  /**
   * 广度优先搜索算法
   * @param  {[type]} s [description]
   */
  this.bfs = function(s) {
    var queue = [];
    this.marked[s] = true;
    queue.push(s); // 添加到队尾
    while (queue.length > 0) {
      var v = queue.shift(); // 从队首移除
      console.log("Visisted vertex: " + v);
      for (var w of this.adj[v]) {
        if (!this.marked[w]) {
          this.edgeTo[w] = v;
          this.marked[w] = true;
          queue.push(w);
        }
      }
    }
  }

而如果看了《数据结构与算法JavaScript描述》这本书,有兴趣的可以去实现下查找最短路径拓扑排序;

两点之间所有路径之流程圖經驗總結>

这算是找到的比较能理解的方式来计算
jsPlumb之流程圖經驗總結

以上图为例,这是一个简单的流程图,可以很简单的看出,右边的流程实际上是未完成的,因为无法到达终点,所以是一个非法点,而通过上面的深度搜索,可以看出,只要对深度优先搜索算法进行一定的修改,那么就可以找到从开始到结束的所有的路径,再通过对比,就可以知道哪些点无法到达终点,从而确定非法点.
上代码:

    /**
     * 深度搜索,dfs,解两点之间所有路径
     * @param  {[type]} v [description]
     * @return {[type]}   [description]
     */
    function Graph2(v) {
        var _this = this;

        this.vertices = v; // 总顶点
        this.edges = 0; //图的起始边数
        this.adj = []; //内部邻接表表现形式
        this.marked = []; // 内部顶点访问状态,与邻接表对应
        this.path = []; // 路径表示
        this.lines = []; // 所有路径汇总

        for (var i = 0; i <p>可以看出修改了<code>addEdge()</code>函数,将邻接表中的双向记录改为单向记录,可以有效避免下图的错误计算:<br><img src="/static/imghwm/default1.png" data-src="https://img.php.cn/upload/article/000/000/001/cabe00ejsPlumb之流程圖經驗總結21jsPlumb之流程圖經驗總結jsPlumb之流程圖經驗總結0d0jsPlumb之流程圖經驗總結1jsPlumb之流程圖經驗總結1jsPlumb之流程圖經驗總結1jsPlumb之流程圖經驗總結1cjsPlumb之流程圖經驗總結c1jsPlumb之流程圖經驗總結jsPlumb之流程圖經驗總結jsPlumb之流程圖經驗總結fjsPlumb之流程圖經驗總結-jsPlumb之流程圖經驗總結.png?x-oss-process=image/resize,p_40" class="lazy" alt="jsPlumb之流程圖經驗總結"></p><p>只计算起点到终点的所有连线有时并不客观,如果出现<br><img src="/static/imghwm/default1.png" data-src="https://img.php.cn/upload/article/000/000/001/cabe00ejsPlumb之流程圖經驗總結21jsPlumb之流程圖經驗總結jsPlumb之流程圖經驗總結0d0jsPlumb之流程圖經驗總結1jsPlumb之流程圖經驗總結1jsPlumb之流程圖經驗總結1jsPlumb之流程圖經驗總結1cjsPlumb之流程圖經驗總結c1jsPlumb之流程圖經驗總結jsPlumb之流程圖經驗總結jsPlumb之流程圖經驗總結fjsPlumb之流程圖經驗總結-jsPlumb之流程圖經驗總結.png?x-oss-process=image/resize,p_40" class="lazy" alt="1jsPlumb之流程圖經驗總結"></p><p>这种情况的话,实际上深度遍历并不能计算出最右边的节点是合法的,那么就需要重新修改起点和终点,来推导是否能够到达终点.从而判定该点是否合法.至于其他的,只是多了个返回值,存储了一下计算出来的所有路径.<br>而在dfs函数中,当满足能够从起点走到终点的,则记录下当前的path中的值,保存到lines中去,而每一次对于path的推入或者推出,保证了只有满足条件的点,才能被返回;<br>而<code>this.marked[v] = false</code>,则确保了,在每一次重新计算路径时,都会验证每个点是否存在不同的相对于终点能够到达的路径是否存在.<br>当然,一定会有更加简单的方法,我这里只是稍微修改了下基础的代码!</p><h2 id="redo和undo">redo和undo</h2><p>这是我觉得最简单却耗时最久的功能,思路都知道:创建一个队列,记录每一次创建一个流程节点,删除一个流程节点,建立一个新的关联关系,删除一个新的关联关系等,都需要记录下来,再通过统一的接口来访问队列,执行操作.<br>但在具体实现上,jsPlumb的remove确实需要注意一下:<br>首先,如果需要删除连线,那么使用jsPlumb提供的<code>detach()</code>方法,就可以删除连线,注意,传入的数据应该是<code>connection</code>对象.<br>当然,也可以使用<code>remove()</code>方法,参数为选择器或者element对象都可以,这个方法删除的是一个节点,包括节点上所有的线.<br>而jsPlumb中会内部缓存所有的数据,用于刷新,和重连.<br>那么当我移除一个多层级且内部有连线的情况时,如果只删除最外层的元素,那么内部的连线实际上并没有清除,所以当redo或者移动时,会出现连线的端点有一端会跑到坐标原点,也就是p上(0,0)的地方去.所以清除时,需要注意,要把内部的所有节点依次清除,才不会发生一些莫名其妙的bug.</p><p>而在删除和连接连线上,我使用了jsPlumb提供的事件<code>bind(jsPlumb之流程圖經驗總結jsPlumb之流程圖經驗總結;connectionjsPlumb之流程圖經驗總結jsPlumb之流程圖經驗總結;)</code>和<code>bind("connectionDetached")</code>,用于判断一条连线被连接或者删除.而在记录这里的redo和undo事件时,尤其要注意,需要首先确定删除和连接时的连线的类型,否则会产生额外的队列事件.<br>因此,在使用连接事件时,就可以使用</p><pre class="brush:php;toolbar:false">jsPlumb.connect({
    source:"foo",
    target:"bar",
    parameters:{
        "p1":jsPlumb之流程圖經驗總結jsPlumb之流程圖經驗總結,
        "p2":new Date(),
        "pjsPlumb之流程圖經驗總結":function() { console.log("i am pjsPlumb之流程圖經驗總結"); }
    }
});

来进行类型的传参,这样事件触发时就可以分类处理.
也可以使用connection.setData()事件,参数可以指定任意的值,通过connection.getData()方法,就可以拿到相应的数据了.
而redo和undo本身确实没有什么东西

    var defaults = {
        jsPlumb之流程圖經驗總結jsPlumb之流程圖經驗總結;namejsPlumb之流程圖經驗總結jsPlumb之流程圖經驗總結;: "mutation",
        jsPlumb之流程圖經驗總結jsPlumb之流程圖經驗總結;afterAddServejsPlumb之流程圖經驗總結jsPlumb之流程圖經驗總結;:$.noop,
        jsPlumb之流程圖經驗總結jsPlumb之流程圖經驗總結;afterUndojsPlumb之流程圖經驗總結jsPlumb之流程圖經驗總結;:$.noop,
        jsPlumb之流程圖經驗總結jsPlumb之流程圖經驗總結;afterRedojsPlumb之流程圖經驗總結jsPlumb之流程圖經驗總結;:$.noop
    }

    var mutation = function(options){
        this.options = $.extend(true,{},defaults,options);

        this.list = [];
        this.index = 0;
    };

    mutation.prototype = {
        addServe:function(undo,redo){
            if(!_.isFunction(undo) || !_.isFunction(redo)) return false;

            // 说明是在有后续操作时,更新了队列
            if(this.canRedo){
                this.splice(this.index+1);
            };
            this.list.push({
                undo:undo,
                redo:redo
            });

            console.log(this.list);

            this.index = this.list.length - 1;

            _.isFunction(this.options.afterAddServe) && this.options.afterAddServe(this.canUndo(),this.canRedo());
        },
        /**
         * 相当于保存之后清空之前的所有保存的操作
         * @return {[type]} [description]
         */
        reset:function(){
            this.list = [];
            this.index = 0;
        },
        /**
         * 当破坏原来队列时,需要对队列进行修改,
         * index开始的所有存储值都没有用了
         * @param  {[type]} index [description]
         * @return {[type]}       [description]
         */
        splice:function(index){
            this.list.splice(index);
        },
        /**
         * 撤销操作
         * @return {[type]} [description]
         */
        undo:function(){
            if(this.canUndo()){
                this.list[this.index].undo();
                this.index--;

                _.isFunction(this.options.afterUndo) && this.options.afterUndo(this.canUndo(),this.canRedo());
            }
        },
        /**
         * 重做操作
         * @return {[type]} [description]
         */
        redo:function(){
            if(this.canRedo()){
                this.index++;
                this.list[this.index].redo();

                _.isFunction(this.options.afterRedo) && this.options.afterRedo(this.canUndo(),this.canRedo());
            }
        },
        canUndo:function(){
            return this.index !== -1;
        },
        canRedo:function(){
            return this.list.length - 1 !== this.index;
        }
    }

    return mutation;

每次在使用redo或者undo时,只需要判断当前是否是队列的尾端或者起始端,再确定是否redo或者undo就可以了.
调用时的undo()redo()通过传参,将不同的函数封装进队列里,就可以减少耦合度.

放大缩小

这里想了想还是记录一下,方法采用了最简单的mousedownmousemove,让元素在节流中动态的变化大小,就可以了,
1jsPlumb之流程圖經驗總結

只需要用一个节点,在点击元素时,根据元素的大小来确定该辅助节点四个点的位置,就可以了,只要监听了这四个点的位置,再同步给该定位元素,就能实现这一效果,方法就不贴了,没有太多东西

小結

這次的專案我個人還是覺得蠻有意思的,可以學習新的演算法,了解新的資料結構,包括設計模式,也代入了其中,進行程式碼的整合,所用到的中間件模式和發布訂閱者模式都讓我對於js有了一個新的理解.雖然已經用require來管理模組,但結構仍然存在高度耦合的情況,應該還是被限制住了.
作為離職前的最後一次的專案來說,其實我感覺我的程式碼能力仍然與年初沒有什麼太大的改變,也許是時候脫離安逸的環境,重新開始了.

以上是jsPlumb之流程圖經驗總結的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
JavaScript是用C編寫的嗎?檢查證據JavaScript是用C編寫的嗎?檢查證據Apr 25, 2025 am 12:15 AM

是的,JavaScript的引擎核心是用C語言編寫的。 1)C語言提供了高效性能和底層控制,適合JavaScript引擎的開發。 2)以V8引擎為例,其核心用C 編寫,結合了C的效率和麵向對象特性。 3)JavaScript引擎的工作原理包括解析、編譯和執行,C語言在這些過程中發揮關鍵作用。

JavaScript的角色:使網絡交互和動態JavaScript的角色:使網絡交互和動態Apr 24, 2025 am 12:12 AM

JavaScript是現代網站的核心,因為它增強了網頁的交互性和動態性。 1)它允許在不刷新頁面的情況下改變內容,2)通過DOMAPI操作網頁,3)支持複雜的交互效果如動畫和拖放,4)優化性能和最佳實踐提高用戶體驗。

C和JavaScript:連接解釋C和JavaScript:連接解釋Apr 23, 2025 am 12:07 AM

C 和JavaScript通過WebAssembly實現互操作性。 1)C 代碼編譯成WebAssembly模塊,引入到JavaScript環境中,增強計算能力。 2)在遊戲開發中,C 處理物理引擎和圖形渲染,JavaScript負責遊戲邏輯和用戶界面。

從網站到應用程序:JavaScript的不同應用從網站到應用程序:JavaScript的不同應用Apr 22, 2025 am 12:02 AM

JavaScript在網站、移動應用、桌面應用和服務器端編程中均有廣泛應用。 1)在網站開發中,JavaScript與HTML、CSS一起操作DOM,實現動態效果,並支持如jQuery、React等框架。 2)通過ReactNative和Ionic,JavaScript用於開發跨平台移動應用。 3)Electron框架使JavaScript能構建桌面應用。 4)Node.js讓JavaScript在服務器端運行,支持高並發請求。

Python vs. JavaScript:比較用例和應用程序Python vs. JavaScript:比較用例和應用程序Apr 21, 2025 am 12:01 AM

Python更適合數據科學和自動化,JavaScript更適合前端和全棧開發。 1.Python在數據科學和機器學習中表現出色,使用NumPy、Pandas等庫進行數據處理和建模。 2.Python在自動化和腳本編寫方面簡潔高效。 3.JavaScript在前端開發中不可或缺,用於構建動態網頁和單頁面應用。 4.JavaScript通過Node.js在後端開發中發揮作用,支持全棧開發。

C/C在JavaScript口譯員和編譯器中的作用C/C在JavaScript口譯員和編譯器中的作用Apr 20, 2025 am 12:01 AM

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。1)C 用于解析JavaScript源码并生成抽象语法树。2)C 负责生成和执行字节码。3)C 实现JIT编译器,在运行时优化和编译热点代码,显著提高JavaScript的执行效率。

JavaScript在行動中:現實世界中的示例和項目JavaScript在行動中:現實世界中的示例和項目Apr 19, 2025 am 12:13 AM

JavaScript在現實世界中的應用包括前端和後端開發。 1)通過構建TODO列表應用展示前端應用,涉及DOM操作和事件處理。 2)通過Node.js和Express構建RESTfulAPI展示後端應用。

JavaScript和Web:核心功能和用例JavaScript和Web:核心功能和用例Apr 18, 2025 am 12:19 AM

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

Dreamweaver Mac版

Dreamweaver Mac版

視覺化網頁開發工具

VSCode Windows 64位元 下載

VSCode Windows 64位元 下載

微軟推出的免費、功能強大的一款IDE編輯器

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

Safe Exam Browser

Safe Exam Browser

Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具