HTML 5 Canvasベースの電気通信キャビネットUビット動的管理


前言
Uはサーバの外部サイズを表す単位で、unitの略語で、詳細なサイズは業界団体であるアメリカ電子工業協会(EIA)が決定します.サーバのサイズを規定するのは、鉄やアルミニウムのラックに置くためにサーバを適切なサイズに保つためです.ラックにはサーバーを固定するネジ穴があり、サーバーのネジ穴と番号を合わせることができ、ネジで固定して、サーバーの設置に必要なスペースを便利にします.サーバの幅(48.26 cm=19インチ)と高さ(4.445 cmの倍数)が規定されています.幅が19インチであるため、この規定を満たすラックを「19インチラック」と呼ぶこともある.厚さは4.445 cmを基本単位とする.1 Uは4.445 cm、2 Uは1 Uの2倍の8.89 cm.「1 UのPCサーバ」とは、形状がEIA仕様を満たし、厚みが4.445 cmの製品です.19インチキャビネットに設置できるように設計された製品は、一般的にラックサーバと呼ばれています.
ワーク制御ではキャビネットUビットに適用するのが一般的ですが、2 D/3 Dモデルを作成する際には、デバイスを内側に追加することがよくあります.デバイスごとに占めるUビットが異なり、単純にキャビネット内部にノードを追加するだけでは、ノードが追加されていないときに具体的な効果を直感的に見ることができません.だから、追加の過程で機械室設備のUビット占有と効果を直接見せることができるかどうか、このDemoが生まれたと思います.
   
https://hightopo.com/demo/rack-builder/index.html
コード生成
シーン構築
全体のDemoは最も左側の木、中間部分のリスト、および右側の電気通信キャビネットのトポロジー全体から構成されており、レイアウト全体をきれいにするために、ここではsplitViewとborderPaneの2つのレイアウト方式を組み合わせて行う.まず、シーンを左右の2つの部分に分け、左側はツリー、右側はリストと電気通信キャビネットのトポロジーの組み合わせです.
treeView = this.treeView = new ht.widget.TreeView(),//     (http://www.hightopo.com/guide/guide/core/treeview/ht-treeview-guide.html)
splitView = this.splitView = new ht.widget.SplitView(treeView, null, 'h', 280);//     ,           ,      ,    ,      280,                      (http://www.hightopo.com/guide/guide/core/splitview/ht-splitview-guide.html)
this.splitView.addToDOM();

 
レイアウトの最後に、最も外側のコンポーネントの最下層divをbodyに追加することを忘れないでください.HTのコンポーネントは一般的にBorderPane、SplitView、TabViewなどのコンテナに埋め込まれて使用されますが、最も外側のHTコンポーネントは、getView()が返す下層div要素をページのDOM要素に手動で追加する必要があります.ここで注意しなければならないのは、親コンテナのサイズが変化すると、親コンテナがBorderPaneやSplitViewなどのHTで事前定義されたコンテナコンポーネントである場合、HTのコンテナは自動的に子供コンポーネントinvalidate関数を呼び出して更新を通知します.しかし、親コンテナが元のhtml要素である場合、HTコンポーネントは更新が必要であることを知ることができないため、最外層のHTコンポーネントは一般的にwindowのウィンドウサイズの変化イベントを傍受し、最外層コンポーネントinvalidate関数を呼び出して更新する必要がある.
最外層コンポーネントのロードがウィンドウに満ちている利便性のために、HTのすべてのコンポーネントにはaddToDOM関数があり、ivはinvalidateの略記である.
addToDOM = function(){
    var self = this,
          view = self.getView(),//        div
          style = view.style;
    document.body.appendChild(view);//     div   body 
    style.left = '0';//ht          position    absolute    
    style.right = '0';
    style.top = '0';
    style.bottom = '0';
    window.addEventListener('resize', function () { self.iv(); }, false);//        ,      
}

右側のトポロジー部分は、選択された変更イベントをリスニングするときに更新されます.もちろん、初期化設定された選択ツリーの最初のノードが選択された変更イベントをトリガーします.
cms.treeView.sm().ss(cms.treeView.dm().getDatas().get(0));//             
treeView.sm().ms(function(){//         
    var ld = treeView.sm().ld();//          
    if (ld) self.updateForm(ld.a('type'));
});
CMS.prototype.updateForm = function(type){
    var self = this,
        ld = this.treeView.sm().ld();//              
    if (type === self.TYPE_RACK_SPACE) {//            ,    “    ”              
        if (!this.rackBuild) {
            this.rackBuild = new RackBuild(this);//                ,              
        }
        this.rackBuild.setData(ld);//            
        this.splitView.setRightView(this.rackBuild.getHTView());//                    “  ”  +       
    }
}

上のコードのsplitView.setRightView関数は右側のコンポーネントを設定することを意味し、この関数があればspliteViewコンポーネントの右側のコンポーネントを動的に変更することができます.
初期化ツリー
   
レイアウトができた以上、具体的な場所に内容を追加しなければなりません.まず、ツリーに電気通信キャビネットノードを追加する方法を見てみましょう.まず、初期化されたツリーの値treeDataを定義し、この配列を巡ってツリー上のノードとノード上の親子関係を作成します.
var treeData = [{
    name: 'Racks',
    type: 8,
    children: [
        {
            name: 'rack1',
            type: 18,
            usize: 32
        }, {
            name: 'rack2',
            type: 18
        }
    ]
}];
CMS.prototype.loadTreeData = function(){//        
    var self = this;
    setTimeout(function(){
        var data = treeData;

        data.forEach(function(d) {//    treeData     
            self.createData(d, null);//          
        });
        self.treeView.expandAll();//    
    }, 10);
}

createData関数を使用してノードを作成し、ノードに親子関係を設定します.
CMS.prototype.createData = function(data, parent){//          
    var self = this,
        htData = new ht.Data(),//    Data     
        dm = this.treeView.dm();//         
    htData.a(data);//          data
    htData.setName(data.name)//       name   
    if (parent) {
        htData.setParent(parent);//       
    }
    dm.add(htData);//            
    if (data.children) {//        children   
        data.children.forEach(function(d){//    children   
            self.createData(d, htData);//     children             
        });
    }
    return htData;
}

シーンの右側を作成
目の先の学生は前のコードの中で1つの未宣言のRackBuildクラスに気づいたかもしれません.このクラスの声明の中で私たちはシーンの右半分を主に左右の2つの部分に分けて、左はまた上下の2つの部分に分けて、右も上下の2つの部分に分けます.
   
ここではまず右側の部分全体をレイアウトし、次のコードの変数listBorderは上図の左半分、変数borderPaneは上図の右半分、鷹眼コンポーネント部分はborderPaneの上位に追加されます.
listView = this.listView = new ht.widget.ListView(),//     (http://www.hightopo.com/guide/guide/core/listview/ht-listview-guide.html)
listForm = this.listForm = new ht.widget.FormPane(),//     (http://www.hightopo.com/guide/guide/plugin/form/ht-form-guide.html)
listBorder = this.listBorder = new ht.widget.BorderPane(),//           (http://www.hightopo.com/guide/guide/core/borderpane/ht-borderpane-guide.html)
gv = this.gv = new ht.graph.GraphView(),//     (http://www.hightopo.com/guide/guide/core/beginners/ht-beginners-guide.html#ref_graphview)
borderPane = this.borderPane = new ht.widget.BorderPane(),
toolbar = this.toolbar = new ht.widget.Toolbar(),//      (http://www.hightopo.com/guide/guide/core/toolbar/ht-toolbar-guide.html)
splitView = this.splitView = new ht.widget.SplitView(listBorder, borderPane, 'h', 220),//     
overview = this.overview = new ht.graph.Overview(gv),//     (http://www.hightopo.com/guide/guide/plugin/overview/ht-overview-guide.html)
overviewDiv = overview.getView();//          div

overviewDiv.style.height = '120px';// HT             
overviewDiv.style.width = '120px';
overviewDiv.style.left = '0';
overviewDiv.style.bottom = '0';
overviewDiv.style.zIndex = 10;
borderPane.getView().appendChild(overview.getView());//         div            div  

listBorder.setTopView(listForm);//       
listBorder.setCenterView(listView);//       
listBorder.setTopHeight(32);//         
listForm.setVPadding(2);//                  
listForm.setHPadding(4);//                  
listForm.addRow([//       
    {
        comboBox: {//     
            labels: ['All', 'Pathch Panel', 'Switch', 'Server', 'Backbone Switch/Router'],//            
            values: [-1, 5, 9, 10, 11],//        
            value: -1,//      ,      
            onValueChanged: function(e) {//        
                var val = this.getValue();//       
                self.listTypeFilter = val;
                self.listView.ivm();//         
            }
        }
    }
], [0.1], 28);//            ,        

borderPane.setCenterView(gv);//       
borderPane.setTopView(toolbar);//       
borderPane.setTopHeight(32);//         

上記のコードから分かるように、splitViewは最外層コンポーネントであり、getHTView関数でこのコンポーネントを返し、シーン全体の右半分のコンポーネントを前に動的に設定するときにthisを設定する.splitView.setRightView(this.rackBuild.getHTView())シーンの右半分がrackBuildの下位divに設定されています.
getHTView: function(){//        
    return this.splitView;
}

ツールバーの内容を追加
 
toolbarツールバーの要素は、キャビネットの追加、キャビネットの編集、キャビネットの削除の3つです.この3つの要素はsetItemsでtoolbarツールバーコンポーネントに追加するだけで、要素の具体的な定義は次のとおりです.
var toolbarItems = [//          
    {
        icon: self.getToolbarIcon('toolbar.add.rack'),//              
        toolTip: 'Add a rack',//         
        action: function(){//           
            self._editingRack = null;
            self.addRackForm.reset();
            self.addRackDialog.show();//      ,        ,         
        }
    },{
        icon: self.getToolbarIcon('toolbar.edit.rack', function(){//                             (        ,            )
            return self.gv.sm().ld() instanceof Rack;
        }),
        toolTip: 'Edit rack info',
        action: function(){
            var ld = self.gv.sm().ld();//    gv         
            if (!ld) return;
            self._editingRack = ld;
            self.addRackForm.v('name', ld.a('name'));//       name     ld       name   
            self.addRackForm.v('usize', ld.a('usize'));//       usize     ld       usize   
            self.addRackDialog.show();//            
        }
    },{
        icon: self.getToolbarIcon('toolbar.delete', function(){
            return self.gv.sm().ld() instanceof Rack;//                   
        }),
        toolTip: 'Delete a rack',
        action: function(){
            self.handleRemoveRack();//          ,             
        }
    },
]

次に、このitemをtoolbarに追加し、レイアウトを設定すればいいです.
toolbar.setItems(toolbarItems);//          
toolbar.setStickToRight(true);//              
toolbar.enableToolTip(true);//          

上に表示されるtoolbarツールバーボタンをクリックしてトリガーされるイベントには、thisを介して「ポップアップダイアログ」の操作があります.addRackDialog.show()を実装し、addRackDialogオブジェクトはinitDialog関数で定義され、dialogダイアログボックス(http://www.hightopo.com/guide/guide/plugin/dialog/ht-dialog-guide.html)を使用して、このダイアログボックスの内容を1つのformフォームに表示するように設定し、2つのボタンを設計しました.「OK」ボタンはキャビネットの作成/変更を実行するプロパティとして使用され、「Cancel」ボタンは他の操作を実行せず、ダイアログボックスを非表示にします.
initDialog: function(){//      “  ”      
    var self = this,
        addRackDialog = this.addRackDialog = new ht.widget.Dialog(),
        addRackForm = this.addRackForm = new FormPane(),//       ht.widget.FormPane
        labelWidth = 72;

    addRackForm.addRow([//    
        'Name',{
            id: 'name',
            textField: {}
        }
    ], [labelWidth, 0.1]);

    addRackForm.addRow([
        'Height(U)',{
            id: 'usize',
            textField: {
                type: 'number'
            }
        }
    ], [labelWidth, 0.1]);

    addRackDialog.setConfig({//         ,  ,   
        title: "New Rack",//       
        content: addRackForm,//         
        width: 320,//         
        height: 220,//         
        draggable: true,//               
        closable: true,//     true/false,          
        resizeMode: "none",//                      none         
        buttons: [//           
            {
                label: "Ok",//       
                action: function(button, e) {// action     ,         ,       
                    var formData = addRackForm.getValueObject(), rack;
                    if (!formData.usize) {//        Height   ,      18
                        formData.usize = 18;
                    }
                    if (self._editingRack) {//    “  rack  ”   
                        rack = self._editingRack;
                        rack.a(formData);
                        rack.a('treeNode').a(rack.getAttrObject());// 
                    }
                    else {// “  ”    
                        rack = self.createRack(formData);//        rack   
                        self.gv.dm().add(rack);//          rack
                        // update tree
                        formData.type = self.cms.TYPE_RACK;
                        var treeNode = self.cms.createData(formData, cms.treeView.sm().ld());
                        rack.a('treeNode', treeNode);
                    }
                    self.gv.fitContent(1);//       ,            
                    addRackDialog.hide();//      
                }
            }, {
                label: 'Cancel',
                action: function(){
                    addRackDialog.hide();//      
                }
            }
        ],
        buttonsAlign: "right"
    });
}

上のコードに現れるFormPaneクラスはhtに継承されます.widget.FormPaneクラス、htwidget.FormPaneに基づいて修正してもいくつかの関数が追加されましたが、主な内容はhtです.widget.FormPaneの実現は、文章の幅が限られているので、ここではコードを貼らないで、興味のある人はFormPaneを参考にすることができます.jsファイル.
キャビネットの追加と編集の2つの機能を実現し、キャビネットを削除する機能は非常に簡単です.ノードをトポロジーとツリーから削除すればいいです.
handleRemoveRack: function(){//          ,             
    var ld = this.gv.sm().ld();//    gv           
    if (ld && ld instanceof Rack) {//     Rack   
        this.cms.treeView.dm().remove(ld.a('treeNode'));//        treeNode      
        this.gv.dm().remove(ld);//    gv     
    }
}

リスト内の要素のドラッグ&ドロップ
   
すべてのコンテンツが作成され、次に考慮するのはインタラクティブなコンテンツです.リスト・コンポーネントには、handleDragAndDrop関数によるドラッグ・アンド・ドロップ機能があります.
listView.handleDragAndDrop = this.handleListDND.bind(this);//          (http://www.hightopo.com/guide/guide/core/listview/ht-listview-guide.html)
handleListDND: function(e, state){//   listView          
    var self = this,
        listView = self.listView,
        gv = self.gv,
        dm = gv.dm(),
        dnd = self.dnd;

    // handleDragAndDrop     prepare-begin-between-end     
    if (state ==='prepare') {
        var data = listView.getDataAt(e);//            event    ,           
        listView.sm().ss(data);//                         
        if (dnd && dnd.parentNode) {
            document.body.removeChild(dnd);
        }
        dnd = self.dnd = ht.Default.createDiv();//      div
        dnd.style.zIndex = 10;
        dnd.innerText = data.getName();
    }
    else if (state === 'begin') {
        if (dnd) {
            var pagePoint = ht.Default.getPagePoint(e);//       
            dnd.style.left = pagePoint.x - dnd.offsetWidth * 0.5 + 'px'; 
            dnd.style.top = pagePoint.y - dnd.offsetHeight * 0.5 + 'px';
            document.body.appendChild(dnd)
        }
    }
    else if (state === 'between') {
        if (dnd) {
            var pagePoint = ht.Default.getPagePoint(e);
            dnd.style.left = pagePoint.x - dnd.offsetWidth * 0.5 + 'px';
            dnd.style.top = pagePoint.y - dnd.offsetHeight * 0.5 + 'px';
            self.showDragHelper(e);
        }
    }
    else {//   “  ”      
        if (ht.Default.containedInView(e, self.gv)) {//              View    
            if (dm.contains(self.dragHelper)) {//          data  
                var rect = self.dragHelper.getRect(),//          (    )
                    target = self.showDragHelper(e),// 
                    node,
                    ld = self.listView.sm().ld(),
                    uindex = target.getCellIndex(rect.y);
                node = self.createPane(rect, ld.getAttrObject(), target, uindex);//     
                dm.add(node);
                // update tree data
                var treeNode = self.cms.createData(ld.getAttrObject(), target.a('treeNode'));//        ,       
                treeNode.a('uindex', uindex);
                node.a('treeNode', treeNode);

                dm.remove(self.dragHelper);
            }
        }
        document.body.removeChild(dnd);
        self.dnd = null;
    }
}

デバイスドラッグ
   
リストコンポーネントからドラッグ&ドロップするインタラクティブな動作がある以上、次にデバイスがキャビネット上のドラッグ&ドロップで位置を変える機能を行うべきであり、トポロジコンポーネントgvのインタラクティブなイベントをリスニングすることによってノードの移動をイベント処理する.
gv.mi(this.handleInteractor.bind(this));//     
handleInteractor: function(e){//               
    if (e.kind.indexOf('Move') < 0) return;//    move           

    var self = this,
        listView = self.listView,
        gv = self.gv,
        dm = gv.dm(),//       
        target = gv.sm().ld(),//          
        uHeight = target.a('uHeight') || 1;// target.a('uHeight')            

    if (e.kind === 'prepareMove') {//     
        self._oldPosition = target.p();//          
    }
    else if (e.kind === 'betweenMove') {//     
        self.showDragHelper(e.event, uHeight);
        dm.sendToTop(target);//  data      ,                
    }
    else if (e.kind === 'endMove') {//     
        var rack = self.showDragHelper(e.event, uHeight);
        if (dm.contains(self.dragHelper)) {//          data  
            target.p(self.dragHelper.p());//        
            target.a('uindex', rack.getCellIndex(target.p().y));//           uindex
            dm.remove(self.dragHelper);//   
            self._savable = true;
            self.toolbar.iv();
            target.setHost(rack);//       
            target.setParent(rack);//       
            // update tree
            var treeNode = target.a('treeNode');//               
            treeNode.setParent(rack.a('treeNode'));
        }
        else {
            target.p(self._oldPosition);
        }
    }
}

コードのshowDragHelperは、デバイスをドラッグしている間にキャビネットに表示され、デバイスの下に位置を占める緑色の矩形があり、現在移動している位置がキャビネットに表示されている位置を容易に見るために表示されます.興味のある人は自分で理解してもいいですが、紙幅が限られているので、ここでは言いません.
リストコンポーネントフィルタ
   
リストバーの上部のformフォームをフィルタリングするのに興味のある学生はいますか?このコードは非常に簡単で、選択したタイプをフィルタするだけでいいです.
listView.setVisibleFunc(function(data){//        
    if (!self.listTypeFilter || self.listTypeFilter === -1)
        return true;
    return data.a('type') === self.listTypeFilter;//            type                   form                    
});

主なコードはここまで説明して、その他の部分の内容は興味のある学生は自分でコードをほじくって理解することができますhttps://hightopo.com/demo/rack-builder/index.html.まだ分からないのは公式サイトで知ることができますhttps://hightopo.com/