以真實設備爲模型,搭建出設備面板,並實時獲取設備運行參數,顯示在設備面板上,這相比於純數值的設備監控系統顯得更加生動直觀。今天咱們就在HT for Web的3D技術上完成設備面板的搭建。
咱們今天模擬的設備是機房設備,先來目擊下最終效果:html
我來解釋下這個模型,一個帶有透明玻璃門的機櫃,機櫃裏裝有5臺設備,門能夠開合,設備能夠插拔,那麼我麼該如何搭建這樣的設備呢?方法不難,咱們一步一步來。
咱們先從設備開始,設備的示意圖以下:node
看起來有模有樣的,其實呢,它就是一個長方體,而後在長方體的正面貼上一張圖片,這樣子設備的殼就出來了,建立代碼以下:函數
var node = createNode([0, 0, 0], [475, 100, 0]); node.s({ 'front.image': 'panel', 'all.color': '#E6DEEC' }); node.setToolTip('Double click to pop the server’);
其中設置設備的正面圖片的方法是經過設置節點的front.image樣式屬性來實現的,在代碼中將front.image屬性設置爲’panel’,而’panel’屬性是已經經過ht.Default.setImage()方法註冊了的圖片的別名,在代碼中還設置了長方體各個面的顏色和鼠標懸停時的提示語。
在代碼中還調用了createNode()的方法,該方法並無作什麼特殊的操做,只是將建立3D拓撲節點的代碼封裝起來,精簡代碼,避免相同的代碼重複書寫,具體的封裝以下:spa
/** * 建立3D拓撲節點,並添加到dataModel中 * @param p3 {array} 位置信息 * @param s3 {array} 長寬高信息 * @returns {ht.Node} 3D拓撲節點 */ function createNode(p3, s3) { var node = new ht.Node(); node.s({ 'shape' : 'rect' }); node.p3(p3); node.s3(s3); dataModel.add(node); return node; }
該方法經過傳入位置信息和大小信息建立出一個3D拓撲節點,並添加到dataModel中,最後返回該節點對象。
剛剛咱們只是建立了設備的外殼而已,在設備上又部分端口是被被佔用的,因此接下來咱們要作的就是填充設備端口,仔細看了下設備的端口形狀,發現形狀是不規則的呢,那麼設備端口該如何填充呢?咱們只須要找一個和端口形狀同樣的圖片貼在長方體的正面,而後套在設備上就能夠了,具體的實現以下:設計
/** * 建立端口節點,並吸附到指定的節點上 * @param indexes {array} 端口位置信息 * @param host {ht.Node} 被吸附的節點對象 */ function createPort(indexes, host) { var p3 = host.p3(), s3 = host.s3(), x = -s3[0] / 2, y = 100 / 2 + p3[1], z = 1 + s3[2] / 2; indexes.forEach(function(index) { var port = new ht.Node(); port.setName('Port'); port.s({ 'front.image' : 'port_green', 'all.light' : true }); port.setHost(host); port.setSize3d(28, 23, 10); if (index % 2 === 0) { var col = (index - 2) / 2; port.setPosition3d(x + 67.5 + col * 32, y - 60.5, z); port.s({ 'front.uv' : [0, 1, 0, 0, 1, 0, 1, 1] }); } else { var col = (index - 1) / 2; port.setPosition3d(x + 67.5 + col * 32, y - 26.5, z); } dataModel.add(port); }); }
在設備上總共有20個端口,咱們經過傳入的端口位置信息來肯定建立出來的節點位置,仔細觀察設備端口能夠發現,第二排的端口和第一排的端口方向不同,因此在建立第二排的端口時須要經過設置front.uv屬性來控制貼圖的翻轉,固然貼圖也是咱們事先註冊好了的。
好了,到這裏咱們的設備模型就構建出來了,那麼接下來就是建立機櫃了,機櫃的建立就和設備外殼的建立基本類似,不同的地方在於,機櫃有一個門,這個門有開合的功能,因爲拓撲節點沒法單獨對節點的某一面分離出來作旋轉操做,因此門必須是一個單獨的拓撲節點,咱們先來看看機櫃的效果圖:3d
效果圖種,咱們把門稍微裝飾了一下,在門的邊緣上加上了藍色的貼邊,讓門看起來更有質感,效果圖和思路都有了,代碼天然而然就出來了,瞧瞧下面的代碼,有一點點小複雜哦。code
var h = 1000, w = 477, d = 400, k = 20; // 建立機櫃 var main = createNode([0, 0, 0], [w, h, d]); main.s({ 'all.color' : '#403F46', 'front.visible' : false }); // 建立門 var face = new ht.Shape(), s = {'all.visible' : false, 'front.visible' : true}; dataModel.add(face); face.addPoint({x : -w / 2, y : d / 2 - 1}); face.addPoint({x : w / 2, y : d / 2 - 1}); face.addPoint({x : w + w / 2, y : d / 2 - 1}); face.setSegments([1, 2, 1]); face.setTall(h); face.setThickness(1); face.s(s); face.setHost(main); face.s({ 'all.color' : 'rgba(0, 40, 60, 0.7)', 'all.reverse.flip' : true, 'all.transparent' : true, '3d.movable' : false }); face.face = true; face.open = false; face.angle = -Math.PI * 0.6; face.setToolTip('Double click to open the door'); // 建立門的貼邊 var up = createNode([0, h / 2 - k / 2, d / 2], [w, k, 1], false, false).s(s), down = createNode([0, -h / 2 + k / 2, d / 2], [w, k, 1], false, false).s(s), right = createNode([w / 2 - k / 2, 0, d / 2], [k, h, 1], false, false).s(s), left = createNode([-w / 2 + k / 2, 0, d / 2], [k, h, 1], false, false).s(s); up.setHost(face); down.setHost(face); left.setHost(face); right.setHost(face);
代碼的邏輯是這樣的,先建立一個長方體做爲機櫃的外殼,而後將長方體的正面設置爲隱藏,而後建立一個多邊形做爲門,將門設爲淺藍色半透明,最後建立4個藍色長方體貼到門的邊緣做爲裝飾,如此一個機櫃就搭建完成了。
設備模型有了,機櫃有了,接下來的工做就是將二者合併起來,方法很簡單,就是建立設備並將設備吸附到機櫃上,具體的代碼以下:視頻
var num = 5, start = 400; for (var i = 0; i < num; i++) { var y = start - 170 * i, z = (d - 30) / 2; var node = createNode([0, y, 0], [w - 2, 100, d - 30]); node.s({ 'front.image' : 'panel', 'all.color' : '#E6DEEC', 'all.reverse.cull' : true, '3d.movable' : false }); node.pop = false; node.setToolTip('Double click to pop the server'); node.setHost(main); createPort([1, 2, 3, 4, 5, 6, 7, 13, 16, 17, 20], node); }
還記得前面構建設備模型和機櫃門的代碼中,咱們對這兩個模型添加了鼠標懸停時的提示內容,雙擊能夠打開門,雙擊能夠抽出設備,那麼咱們如今就來實現這兩個效果,首先咱們來分析下具體的實現方案:
雙擊的事件要添加在哪裏呢?對每一個拓撲節點作監聽嗎?這是最直接的方法,可是這樣作的話,有多少節點將會有多少個對應的處理函數,並且同一類型的處理函數又是同樣的,那麼這就會致使系統資源的浪費,因此對每一個節點作雙擊的監聽方案是不可取的,那麼咱們該如何處理雙擊事件呢?咱們能夠換個角度思考,全部的節點都是添加到3D拓撲組件上的,那麼咱們是否能夠經過監聽3D拓撲組件的雙擊事件,而後經過事件對象獲取到對應的節點,而後經過判斷節點上設置的自定義標識屬性來作相應的處理,具體的代碼以下:server
// 監聽3D拓撲組件的dataDoubleClicked事件 g3d.onDataDoubleClicked = function(data, e, dataInfo) { // 若果節點爲門 if (data.face) { // 遍歷全部吸附在機櫃下的節點 data.getHost().getAttaches().each(function(attach) { // 若是節點狀態爲彈出,則調用函數還原節點位置 if (attach.pop) { toggleData(attach); } }); } // 若是節點爲端口節點,則觸發其所吸附設備的雙擊事件 else if (data.a('port')) { toggleData(data.getHost()); return; } toggleData(data); };
閱讀上面的代碼,你們會發現實現的方案和咱們提到的方案不太同樣,咱們經過監聽3D拓撲組件的dataDoubleClicked事件就能夠直接獲取到被雙擊的節點對象,而無需咱們本身經過事件對象獲取對應的節點對象,固然就監聽dataDoubleClicked事件了。
在代碼中,咱們調用了toggleData()方法來處理雙擊事件,具體的處理代碼以下:htm
/** * 節點雙擊處理函數 * @param data {ht.Node} 被雙擊的節點 */ function toggleData(data) { var angle = data.angle, pop = data.pop; // 當前雙擊的對象爲門 if (angle != null) { if (anim) { anim.stop(true); } var oldAngle = data.getRotation(); if (data.open) { angle = -angle; } data.open = !data.open; anim = ht.Default.startAnim({ action : function(t) { data.setRotation(oldAngle + t * angle); } }); } // 當前雙擊的對象爲設備 else if (pop != null) { if (anim) { anim.stop(true); } var p3 = data.p3(), s3 = data.s3(), dist = (pop ? -s3[2] : s3[2]) / 2; data.pop = !data.pop; anim = ht.Default.startAnim({ action : function(t) { data.p3(p3[0], p3[1], p3[2] + t * dist); } }); } }
在代碼中,根據節點預設的不一樣的屬性值來確認該作什麼處理,若是節點對象是門的話,則經過ht.Default.startAnim()方法緩慢的修改門的rotation;若是節點對象是設備的話,則緩慢修改設備的position。
到這裏,今天的Demo的全部表現和功能就完成了,今天的內容中有設計到節點的style應用,我沒有作深刻的講解,後續會給你們一一介紹,感興趣的朋友能夠經過HT for Web的樣式手冊來了解更多的內容。
下面附上今天的Demo源碼及視頻。
我已經把今天的Demo上傳至官網了,感興趣的朋友能夠點擊這裏訪問。
http://v.youku.com/v_show/id_XMTMwNTY2ODE0NA==.html