基於 HTML5 Canvas 的 3D 渲染引擎構建生產管控系統

前言

這一期爲大家帶來一個非常好玩的 demo,我們製作一套自己的 3D 管道控制系統,運用了( http://www.hightopo.com )HT 的 Graph3dView 組件通過對 WebGL 底層技術的封裝,與 HT 其他組件一樣,基於 HT 統一的 DataModel 數據模型來驅動圖形顯示。

效果圖

此爲 2D 主界面:

此爲 3D 界面的部分分段演示:

由於 gif 上傳有大小限制,所以請大家務必去網頁感受和體驗,雙擊進口閥開始。 ( 戳我進入!

代碼實現

主要教大家的是一種流程動畫的製作方式,我用到包括動畫在內的多種方法,下面我聽我慢慢道來。由於是 3D 界面,關於創建 3D 渲染引擎組件,可視化呈現數據模型的三維環境場景我之前有講過,就是 dataModel 和 graph3dView。(後面用簡寫的dm,gv代替)

我們先對整體界面的基礎進行一下設置:

// 禁止拖動
gv.setMovableFunc(function() { return false })
// 設置眼睛
gv.setEye([-922, 1745, 4659])
// 設置中心點
gv.setCenter([98, 621, -318])

然後,把需要加動畫的閥都獲取到,我們按照步驟依次來,以免落下關鍵步:

var a = dm.getDataByTag('進口電動球閥')
var b = dm.getDataByTag('旁通閥')
var c = dm.getDataByTag('出口電動球閥')
...
...

可以開始我們的動畫設計了!我用的是 flyTo() 的方法,事實證明這種效果真的很不錯。我們要事先準備好所有的動畫組並把它們串聯在一起,我設計的開始演示是通過雙擊進口閥來控制。比如第一步,應該打開將進口球閥由遠程控制轉爲就地控制。所以,我們要讓鏡頭從這裏開始:

gv.mi(function (e) {
    if (e.kind === 'doubleClickData') {
        if (e.data.getTag() === '進口電動球閥') {
            gv.flyTo(n, {
                animation : true,
                direction : [-200, 0, 0],
                distance : 100
            })
            anim1()
        }
    }
})

注意 mi 是增加交互事件監聽器,addInteractorListener 的縮寫。

// 示例:
gv.mi(function (event) {
    // event 格式:
    {
        kind: 'clickData', // 事件類型
        data: data, // 事件相關的數據元素
        part: "part", // 事件的區域, icon、label 等
        event: e // html 原生事件
    }
})

這裏面 n 就是第一步的那個按鈕,再介紹一下這個方法的相關參數:
flyTo : 相機看向具體的節點或者節點列表,參數 (target, options),其中 options : 可選屬性,格式爲對象({}),屬性包括有:

  • animation : 默認 false,是否啓用動畫,可以設置爲 true 或者 false 或者 animation 動畫對象
  • center : 默認 undefined,新的場景 center 點,形如 [0,0,0](空的話,target 爲一個則看向 node 中心,target 爲列表則看向根據節點列表計算出來的中心)
  • direction : 默認 undefined,眼睛處於目標的方向(相對目標,受到目標自身旋轉影響),例如 [0,1,5] 在目標正面的斜向上
  • worldDirection : 默認 undefined,眼睛處於目標的方向(相對場景,不受目標旋轉影響),例如 [0,1,5] 在目標所在位置的斜向上
  • distance : 默認undefined(未定義的話則使用下面的 ratio 模式計算距離),浮點類型,表示眼睛跟中心的固定距離
  • ratio : 默認 0.8,浮點類型,表示眼睛跟中心的距離動態計算(例如 0.8 表示眼睛在上述方向上動態計算距離以將目標包圍盒的 8 個角全部適配到屏幕 80% 範圍內)

注意,direction跟worldDirection如果都不配置,則使用之前相機的角度保持不變化。

後面全部用到動畫,解釋一下。在 HT 的數據模型驅動圖形組件的設計架構下,動畫可理解爲將某些屬性由起始值逐漸變到目標值的過程, HT 提供了 ht.Default.startAnim 的動畫函數。它支持 Frame-Based 和 Time-Based 兩種方式的動畫,Frame-Based 方式是用戶通過指定 frames 動畫幀數,以及 interval 動畫幀間隔參數控制動畫效果。

我用的是 Time-Based 方式,該方式用戶只需要指定 duration 的動畫週期的毫秒數即可,HT 將在指定的時間週期內完成動畫, 不同於 Frame-Based 方式有明確固定的幀數,即 action 函數被調用多少次,Time-Based 方式幀數或 action 函數被調用次數取決於系統環境, 一般來說系統配置更好的機器,更高效的瀏覽器則調用幀數越多,動畫過程更平滑。由於 js 語言無法精確控制 interval 時間間隔, 採用 Frame-Based 不能精確控制動畫時間週期,即使相同的 frames 和 interval 參數在不同的環境,可能會出現動畫週期差異較大的問題, 因此 HT 默認採用 Time-Based 的方式,如果不設置 duration 和 frames 參數,則 duration 參數將被系統自動設置爲 ht.Default.animDuration 值。action 函數就是實現動畫過程中的屬性變化(變化參數和進度)。

緊接着我們要開始執行第一個動畫—— anim1() 了:

function anim1() {
    ht.Default.startAnim({
        duration: 2000,
        action: function (v, t) {
            // 讓旋鈕旋轉,改變其角度 r3
            n.r3(n.r3()[0] - 0.02, n.r3()[1], n.r3()[2])
        },
        finishFunc: function () { // 動畫結束後調用的函數
            gv.flyTo(a, {
                animation : true,
                direction : [-2000, 1000, 2000],
                distance : 1000
            })
            anim2()
        }
    })
}...

可以看到動畫結束後我們再次用到 flyTo() 向下一個步驟開關去拉近,然後再次執行它的動畫,以此類推,關於一套清晰的操作流程的動畫實現指日可待!

當所有步驟結束後我們應當將鏡頭拉回到最開始時的初始視角,所以我們要注意一點,在最開始的時候提前把位置複製一下:

var oEye = ht.Default.clone(gv.getEye())
var oCenter = ht.Default.clone(gv.getCenter())

這樣,在最後一個 finishiFunc 中我們還原位置:

gv.setEye(oEye)
gv.setCenter(oCenter)

最後,一個簡明的系統操作流程就做好了,想看不懂都難~

總結

HT For Web 提供完整的基於 HTML5 圖形界面組件庫。您可以輕鬆構建現代化的,跨桌面和移動終端的企業應用,無需擔憂跨平臺兼容性,及觸屏手勢交互等棘手問題。也可用於快速創建和部署,高度可定製化,並具有強大交互功能的拓撲圖形及錶盤圖表等應用。HT for Web 非常適用於實時監控系統的界面呈現,廣泛應用於電信網絡拓撲和設備管理,以及電力、燃氣等工業自動化 ( HMI / SCADA ) 領域。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章