3
2

More than 1 year has passed since last update.

ガントチャート(JSライブラリ: jquery.ganttView)をカスタマイズしてみた(いろんな形状(星・矢印・丸)でスケジュール管理)

Posted at

はじめに

今となっては古のJSライブラリ

上記ライブラリのスケジュール管理オブジェクトを、いろんな形状で置いてみたので、勘所をメモ書きしておきます。

ニッチな内容だけど、誰かの参考になれば幸いです。

全然、こんなニッチな物、誰も読まないだろうと思ってメモ書きしたら、意外に閲覧数があったので、第2弾です。(根強い人気があるんですかね?)

ソースコードは、前回のカスタマイズ(上記)の反映した上での改修となっています。

色んな形状って?

ganttChart.JPG

上記のような、丸、星、シャフトのついた矢印など、様々な図形でスケジュールを引けるように改造しました。

入力データの変更

各データに、type属性を追加して、図形の形を入力データで指示できるように変更します。

	{
		id: 2, name: "Feature 2", series: [
			{ name: "Planned", start: new Date(2010,00,05), end: new Date(2010,00,20), type: "box" },
			{ name: "Actual", start: new Date(2010,00,06), end: new Date(2010,00,17), type: "arrow", color: "#F15B55" },
			{ name: "Projected", start: new Date(2010,00,06), end: new Date(2010,00,17), type: "box", color: "#e0e0e0" },
			{ name: "Projected", start: new Date(2010,00,19), end: new Date(2010,00,19), type: "star", color: "#FFC20E" },
			{ name: "Projected", start: new Date(2010,00,21), end: new Date(2010,00,21), type: "circle", color: "#007394" }
		]
	}

サンプルデータの一部を抜粋してくると、上記のようなイメージです。

追加のスタイルシート

div.ganttview-arrow-cells {
	position: relative;
	height: 25px;
}

div.ganttview-star-clippath {
    position: relative;
    width: 25px;
    height: 25px;
    clip-path: polygon(50% 5%, 61% 40%, 98% 40%, 68% 62%, 79% 96%, 50% 75%, 21% 96%, 32% 62%, 2% 40%, 39% 40%);
}

div.ganttview-circle-clippath {
    position: relative;
    width: 25px;
    height: 25px;
    clip-path: circle(41% at 50% 50%);
}

div.ganttview-arrow-cell-clippath {
    position: absolute;
    height: 25px;
    clip-path: polygon(0 33%, 100% 33%, 100% 67%, 0 67%);
}

div.ganttview-arrow-triangle-cell-clippath {
    position: absolute;
    width: 25px;
    height: 25px;
    clip-path: polygon(0 0, 100% 50%, 0 100%, 30% 50%);
}

div.ganttview-arrow-circle-cell-clippath {
    position: absolute;
    width: 25px;
    height: 25px;
    clip-path: circle(41% at 50% 50%);
}

div.ganttview-star-cell-clippath {
    position: absolute;
    width: 25px;
    height: 25px;
    clip-path: polygon(50% 5%, 61% 40%, 98% 40%, 68% 62%, 79% 96%, 50% 75%, 21% 96%, 32% 62%, 2% 40%, 39% 40%);
}

div.ganttview-circle-cell-clippath {
    position: absolute;
    width: 25px;
    height: 25px;
    clip-path: circle(41% at 50% 50%);
}

clip-path を使って、いろんな図形を作るというのは、他にも記事をアップしてる方がいますので、jquery.ganttViewのカスタムの方に焦点を当てて、clip-pathに深くは触れません。

改修ポイント編(javascript)

前回、手をいれた「addBlocks」を、更に改造していきます。

addBlocks :管理オブジェクト描画の全体制御

function addBlocks(div, data, cellWidth, start) {
    //  ~省略
    // 入力データのループ
    for (var i = 0; i < data.length; i++) {
        for (var j = 0; j < data[i].series.length; j++) {
            var series = data[i].series[j];

            if("複数オブジェクトのキー" in series && series."複数オブジェクトのキー".length > 0){
                var tasks;
                tasks = jQuery("<div>", {
                            "class": "ganttview-block-cells"
                });
                rowIdx = addRowDataForTasks(div, tasks, data[i], series."複数オブジェクトのキー", cellWidth, start, rows, rowIdx);        
            } else {
                rowIdx = addRowData(div, data[i], series, cellWidth, start, rows, rowIdx);
            }
        }
    }
}

スケジュール管理オブジェクトを置くところは、全部、関数に切り出してしまいます。

1行に1管理オブジェクトを設置するようの関数(元々)

ですが、、、矢印型は、1行複数オブジェクト(丸型と箱型とシャフト型の組み合わせ)なので専用関数を呼び出します。

addRowData : 1行データの描画


        function addRowData(div, series, data, cellWidth, start, rows, rowIdx) {

            var styleName = judgeDisplayStyleType(data, false);

            var obj;
            if(CHART_TYPE.ARROW == data.type){
                var arrowObjs = jQuery("<div>", {
                    "class": "ganttview-arrow-cells"
                });
                obj = getArrowBlock(div, series, data, cellWidth, start, rows, styleName, arrowObjs);
            } else {
                obj = getEachBlock(div, series, data, cellWidth, start, rows, styleName, false);
            }
            
            jQuery(rows[rowIdx]).append(obj);

            rowIdx = rowIdx + 1;

            return rowIdx;
        }


こっちは、第1段で改造した、1行に複数の管理オブジェクトを設置する関数です。

addRowDataForTasks 1行に複数タスクを描画する用の関数

        function addRowDataForTasks(div, tasks, series, taskList, cellWidth, start, rows, rowIdx) {

            for(var i = 0; i < taskList.length; i++){
                var data = taskList[i];
                var styleName = judgeDisplayStyleType(data, true);
                if(CHART_TYPE.ARROW == data.type){
                    tasks = getArrowBlock(div, series, data, cellWidth, start, rows, styleName, tasks);
                }else{
                    var blockObj = getEachBlock(div, series, data, cellWidth, start, rows, styleName, false);
                    tasks.append(blockObj);    
                }
            }
            
            jQuery(rows[rowIdx]).append(tasks);
            rowIdx = rowIdx + 1;

            return rowIdx;
        }

こちらでも矢印型は、(丸型と箱型とシャフト型の組み合わせ)なので専用関数を呼び出します。

judgeDisplayStyleType 図形毎の専用スタイルシート呼び分け

        function judgeDisplayStyleType(data, multiObjectsFlag) {
            // デフォルト宣言
            var styleName = "ganttview-block";
            if(multiObjectsFlag){
                styleName = "ganttview-block-cell";
            }
            
            // 引き渡された指定値で表示する形状を変更
            if("type" in data && data.type){
                if(multiObjectsFlag){
                    // 1行内に複数の管理オブジェクトを配置
                    if(CHART_TYPE.BOX == data.type){
                        styleName = "ganttview-block-cell";
                    } else if(CHART_TYPE.ARROW == data.type){
                        styleName = "ganttview-arrow-cell-clippath";
                    } else if(CHART_TYPE.STAR == data.type){
                        styleName = "ganttview-star-cell-clippath";
                    } else if(CHART_TYPE.CIRCLE == data.type){
                        styleName = "ganttview-circle-cell-clippath";
                    }
                }else{
                    // 1行内に1行の管理オブジェクトを配置
                    if(CHART_TYPE.BOX == data.type){
                        styleName = "ganttview-block";
                    } else if(CHART_TYPE.ARROW == data.type){
                        // 矢印型は複数オブジェクト前提のため cell付きのスタイルを利用する
                        styleName = "ganttview-arrow-cell-clippath";
                    } else if(CHART_TYPE.STAR == data.type){
                        styleName = "ganttview-star-clippath";
                    } else if(CHART_TYPE.CIRCLE == data.type){
                        styleName = "ganttview-circle-clippath";
                    }
                }
            }
            return styleName;
        }

addRowDataとaddRowDataForTasksの関数で、それぞれの図形に合わせたスタイルシートを取得している為、追加したスタイルシートの、どれを利用するかサンプルデータの中の図形の指定でスタイルシートを切り替えます。

getArrowBlock 矢印型を描画するには、3つのオブジェクト(丸型・箱型・シャフト型)がいるので特殊処理を実装

        function getArrowBlock(div, series, data, cellWidth, startPlanDate, rows, styleName, objs) {
            
            var startPointObj = Object.create(data);
            var endPointObj = Object.create(data);

            // 補正用に開始日・終了日を退避する
            startPointObj['arrow_start'] = data.start;
            startPointObj['arrow_end'] = data.end;
            // 開始日基準で終了日を上書きする
            startPointObj.end = data.start;

            // 補正用に開始日・終了日を退避する
            startPointObj['arrow_start'] = data.start;
            startPointObj['arrow_end'] = data.end;
            // 終了日基準で開始日を上書きする
            endPointObj.start = data.end;

            // 強制的に丸型の箱をオブジェクトを用意する
            var startPontBlock = getEachBlock(div, series, startPointObj, cellWidth, startPlanDate, rows, "ganttview-arrow-circle-cell-clippath", true);
            // 矢印型を用意する
            var arrowObjBlock = getEachBlock(div, series, data, cellWidth, startPlanDate, rows, styleName, false);
            // 強制的に終端の矢印箱オブジェクトを用意する
            var endPointBlock = getEachBlock(div, series, endPointObj, cellWidth, startPlanDate, rows, "ganttview-arrow-triangle-cell-clippath", true);

            objs.append(startPontBlock);
            objs.append(arrowObjBlock);
            objs.append(endPointBlock);

            return objs;
        }

描画しようとしてるデータのディープコピーを作って、開始日・終了日を騙して、始点(丸型)と終点(シャフト型)を描画するようにして、組み合わせて矢印に見えるようにしています。

getEachBlock 各種図形の位置決め、微調整、描画

        
        function getEachBlock(div, series, data, cellWidth, start, rows, styleName,  camouflageDateFlag) {
            
            var size = DateUtils.daysBetween(data.start, data.end ) + 1;
            var offset = DateUtils.daysBetween(start, data.start);
            
            // 初期化
            var widthVal = 0;
            var marginLeftVal = 0;

            widthVal = ((size * cellWidth) - 5);
            marginLeftVal = (offset * cellWidth) + 1;
            
            // 丸型と星型の時は、正方形になるように幅を調整する
            if(CHART_TYPE.STAR == data.type || CHART_TYPE.CIRCLE == data.type){
                widthVal = 25;
            }

            // 矢印型で始点・終点用の箱を用意する時は微調整する
            if(CHART_TYPE.ARROW == data.type &&  camouflageDateFlag){
                widthVal = 25;
                var originArrowSize = DateUtils.daysBetween(data.arrow_start, data.arrow_end) + 1;
                size = originArrowSize;
            }

            // 矢印型の時で真ん中の箱は左横のマージンと横幅を微調整
            if(CHART_TYPE.ARROW == data.type && ! camouflageDateFlag){
                widthVal = widthVal - 5; 
                marginLeftVal = marginLeftVal + 7;
            }

            let title = data.name ;
            
            var block = jQuery("<div>", {
                "title": title + ", " + size + " 日間",
                "class": styleName,
                "css": {
                    "width": widthVal + "px",
                    "margin-left": marginLeftVal + "px",
                    "left" : "0px",
                    "right" : "0px",
                }
            });
            
            // 背景色指定をデータで受け取る
            if("color" in data && data.color){
                block.css("background-color", data.color);
            }

            if(CHART_TYPE.BOX == data.box){
                block.append(jQuery("<div>", { "class": "ganttview-block-text" }).text(size));
            }

            addBlockData(block, data, series);
            
            return block;
        }

既存の jquery.gannttViewの箱型を描画する為の処理が色濃く残ってるのが、この関数ですね。
図形によって、微調整(矢印型の重ね合わせ位置の調整、星型・丸型の縦幅・横幅の均一化)をしています。

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2