基於 HTML5 WebGL 的 3D 棉花加工監控系統
來自專欄前端外刊評論
前言
現在的棉花加工行業還停留在傳統的反應式維護模式當中,當棉花加下廠的設備突然出現故障時,控制程序需要更換。這種情況下,首先需要客戶向設備生產廠家請求派出技術人員進行維護,然後生產廠家才能根據情況再派人到現場進行處理。由於棉花加工設備分布在中國各地乃至出口到世界各地,從客戶反應問題到廠家派人到達現場的時間周期就會很長,少則 一天,個別偏遠的地方可能會需要幾天,不同程度地影響到企業生產活動的繼續進行。傳統的反應式維護存在以下缺點:售後服務響應速度慢;維護成本高;生產效率低下;停車率高;管理成本高;無法應對合格工程師不足的情況。
遠程監控系統主要是通過分布於棉花加工生產線各種設備的感測器、開關信號、視頻監控設備、 PLC 控制器等裝置,通過智能聯網設備集成到互聯網和區域網上面,實現對生產、運營情況的隨時掌握,建立網路範圍內的監控數據和網上知識資源庫,根據現場採集的設備運行數據進行遠程診斷和在線維修。
抓棉機
代碼實現
創建場景
首先是創建一個三維場景(https://hightopo.com/guide/guide/core/3d/ht-3d-guide.html),通過將場景中的元素添加到保存數據的數據容器(https://hightopo.com/guide/guide/core/datamodel/ht-datamodel-guide.html)中即可:
var dm = new ht.DataModel();// 數據容器var g3d = new ht.graph3d.Graph3dView(dm);// 三維組件g3d.addToDOM();// 將三維組件添加到 body 體中
上面代碼中出現的 addToDOM 方法是將調用此方法的組件通過 getView 方法獲取到此組件的底層 div,隨後將此 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);//窗口大小改變事件,調用刷新函數}
整個大環境搭建好了後,我們需要向場景中添加 3D 模型,並進行位置的擺放,這裡採用的是將整個場景的模型以及模型的擺放放在一個 JSON 格式的文件中,然後通過將這個 JSON 文件反序列化到數據容器 DataModel 中,即可呈現 JSON 文件中的場景內容以及模型的擺放位置:
ht.Default.xhrLoad(scenes/抓棉機.json, function(text) {// 載入 JSON 文件 dm.deserialize(text);// 將 JSON 反序列化到數據容器中});
上面出現的 ht.Default.xhrLoad 方法是一個封裝好的非同步載入文件的函數,可以通過這種方法來載入 JSON 文件,因為此方法為非同步載入,所以如果要操作此函數反序列化後的數據容器中的元素,需要在此函數中進行後續的操作。
場景動畫
因為整個場景中的元素都是從此 JSON 文件中反序列化出來的,此 JSON 文件中保存的只有場景的內容,並不包括動畫以及交互,對於不同部分的元素的動畫也不同,我們需要單獨將這些元素取出來,這裡通過 dm.getDataByTag(tag) 方法實現(https://hightopo.com/guide/guide/core/datamodel/ht-datamodel-guide.html#ref_datamodel),此方法通過 tag 唯一標識來獲取節點的信息:
var equipment = dm.getDataByTag(equipment);// 獲取軋棉機的節點var hand = dm.getDataByTag(hand);// 獲取軋棉機「手」節點var light = dm.getDataByTag(light);// 獲取軋棉機頂部的指示燈的節點
獲取到這些需要做動畫的節點後,這裡我用的是動畫插件 setAnimation (https://hightopo.com/guide/guide/plugin/animation/ht-animation-guide.html)方法來做的,動畫插件更進一步對動畫進行封裝,用戶只需用描述性的說明 HT 即可自動實現動畫過程,動畫插件可以將圖元的一個或多個屬性值 (如 width、height、opacity 等)從一個值平滑的緩動至另一個值,同時提供了豐富的緩動方式用於實現各種效果。但是使用這個插件前得先引入 ht-animation.js 文件:
<script src="ht.js"></script> <!--先引入ht.js--><script src="ht-animation.js"></script>
這裡總共有三個部分有動畫,採用的方法大致相同,這裡僅對整個軋棉機的機身的左右移動的動畫進行說明。
equipment.setAnimation({// 動畫調用方法 moveDown: {// 定義了一個名為 moveDown 的動畫過程,這個動畫過程改變圖元的 x 軸坐標,將其從 623 變化至 -256 from: 623,// 動畫開始時的屬性值 to: -256,// 動畫結束時的屬性值 interval: equipInterval,// 動畫間隔,單位ms next: ["moveUp"],// 字元串類型,指定當前動畫完成之後,要執行的下個動畫,可將多個動畫融合 onUpdate: function(value) {// 回調函數,動畫的每一幀都會回調此函數 this.setX(value);// 設置該節點的 x 軸的值為當前動畫 from 到 to 的值 formPane.getItemById(xValue).element = value.toFixed(2);// 獲取 form 表單上的 xValue 元素,同時改變此值 formPane.iv();// 表單內容變化後要通知表單進行刷新變化 } }, moveUp: {// 定義了一個名為 moveUp 的動畫過程,這個動畫過程改變圖元的 x 軸坐標,將其從 -256 變化至 623 from: -256, to: 623, interval: equipInterval, next: ["moveDown"], onUpdate: function(value) { this.setX(value); formPane.getItemById(xValue).element = value.toFixed(2); formPane.iv(); } }, start: [moveDown]// start是一個數組,用於指定要啟動的一個或多個動畫});
注意,要使用動畫,首先您需要調用 ht.DataModel#enableAnimation(interval) 啟動全局動畫定時器,默認 interval 為 16ms,如果不設置此參數值,則 DataModel 定時器每隔 16ms 左右就會遍歷自己所有的 Data,根據 Data 的 animation 配置執行動畫。
dm.enableAnimation();
表單創建
前面代碼中出現的 form 表單(https://hightopo.com/guide/guide/plugin/form/ht-form-guide.html),是通過 createForm 方法創建的,此方法定義如下(PS:由於 form 表單的列表稍長,這裡就選取幾個比較有代表性的表單元素進行說明):
// 創建 form 表單function createForm() { var fp = new ht.widget.FormPane();// 創建表單組件實例 fp.setWidth(200);// 設置表單組件的寬度 fp.setHeight(250);// 設置表單組件的高度 fp.getView().style.top = 8%;// 設置表單組件的樣式 fp.getView().style.right = 0; fp.getView().style.background = rgba(255, 255, 255, 0.3); document.body.appendChild(fp.getView());// 將表單組件的底層 div 添加到 body 體中 var equipment = dm.getDataByTag(equipment);// 通過 tag 唯一標識獲取元素 var hand = dm.getDataByTag(hand); fp.setLabelAlign(right);// 設置表單的文本對齊方式 fp.addRow([// 向表單中添加一行 此方法的參數一為元素數組,可在一行中添加多個元素 {// 元素一 顯示文本內容為 「機器號」 element: 機器號, color: #fff }, {// 元素二 顯示文本內容為 「Machine」 element: Machine, color: #fff } ], [0.1, 0.1]);// 參數二為每個元素寬度信息數組,寬度值大於1代表固定絕對值,小於等於1代表相對值,也可為80+0.3的組合 fp.addRow([// 向表單中添加一行 此方法的參數為一個數組,可在一行中添加多個元素 {// 元素一 顯示文本內容為 「機器號」 element: 抓棉機動畫, color: #fff }, {// 元素二 顯示文本內容為 一個按鈕元素 button: { label: 開始, onClicked: function() {// 按鈕點擊觸發事件,啟動軋棉機左右移動動畫 dm.enableAnimation();// 啟動全局動畫定時器 var light = dm.getDataByTag(light); if (!light) return; lightColor(light); colorTimer = setInterval(function() { lightColor(light); }, 1000); } } }, { button: { label: 停止, onClicked: function() {// 按鈕點擊觸發事件,關閉軋棉機左右移動動畫 dm.disableAnimation();// 停止全局動畫定時器 clearInterval(colorTimer); var light = dm.getDataByTag(light); if (light) light.s(all.color, rgba(0,0,255,0.51)); } } } ], [0.2, 0.1, 0.1]);// 將一行中的三個元素都分配寬度進行顯示 fp.addRow([// 向表單中添加一行 此方法的參數為一個數組,可在一行中添加多個元素 {// 元素一 顯示文本內容為「小車行走速度」 element: 小車行走速度, color: #fff }, {// 元素二 顯示文本內容為一個 滑動條 id: slide, slider: {// 滑動條 value: equipInterval,// slider 當前值 min: 0,// slider 最小值 max: 300,// slider 最大值 step: 20,// slider 每移動一步的值 onValueChanged: function() {// slider 值變化觸發事件 var anim = dm.getDataAnimation(equipment);// 獲得參數data的動畫配置 anim.moveDown.interval = this.getValue();// 設置 moveDown 動畫的 interval 內容 anim.moveUp.interval = this.getValue();// 設置 moveUp 動畫的 interval 內容 } } } ], [0.1, 0.1]); fp.addRow([// 向表單中添加一行 此方法的參數為一個數組,可在一行中添加多個元素 { element: X 軸, color: #fff }, { id: xValue, element: equipment.getX().toFixed(2),// 獲取軋棉機當前 x 軸值 color: #fff } ], [0.1, 0.1]); return fp;}
有趣的部分就說這麼多,如果有什麼建議和意見,歡迎留言或者私信~或者上官網查閱相關資料:
Everything you need to create cutting-edge 2D and 3D visualization
總結
以前對 animation 動畫用的比較少,這次也是特地用它仔細研究一下 animation 的機制,就 animation 設置的動畫能夠以一種「平和」的方式進行值的變化,動畫也看起來比較有條理一些;當然還有能夠設置下一次動畫需要做什麼的操作,這個設計也非常的人性化;同時還能通過 getDataAnimation 獲取對象的動畫配置,並通過此方法對對象的動畫進行重新配置,這些優點都是值得拿出來跟大家分享的。
推薦閱讀:
※原畫和3d的選擇?
※問題:為什麼我覺得網上的3D圖片都不是3D的?「裸眼3D」存在嗎?PPTV發布裸眼3D手機是不是噱頭?
※C4D為什麼突然火了?小白如何快速的入門?
※自己做的C4D效果圖看起來不夠逼真,問題在哪裡?
※為什麼現在3D遊戲風格、顏色都是偏黑暗,沒有色彩明朗、鮮艷的呢?