文章出處

1、Twproject Gantt甘特圖介紹

    Twproject Gantt 是一款基于 jQuery 開發的甘特圖組件,也可以創建其它圖表,例如任務樹(Task Trees)。內置編輯、縮放和 CSS 皮膚等功能。更重要的是,它是免費開源的。

    官網地址是:https://gantt.twproject.com/ 源碼可以從github下載:

2016-09-17_112730

2、擴展功能一:code自動層級編碼,滿足wbs編碼要求

    工作分解結構 (WBS) 代碼是項目的識別您的分級顯示結構中的每個任務的唯一位置的字母數字代碼。WBS 代碼可用于報告日程和跟蹤成本。在工程系統中,應用非常廣泛,相關的編碼還有OBS\RBS\PBS等等。

   在Microsoft Office Project中的WBS代碼有兩種:大綱數字和自定義WBS代碼。大綱數字是最簡單的 WBS 編碼類型。Microsoft Office Project 將自動計算每項任務大綱數字基礎上的任務列表的大綱結構編號。例如,您的任務列表中的第一個任務的編號的 1。如果該任務具有三個子任務,子任務是編號 1.1、1.2 和 1.3。大綱數字只包含數字 (無號) 和不能對其進行編輯。他們執行操作,但是,自動移動時更改任務向上或向下在任務列表中,當降級或升級任務。例如,如果子任務當前具有的大綱數字為 3.5.4,并且移列表中的一行,大綱數字將自動更新為 3.5.3。如果然后升級該相同的子任務,大綱數字將自動更新到 3.6。

   理解WBS編碼的概念,擴展Twproject Gantt的code編碼,采用大綱數字。如下圖:

image

    對ganttMaster.js進行擴展,增加屬性:isLevelCode(bool類型),是否自動對code進行編碼。增加擴展方法levelCode:

GanttMaster.prototype.levelCode = function () {
    if (this.tasks && this.tasks.length > 0) {
        var curCodeExt = 1;
        for (var i = 0; i < this.tasks.length; i++) {
            var tsk = this.tasks[i];
            if (tsk.level == 0) {
                tsk.code = curCodeExt;
                levelChildCode(tsk.getChildren(), tsk.code);
                curCodeExt++;
            }
        }
    }

    function levelChildCode(cTasks, prefix) {
        if (cTasks && cTasks.length > 0) {
            var curCodeExt = 1;
            for (var i = 0; i < cTasks.length; i++) {
                var tsk = cTasks[i];
                tsk.code = prefix + "." + curCodeExt;
                levelChildCode(tsk.getChildren(), tsk.code);
                curCodeExt++;
            }
        }
    }
}

    本函數采用遞歸實現:

%5V1IPFG8}OCFC`ZQF2UGS9

    調用步驟:

    定義甘特圖:var ge; //this is the hugly but very friendly global var for the gantt editor

    加載甘特圖模板: $("#ganttemplates").loadTemplates();

    初始化甘特圖:

          // here starts gantt initialization
            ge = new GanttMaster();
            //TODO:是否自動顯示bar
            ge.isBrowserTaskBar = true;
            //TODO:設置code是否自動編碼
            ge.isLevelCode = true;
            var workSpace = $("#workSpace");
            workSpace.css({
                width: $(window).width() - 20,
                height: $(window).height() - 100
            });
            ge.init(workSpace)

    加載數據并調用層級方法:

         function loadFromLocalStorage() {
            var ret;
            if (localStorage) {
                if (localStorage.getObject("teamworkGantDemo")) {
                    ret = localStorage.getObject("teamworkGantDemo");
                }
            } else {
                $("#taZone").show();
            }
            if (!ret || !ret.tasks || ret.tasks.length == 0) {
                ret = JSON.parse($("#ta").val());
                //actualiza data
                var offset = new Date().getTime() - ret.tasks[0].start;
                for (var i = 0; i < ret.tasks.length; i++)
                    ret.tasks[i].start = ret.tasks[i].start + offset;
                //debugger;
            }
            ge.loadProject(ret);
            ge.checkpoint(); //empty the undo stack
            ge.levelCode();
        }

     可查看gantt213.html頁面。

3、擴展功能二:讓選擇的task出現在顯示窗口

3.1 效果圖

    選擇task對應的gantt bar顯示出來,且在滑動窗口居中顯示,效果如下圖:

a

3.2 ganttMaster修訂

    經過dom之間關系分析,找到其中的內在聯系,主要增加如下方法:

GanttMaster.prototype.browserTaskBar = function (tsk) {
    if (tsk && tsk instanceof Task) {
        var id = tsk.id;
        //找到taskBar
        var taskBar = $("svg[taskid=\"" + id + "\"]");
        var taskBarX = parseInt($(taskBar).attr("x"));
        var taskBarWidth = parseInt($(taskBar).attr("width"));
        //獲取svg最外層畫布canvas
        var canvas = $(taskBar).parent().parent().parent();
        var canvasWidth = $(canvas).width();
        //獲取滾動區域
        var scroll = $(taskBar).parent().parent().parent().parent();
        var scrollWidth = $(scroll).width();
        var scrollScrollWidth = canvasWidth - scrollWidth + 17;
        //獲取水平滾動條要移動的位置
        var centerLeft = (taskBarX + taskBarWidth / 2);
        var scrollLeft = (centerLeft – scrollWidth/2) / canvasWidth * scrollScrollWidth;
        console.log("-------------------------------------------------------");
        console.log("canvas width:" + canvasWidth); //畫布區域大小
        console.log("scroll width:" + scrollWidth); //滾動區域大小
        console.log("scroll scroll max width:" + scrollScrollWidth); //滾動區域最大滑動區域
        console.log("scroll left position(old):" + $(scroll).scrollLeft()); //滾動區域滑塊位置(舊)
        $(scroll).scrollLeft(scrollLeft); //設置滑塊位置
        console.log("scroll left position(new):" + $(scroll).scrollLeft()); //滾動區域滑塊位置(新)
        console.log("-------------------------------------------------------");
    }
};

    方法講解:根據taskid找到bar,獲取對應的div層與滾動窗口,設置滾動窗口的scrollLeft。

    注意:紅色底紋17,是如何得到的呢?這個是滾動條寬度,3.4中有詳細分析。

3.3 思路與模型

    設置bar在滾動區域的中心位置,就是如何設置滾動區域的scrollLeft值,這是解決問題的關鍵。原甘特圖如下:

image

畫布區域(灰色部分)

image

滾動區域(灰色部分)

    對應的dom層級結構如下:

{A79DD(QZ321T4GII1I4)[E

    從Bar到展示區域,總共是4層嵌套。找到對應的bar,分析上層的節點間的依賴關系,從以下4個問題入手:

    • (1)如何找到Bar?
    • (2)上層節點哪些起決定作用?
    • (3)畫布區域與滾動區域的關系如何?
    • (4)滾動區域的最大scrollLeft是多少?

問題一:如何找到Bar?

    Bar與任務項是聯動的,兩者是通過taskid進行關聯的,比如找到taskid=-6的甘特圖形:

        var id = -6;
        //找到taskBar
        var taskBar = $("svg[taskid=\"" + id + "\"]")

問題二:上層節點哪些起到決定作用?

    上層節點只有畫布區域與滾動區域起到作用,bar在這兩個區域的位置是如何的?通過分析dom結構,建立以下模型:

L`W1OF2_(XE4{56F@TR}C3Y

模型示意圖1

    注意事項:(1)垂直滾動條暫不考慮。(2)task bar相對于畫布區域的坐標x是不變的。   

問題三:畫布區域與滾動區域的關系如何?

    從模型示意圖來看,滾動區域必須而且正好可以查看畫布區域,要保證這點,canvasWidth與scrollScrollWidth是一定的比例關系,則比例系數為scale=canvasWidth/scrollScrollWidth,表示滑動區域的比例,例如滑動區域向左滑動1,則畫布區域則向右滑動scale。

(1)當出現滾動條時,canvasWidth>scrollScrollWidth>scrollWidth,而scale>1

(2)當滾動區域寬度增加時,scrollScrollWidth是在逐漸變小

(3)未出現滾動條時,scrollScrollWidth = 0

    解惑,從模型來看,scrollWidth應該是大于scrollScrollWidth的吧?如何把比自己更大的畫布區域包括進來,就是依靠滾動區域,這里可看作scrollScrollWidth是對scrollWidth的放大,其放到比例就是scale。

問題四:滾動區域最大scrollLeft是多少

    如何獲取某一時刻,最大scollLeft。解決辦法,通過chrome瀏覽器打開甘特圖,將滾動區域的滑塊拉到最左邊,在console窗口輸入$("div[class='splitElement splitBox2']").scrollLeft();可得到。如下圖:

image

3.4 數字分析確定依賴關系

    根據chrome調試器,可獲取canvasWidth、scrollWidth、scrollScrollWidth,甘特圖原始狀態下,使用兩個瀏覽器記測試,數據記錄如下:

image

Excel分析圖表1

   對canvasWidth-scrollWidth與scrollScrollWidth比較,絕對值都為17,得到結果為:

var scrollScrollWidth = canvasWidth - scrollWidth + 17

    如果甘特圖處于放到、縮小狀態,是否成立呢?再次測試結果如下:

image

Excel分析圖表2

    無論甘特圖如何縮放,公式依然成立。這就找到了滾動窗口最大滑動區域scrollScrollWidth,bar的顯示位置,對應的值就是scrollScrollWidth的一部分。把模型中的bar至于中心位置,如下圖:

%L}9516[GEF}ZZVVDM4AWTE

模型示意圖2

    上圖清晰的給出以下結論:left=centerLeft - scrollWidth/2,計算滑塊的位置,也就是找到left在滑塊上的映射scrollLeft,結合scale系數,可計算出scrollLeft = left * scale。

    這樣就實現了定位scrollLeft,真的是這樣嗎?經過測試,發現bar在滾動區域,并不在中心位置。從分析方法來看,比較臃腫、復雜,要簡化處理,如何做呢?下面將繼續進行分銷模型并優化。

3.5 模型深入分析與優化

    在模型分析中,沒有對遮擋區域進行解析,分析圖表如下:

FCLYV@HWGDWQ%V`}SO4H4@3

模型示意圖3

    遮擋區域寬度maskWidth(maskWidth1+maskWidth2)=canvasWidth – scrollWidth,與scrollScrollWidth相差17,也就是scrollScrollWidth≈maskWidth。而滾動條的存在,正是滑動遮擋區域,scrollLeft≈left,這樣就非常簡單化了。考慮17呢?為什么是固定值?而不是變量值呢?

    為了弄懂這個問題,我們使用QQ截圖滾動區域的垂直滾動條,如下圖:

image

    推測17是垂直滾動條的寬度,對此可忽略不計,則scrollScrollWidth=maskWidth,可以看作scrollLeft=left=maskWidth1,實施也的確如此。

    修訂后的js代碼如下(也包括獲取svg相關屬性的修訂):

GanttMaster.prototype.browserTaskBar = function (tsk) {
    if (tsk && tsk instanceof Task) {
        var id = tsk.id;
        //找到taskBar
        var taskBar = $("svg[taskid=\"" + id + "\"]");
        //var taskBarX = $(taskBar)[0].getBoundingClientRect().left;
        var taskBarX = parseFloat($(taskBar)[0].getAttribute("x"));
        var taskBarWidth = parseFloat($(taskBar)[0].getBoundingClientRect().width);
        //獲取svg最外層畫布canvas
        var canvas = $(taskBar).parent().parent().parent();
        var canvasWidth = parseFloat($(canvas).width());
        //獲取滾動區域
        var scroll = $(taskBar).parent().parent().parent().parent();
        var scrollWidth = parseFloat($(scroll).width());
        //獲取水平滾動條要移動的位置
        var centerLeft = (taskBarX + taskBarWidth / 2.0);
        var scrollLeft = (centerLeft - scrollWidth / 2.0);
        var oldScrollLeft = $(scroll).scrollLeft();
        if (Math.abs(oldScrollLeft - scrollLeft) >= 1.0) {
            $(scroll).scrollLeft(scrollLeft); //設置滑塊位置
        } else {
            return;
        }
        console.log("-------------------------------------------------------");
        console.log("task left:" + taskBarX);
        console.log("task width:" + taskBarWidth);
        console.log("canvas width:" + canvasWidth); //畫布區域大小
        console.log("scroll width:" + scrollWidth); //滾動區域大小
        console.log("scroll left position(old):" + oldScrollLeft); //滾動區域滑塊位置(舊)
        console.log("scroll left position(new):" + scrollLeft); //滾動區域滑塊位置(新)
        console.log("-------------------------------------------------------");
    }
};

    運行后的效果圖如下:

b

    最終優化后的代碼:http://files.cnblogs.com/files/zsy/gantt.rar

    溫馨提示,如對您有幫助,麻煩贊一下。~~~~~~~~~~


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜

    大師兄 發表在 痞客邦 留言(0) 人氣()