基於 HTML5 的網路拓撲圖之 DataBinding 資料繫結

圖撲軟體發表於2019-03-13

前言

相信大家對於我從 json 檔案中直接操作節點屬性來控制介面的動態變化感到比較好奇,所以這篇就針對資料繫結以及如何使用這些繫結的資料做一篇說明,我寫了一個簡單的例子,基於機房工控的伺服器上裝置的燈閃爍現象。我們從 2D 和 3D 兩個角度來分析資料繫結的問題。

2D 效果圖:
圖片描述

程式碼實現

其實不管是 2D 還是 3D,在 HT 中,資料繫結不分維度的,所以兩者在實現上非常類似。

程式碼下載地址:https://download.csdn.net/download/u013161495/10290996

繪製裝置

2D 和 3D 中的裝置都是基於下面這張用“向量”繪製的一個機櫃內部裝置,並且對這個向量的“閃爍燈”部分加了資料繫結,具體繫結了“閃爍燈”的背景顏色以及陰影顏色,改變陰影顏色是為了讓“燈”有“發光”的效果,下圖中的紅色方框即為“閃爍燈”。

圖片描述

首先我們必須清楚如何繪製向量(http://hightopo.com/guide/guide/core/vector/ht-vector-guide.html) 我們這個 Demo 整體的向量繪製比較複雜,我就只說一下上圖中的“燈”矩形框和文字是怎麼繪製的。

我們知道,繪製一個向量 json 必須包含以下三個引數:

每個元素肯定都是要寬度和高度的,這兩個屬性不做說明,comps 這個屬性倒是挺厲害的,上面第三點中提到了,這是一個陣列,繪製圖形和文字的實際操作如下:

{
    "width": 48, // 向量整體寬度
    "height": 262, // 向量整體高度
    "comps": [
    {
        "type": "text", // 文字型別
        "text": "埠3", // 文字內容
        "align": "center", // 文字對齊方式
        "color": "rgb(255,255,255)", // 字型顏色
        "font": "8px arial, sans-serif", // 文字字型大小
        "rect": [ // 元件繪製在向量中的矩形邊界
            18.28654, // x 軸座標
            39.80679, // y 軸座標
            27.82265, // width
            11.5434 // height
        ]
    },
    {
        "type": "rect", // 矩形
        "background": "rgb(255,0,0)", // 屬性預設值
        "shadow": true, // 設定為 true 顯示陰影
        "shadowOffsetX": 0, // 選中圖元的陰影水平偏移
        "shadowOffsetY": 0, // 選中圖元的陰影垂直偏移
        "rect": [ // 元件繪製在向量中的矩形邊界
            4.38544, // x 軸座標
            32.55505, // y 軸座標
            14.46481, // width
            6.1554 // height
        ]
    }]  
}

這段程式碼繪製了一個矩形和一個文字。

繪製完向量之後,我們就可以通過給節點設定圖片的方式來顯示這個向量。當然上面繪製的向量並不是全部的繪製向量的程式碼,具體內容請參考:https://download.csdn.net/download/u013161495/10290996

要動態改變一個節點的屬性,那麼肯定要先獲取到這個節點,我們可以通過遍歷資料模型 DataModel,或者通過 tag 標籤(節點的唯一識別符號)獲取節點,又或者通過滑鼠點選事件等等。這個 Demo 中需要操作的節點比較多,所以我選擇用遍歷資料模型的方法來獲取節點。那麼問題來了,我怎麼通過一張圖片或者一個向量定位這個節點?如果節點都沒有建立,也不可能獲取到圖片對應的節點(或者說如果直接把這個向量圖拿來作為一個節點的圖片,有可能出現的情況就是,六個裝置的變化情況都一模一樣!畢竟是同一個節點!)。所以我們得把這些特殊的部分從圖片中刪除掉,然後在對應的位置填充上節點,再給節點設定上裝置的向量圖。先把對應位置的向量圖刪除掉,如下圖紅框部分:

圖片描述

我們在紅框部分單獨建立八個裝置節點,並給這八個節點分別設定同一張向量圖。誒?你可能會詫異為什麼同一張圖顯示卻不同(燈亮的變化順序不同),下面我們來看看這是怎麼完成的。

圖片描述

那麼這八個擁有相同向量圖的裝置是如何通過程式碼控制閃爍燈隨機變化的呢?關鍵就在我們上面繪製的向量圖中,前面有意略過了這部分:資料繫結。

資料繫結

由於燈閃爍是通過設定矩形的背景顏色來實現的(當然我這裡還加了一個陰影,為了有“亮燈”的效果),所以我們對這個矩形的背景顏色屬性進行資料繫結,然後通過 data.a 方法獲取和設定屬性值。

{
    "type": "rect", // 矩形
    "background": { // 矩形背景
        "func": "attr@rectBg2", // 資料繫結 string 型別若以 attr@*** 開頭,則返回 data.getAttr(***) 值,其中 *** 代表 attr 的屬性名
        "value": "rgb(255,0,0)" // 屬性預設值
    },
    "shadow": true, // 設定為 true 顯示陰影
    "shadowColor": { // 陰影顏色
        "func": "attr@shadowColor2", // 資料繫結 string 型別
        "value": "rgba(255,0,0,0.35)" // 屬性預設值
    },
    "shadowOffsetX": 0, // 選中圖元的陰影水平偏移
    "shadowOffsetY": 0, // 選中圖元的陰影垂直偏移
    "rect": [ // 元件繪製在向量中的矩形邊界
        4.38544, // x 軸座標
        32.55505, // y 軸座標
        14.46481, // width
        6.1554 // height
    ]
}

上面是我對矩形燈向量的部分重新繪製後的程式碼,看出什麼不同了?對,background 屬性和 shadowColor 屬性都出現了兩個值,並且這兩個值看起來“怪怪的”?資料繫結(http://hightopo.com/guide/guide/core/databinding/ht-databinding-guide.html) 沒有那麼難,繫結的格式很簡單,只需將以前的引數值用一個帶 func 屬性的物件替換即可,如果對應的 func 取得的值為 undefined 或 null 時,則會採用 value 屬性定義的預設值。

func 的內容有以下幾種型別:

  • function 型別,直接呼叫該函式,並傳入相關 Data 和 view 物件,由函式返回值決定引數值,即 func(data, view) 呼叫。
  • string 型別:
    • style@*** 開頭,則返回 data.getStyle(***) 值,其中 *** 代表 style 的屬性名。
    • attr@*** 開頭,則返回 data.getAttr(***) 值,其中 *** 代表 attr 的屬性名。
    • field@*** 開頭,則返回 data.*** 值,其中 *** 代表 data 的屬性名。
    • 如果不匹配以上情況,則直接將 string 型別作為 data 物件的函式名呼叫 data.***(view),返回值作為引數值。

所以我們通過 “func” 來繫結資料,這裡用的是 attr@*** 的方式繫結,到時候要呼叫這個屬性的時候就直接通過 data.getAttr(***) 或者縮寫 data.a(***) ;然後通過 “value” 設定一個預設值,作為 func 返回的值為空時的“備用”。

一般我們將程式碼比較多的向量圖放在一個 json 檔案中,我取名叫做 service3d.json 放在 scene 資料夾下 ,通過 ht.Default.xhrLoad 方法解析 json 檔案的內容,如下:

ht.Default.xhrLoad('scene/service3d.json', function(text) {
    var json = ht.Default.parse(text);
    dm.deserialize(json); // 反序列化
})

其中 deserialize 反序列化函式是將資料反序列化到模型,傳入的引數 json 為資料資訊物件,用於解析生成對應的 Data 物件並新增到資料容器中。

因為 xhrLoad 方法是非同步載入,為了避免後面出現獲取不到資料的問題,我們將剩下的節點屬性控制程式碼也寫在 xhrLoad 函式中:

dm.each(function(data) { // 遍歷 dataModel
    var infos = [ // 我設定的業務屬性名稱
        {shadowColor: 'shadowColor1', background: 'rectBg1'},
        {shadowColor: 'shadowColor2', background: 'rectBg2'},
        {shadowColor: 'shadowColor3', background: 'rectBg3'},
        {shadowColor: 'shadowColor4', background: 'rectBg4'},
        {shadowColor: 'shadowColor5', background: 'rectBg5'},
    ];
    infos.forEach(function(info) { // 遍歷 infos 陣列
        data.a(info.shadowColor, 'rgba(255, 0, 0, 0.35)'); // 註冊業務屬性 attr 為業務屬性 簡寫為 a
        data.a(info.background, 'rgb(255, 0, 0)');
    });

    setInterval(function() { // 設定動畫 動態變化閃爍燈的亮和滅的顯示
        var random = Math.ceil(Math.random() * 5); // 獲取 5 以內一個隨機整數 (可以配合我設定的業務屬性名稱)
        var shadowName = 'shadowColor' + random,
            bgName = 'rectBg' + random;

        if(data.a(shadowName) === 'rgba(255, 0, 0, 0.35)') { // 如果是紅色透明
            data.a(shadowName, 'rgba(0, 255, 0, 0.35)'); // 設定為綠色透明
        }
        else if(data.a(shadowName) === 'rgba(0, 255, 0, 0.35)') { // 如果是綠色透明
            data.a(shadowName, 'rgba(255, 0, 0, 0.35)'); // 設定為紅色透明
        }

        if(data.a(bgName) === 'rgb(255, 0, 0)') { // 如果是紅色
            data.a(bgName, 'rgb(0, 255, 0)'); // 設定為綠色
        }
        else if(data.a(bgName) === 'rgb(0, 255, 0)') { // 如果是綠色
            data.a(bgName, 'rgb(255, 0, 0)'); // 設定為紅色
        }
    }, 1000);
    
});

值得注意的一點是,雖然我們在 json 中已經繫結了業務屬性(這裡是“shadowColor1,2,3,4,5…”和“rectBg1,2,3,4,5”),但是節點上並沒有這個屬性,所以我們需要註冊一下這些屬性,並給這些屬性設定屬性值。

然後我們就可以通過呼叫這些屬性來動態更新 Data 上的屬性值圖形介面就會自動重新整理,從而達到實時顯示資料的效果。因為 HT 只有一個資料模型,繫結 DataModel 的圖形元件並沒有元件內部的其他資料模型,所以元件都是如實根據 DataModel 來呈現介面效果,因此當使用者拖拽圖元移動時, 本質也是修改了資料模型中 Node 的 position 位置值,而該屬性變化觸發的事件通過模型再次派發到圖形元件,引發圖形元件根據新的模型資訊重新整理介面。

總結

其實資料繫結沒有什麼很深奧的部分,HT 也不需要你考慮太多,一切以最簡單的方式進行著。這個 Demo 需要注意的就是,相同的圖片,如果要顯示不同,那麼肯定需要建立不同的節點,若是節點相同,那麼變化肯定相同的!

相關文章