随着近几年物联网、万物互联等诸多概念的大行其道,智慧城市的概念也早已经被人们耳熟能详,而做为城市的组成部分,智慧建筑也是重中之重,智慧园区,智慧小区等也如雨后春笋般的相继出现。javascript
智慧建筑是指经过将建筑物的结构、系统、服务和管理根据用户的需求进行最优化组合,从而为用户提供一个高效、温馨、便利的人性化建筑环境,智慧建筑毫不仅仅只是智慧园区、智慧小区这种模式,这里我就经过 HT for Web 制做了一个以会展中心为主体的智慧建筑监控系统。html
经过上面的效果预览,能够分辨出整个监控系统是分为 3 个层次的,分别是主体、楼内、展厅,若是是使用单个 graph3dView
加载全部场景,经过 dm.clear()
清除场景,dm.deserieialize()
加载新场景这种切换方式必然会有一个极短的渲染时间,使切换时不连贯,因此我这里就使用了 3 个 graph3dView
,去呈现各自的层级模型,经过 notifier
事件通知器监听场景切换,代码以下:java
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
是自行封装的一个方法node
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
等模型相关资源是不会请求和渲染的,这样对性能是十分友好的,可是当我第一次切换场景时,仍是会有短暂的请求和渲染时间,因此这里我须要对资源进行预加载。web
这里我经过在 body
中添加一个不在窗口展现的与窗口等宽高的 div
元素,经过把当前不展现的 graph3dView
放到其中触发对相应 obj
等模型资源的请求和渲染,完成预加载,代码以下:数组
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);
web 页面加载是依赖网速的,会展中心模型 obj
等资源文件是有必定大小的,可能对于不一样带宽网速的用户所须要加载的时间也不尽相同,这里就须要判断下 obj
是否所有加载完成,加载完成后再执行动画效果,经过 ht.Default.handleModelLoaded
监控是否全部模型都请求加载完成, 加载完成后开始执行动画,顺便释放以前预加载的 graph3dView
,代码以下:app
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); } };
我想要场景第一次加载时,视角拉近后左右两边的面板再一点一点的加载出来,动画效果是不彻底线性顺序的去执行,因此我这里经过 ht.Default.startAnim
方法封装了一套经过参数数组进行的动画的方法,代码以下:性能
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(); }, }); }
参数格式以下:优化
// 视角移动 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
方式筛选须要鼠标高亮的图元,代码以下:动画
g3d.setHighlightMode('mouseover'); g3d.getHighlightHelper().setFetchTargetFunc(function (nodes) { let sortList = new ht.List(nodes); return sortList.toArray(node => { return jumpList.contains(node); }); });
由于总体的楼层比较大,而每一个楼层中可选择的展区又比较小,因此这里我作了一个视角调整,可使用单独移动视角到正视相应楼层的视角 flyTo
,这里除了采用右侧边栏选中移动,也作了鼠标移入相应楼层右键改变视角的处理,使用了新建的类 messageView
作交互提示。
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/demo...