基於 HTML WebGL 的會展中心智慧監控系統
阿新 • • 發佈:2020-04-27
## **前言**
隨著近幾年物聯網、萬物互聯等諸多概念的大行其道,智慧城市的概念也早已經被人們耳熟能詳,而作為城市的組成部分,**智慧建築**也是重中之重,智慧園區,智慧小區等也如雨後春筍般的相繼出現。
智慧建築是指通過將建築物的結構、系統、服務和管理根據使用者的需求進行最優化組合,從而為使用者提供一個高效、舒適、便利的人性化建築環境,智慧建築絕不僅僅只是智慧園區、智慧小區這種模式,這裡我就通過 **HT for Web** 製作了一個以**會展中心**為主體的智慧建築監控系統。
## **效果預覽**
![](https://img2020.cnblogs.com/blog/1496396/202004/1496396-20200421150139363-345431036.png)
![](https://img2020.cnblogs.com/blog/1496396/202004/1496396-20200421150159057-980567122.png)
![](https://img2020.cnblogs.com/blog/1496396/202004/1496396-20200421150215967-1063111252.png)
## **程式碼實現**
### **場景呈現**
通過上面的效果預覽,可以分辨出整個監控系統是分為 3 個層次的,分別是主體、樓內、展廳,如果是使用單個 `graph3dView` 載入所有場景,通過 `dm.clear()` 清除場景,`dm.deserieialize()` 載入新場景這種切換方式必然會有一個極短的渲染時間,使切換時不連貫,所以我這裡就使用了 3 個 `graph3dView` ,去呈現各自的層級模型,通過 `notifier` 事件通知器監聽場景切換,程式碼如下:
``` javascript
notifier.add((event) => {
if (event.kind === 'sceneChange') {
const oldSceneKey = event.oldScene,
newSceneKey = event.newScene,
oldScene = G[oldSceneKey],
newScene = G[newSceneKey];
oldScene.removeFromDOM();
newScene.addToDOM();
if (newScene.graph2d.isAnimed) {
newScene.graph3d.animByList();
} else {
newScene.graph3d.animByList(newScene.graph2d.animByList, newScene.graph2d);
}
}
});
```
其中 `removeFromDOM` 是自行封裝的一個方法
``` javascript
removeFromDOM() {
const g3d = this.g3d,
view = g3d.getView();
if (view.remove) {
view.remove()
} else {
view.parentNode.removeChild(view)
}
this.notifier.fire({
kind: 'reset',
});
}
```
但是這樣還是有一個問題,`graph3dView` 預設如果不放到頁面中,場景中的 `obj` 等模型相關資源是不會請求和渲染的,這樣對效能是十分友好的,但是當我第一次切換場景時,還是會有短暫的請求和渲染時間,所以這裡我需要對資源進行預載入。
### **資源預載入**
這裡我通過在 `body` 中新增一個不在視窗展示的與視窗等寬高的 `div` 元素,通過把當前不展示的 `graph3dView` 放到其中觸發對相應 `obj` 等模型資源的請求和渲染,完成預載入,程式碼如下:
``` javascript
const preloadDiv = document.createElement('div');
preloadDiv.style.position = 'absolute';
preloadDiv.style.bottom = '100%';
preloadDiv.style.width = '100%';
preloadDiv.style.height = '100%';
document.body.appendChild(preloadDiv);
scene2.addToDOM(preloadDiv);
scene3.addToDOM(preloadDiv);
```
### **模型載入完成後再執行動畫**
![](https://img2020.cnblogs.com/blog/1496396/202004/1496396-20200421150254770-784559626.gif)
web 頁面載入是依賴網速的,會展中心模型 `obj` 等資原始檔是有一定大小的,可能對於不同頻寬網速的使用者所需要載入的時間也不盡相同,這裡就需要判斷下 `obj` 是否全部載入完成,載入完成後再執行動畫效果,通過 `ht.Default.handleModelLoaded` 監控是否所有**模型**都請求載入完成, 載入完成後開始執行動畫,順便釋放之前預載入的 `graph3dView` ,程式碼如下:
``` javascript
let modelSize = 0;
ht.Default.handleModelLoaded = (name, model) => {
modelSize++;
if (modelSize === 62) {
scene1.graph3d.enableShadow();
scene3.graph3d.enableShadow();
scene2.removeFromDOM();
scene3.removeFromDOM();
scene1.graph3d.animByList(scene1.graph2d.animByList, scene1.graph2d);
}
};
```
### **動畫依引數順序執行**
![](https://img2020.cnblogs.com/blog/1496396/202004/1496396-20200421150417374-439953310.gif)
![](https://img2020.cnblogs.com/blog/1496396/202004/1496396-20200421150431395-162765189.gif)
![](https://img2020.cnblogs.com/blog/1496396/202004/1496396-20200421150447867-784271161.gif)
我想要場景第一次載入時,視角拉近後左右兩邊的面板再一點一點的加載出來,動畫效果是不完全線性順序的去執行,所以我這裡通過 `ht.Default.startAnim` 方法封裝了一套通過引數陣列進行的動畫的方法,程式碼如下:
``` javascript
animByList(callback, obj) {
this.isAnimed = true;
const animList = this.animList,
self = this;
let callAnim = (ind) => {
const param = animList.get(ind);
param && self.anim(param, () => {
callAnim(ind + 1);
const lastParam = animList.get(ind + 1);
lastParam || callback && callback.call(obj || this);
});
};
callAnim(0);
}
anim(animParam, callback) {
const self = this,
time = animParam['time'] || 1000,
easing = animParam['easing'] || function (t) {
return t * t;
},
func = animParam['func'];
this.__animObj = ht.Default.startAnim({
duration: time || 1000,
easing: easing,
action: function (v, t) {
const V = v,
T = t;
function animFunc(param) {
let v = V,
t = T;
if (param instanceof Function) {
param(v, t);
} else {
const type = param['type'],
object = param['object'],
objectTag = param['objectTag'],
key = param['key'],
oldValue = param['oldValue'],
newValue = param['newValue'],
oneTime = param['time'],
scope = param['scope'];
if (scope) {
v = v < scope[0] ? 0 : v > scope[1] ? 1 : (v - scope[0]) / (scope[1] - scope[0]);
} else {
v = !oneTime || oneTime > time ? v : v * time / oneTime < 1 ? v * time / oneTime : 1;
}
let obj, value;
obj = object ? object : objectTag ? self.view.dm().getDataByTag(objectTag) : undefined;
if (!obj) return;
if (!isSameType(oldValue, newValue) || !isNumORNumArray(oldValue)) return;
if (oldValue instanceof Array) {
if (oldValue.length !== newValue.length) return;
const darr = newValue.map((n, i) => {
return n - oldValue[i];
});
value = oldValue.map((n, i) => {
return n + darr[i] * v;
});
} else {
const d = newValue - oldValue;
value = oldValue + d * v;
}
ht.Default.setPropertyValue(obj, type, key, value);
}
}
if (animParam instanceof Array) {
animParam.forEach(ele => {
animFunc(ele);
});
} else {
animFunc(animParam);
}
},
finishFunc: function () {
func && func(func);
callback && callback();
},
});
}
```
引數格式如下:
``` javascript
// 視角移動
param = {
object: g3d,
type: undefined,
key: 'eye',
oldValue: [-118, 5130, 15858],
newValue: [-26, 1130, 3494],
time: 1000,
}
animList.add();
// 標題從左到右出現
param = {
object: title,
type: 'style',
key: 'clip.percentage',
oldValue: 0,
newValue: 1,
time: 1500,
};
animList.add(param);
```
### **可點選部分高亮效果**
為了突出可以點選的部分,我加了高亮效果,設定滑鼠懸浮高亮模式,並通過 `g3d.getHighlightHelper().setFetchTargetFunc` 方式篩選需要滑鼠高亮的圖元,程式碼如下:
![](https://img2020.cnblogs.com/blog/1496396/202004/1496396-20200421150619483-539777579.png)
``` javascript
g3d.setHighlightMode('mouseover');
g3d.getHighlightHelper().setFetchTargetFunc(function (nodes) {
let sortList = new ht.List(nodes);
return sortList.toArray(node => {
return jumpList.contains(node);
});
});
```
### **樓層視角跳轉**
因為整體的樓層比較大,而每個樓層中可選擇的展區又比較小,所以這裡我做了一個視角調整,可以使用單獨移動視角到正視相應樓層的視角 `flyTo`,這裡除了採用右側邊欄選中移動,也做了滑鼠移入相應樓層右鍵改變視角的處理,使用了新建的類 `messageView` 做互動提示。
![](https://img2020.cnblogs.com/blog/1496396/202004/1496396-20200421150637570-607035870.gif)
``` javascript
g3d.flyTo(floor, {
animation: true,
direction: [0, 1, 2],
center: floor.p3().map((n, i) => {
return i !==1 ? n : n + floor.getTall() / 2;
}),
distance: distances[newFloor - 1],
});
```
## **總結**
隨著科技的井噴式發展,智慧建築將如雨後春筍般崛起,其應用的場景也會不斷拓展,應運而生的資料視覺化管理系統也應該配套升級,為其把數字資訊變為直觀的、以圖形影象資訊表示的資訊,清晰的展現在客戶的面前,這將是無可阻擋的時代大趨勢。
還有更多的視覺化案例可以參考:https://www.hightopo.com/demos/in