在前端中,視圖層和數據層須要進行單向或者雙向數據綁定,你們都已經不陌生了,有時候 2D 作的比較順了以後,就會想要挑戰一下 3D,否則總以爲癢癢的。這個 3D 機架的 Demo 我以爲很是有表明性,首先,3D 機架用途很是廣,尤爲是在電信行業,就算不是機架,在好比工業方面 3D 模型以及數據綁定的應用也是很是普遍的,畢竟如今工業物聯網已是大趨勢了。前端
上面動圖中,閃爍燈是在不斷變化的,因爲須要顯示的效果美觀一點,也實際一點,我截的圖仍是比較完整的,可是這個閃爍的部分有點看不清楚(cnblog 中放太明顯的外鏈容易被移出首頁啊!!!等會再發!)。node
雖然上面 gif 圖中顯示的一個是 2D 的一個是 3D 的,可是構建的步驟以及須要的內容是同樣的,因此本文只針對 3D 的模型進行代碼實現。json
搭建一個 3D 場景是很是快速的,只須要三行代碼:數組
dm = new ht.DataModel();//建立一個數據容器 數據容器也能夠經過 g3d.getDataModel() 獲取 g3d = new ht.graph3d.Graph3dView(dm);//建立一個 3D 場景,將數據容器做爲參數傳遞進去,這樣數據容器中的內容就能夠顯示在 3D 場景中了 g3d.addToDOM();//將 3D 場景添加到 body 體中
雖然能夠叫設計師直接給我一個 obj 格式的模型,可是我以爲這個比較簡答,仍是不要麻煩人家了。。。首先是建立一個六面體,模型上面的貼圖是我之前用的一個 json 格式的文件,用來做爲這個六面體的正面貼圖,這些部分都是寫在 json 文件裏面的,我先截取一小部分的 json 內容,而後用 js 代碼復現:dom
{ "c": "ht.Node", //一個 ht.Node 類型的元素 "i": 1277,//id "p": {//經過 set/get 來設置/獲取的元素的部分。如 setPosition/getPosition "tag": "service",//設置元素標籤 用來做爲惟一標識 "image": "symbols/機櫃.json",//設置節點圖片 "rotationX": 1.5708,//設置節點 X 軸旋轉角度 "position": {//設置節點位置 "x": 0, "y": 225 }, "anchor": {//設置節點錨點 "x": 0.5, "y": 0.54 }, "anchorElevation": 1, //設置節點 y 軸錨點 "width": 507,//設置節點寬度 "height": 980, //設置節點長度 "tall": 450, //設置節點高度 "elevation": 451 //控制Node圖元中心位置所在3D座標系的y軸位置 }, "s": {//設置圖元的 style 樣式,HT 預約義的一些樣式屬性,經過 node.s('all.color') 獲取和設置節點的樣式 "all.color": "#DDDDDD", //設置節點六面顏色 "top.image": "symbols/機櫃.json",//設置節點頂部圖片 "front.visible": true, //設置節點正面是否可見 "back.visible": true, "left.visible": true, "right.visible": true, "bottom.visible": true } }
這部分的 json 內容大致上就是建立了一個 ht.Node 節點,而後對這個節點設置了一些屬性,包括節點座標,節點的大小,以及一些 style 樣式設置。異步
那麼,如何用代碼來建立這樣一個節點呢?函數
var node = new ht.Node();//建立一個 ht.Node 類型的節點 node.setTag('service'); //設置節點的標籤 node.setImage('symbols/機櫃.json'); //設置節點圖片 node.setRotationX(Math.PI/2);//設置節點x軸旋轉 node.setPosition(0, 225);//設置節點位置 node.setAnchor(0.5, 0.54);//設置節點錨點 node.setAnchorElevation(1);//設置節點y軸方向的錨點 node.setWidth(507);//設置節點的寬度 node.setHeight(980);//設置節點的長度 node.Tall(450);//設置節點的高度 node.setElevation(451);//控制Node圖元中心位置所在3D座標系的y軸位置 node.s({ //設置節點樣式 'all.color': '#ddd', //六面顏色 'top.image': 'symbols/機櫃.json', //節點頂部圖片 'front.visible': true,//設置節點正面可見 'back.visible': true, 'left.visible': true, 'right.visible': true, 'bottom.visible': true });
其實整個 json 就是由多個這種類型的圖元組合而成的。咱們來拆析一下,整個 3D 機架其實是由十個圖元組合而成的,第一個是總體的 3D 機櫃(也就是咱們上面 json 內容中建立的部分),剩下的九個都是須要動態變化閃爍燈的設備,也就是我紅框框起來的部分:spa
http://www.hightopo.com/demo/...
這些設備的建立方式跟上面的 3D 機架是相似的,只不過對應的節點尺寸小點,貼圖不同,座標不同罷了。可是下面的這九個節點的貼圖彷佛有點不同?上面有閃爍的燈,而且不止一盞!怎麼動態獲取他們呢?設計
不得不說到矢量這個概念。矢量在 HT for Web 中是矢量圖形的簡稱,常見的 png 和 jpg 這類的柵格位圖, 經過存儲每一個像素的顏色信息來描述圖形,這種方式的圖片在拉伸放大或縮小時會出現圖形模糊,線條變粗出現鋸齒等問題。 而矢量圖片經過點、線和多邊形來描述圖形,所以在無限放大和縮小圖片的狀況下依然能保持一致的精確度。3d
這些有點都是次要的,最重要的是這個矢量能夠進行數據綁定(這個數據綁定是綁定到節點中的),並且綁定方式很是容易!
矢量採用 json 格式描述,使用方式和普通的柵格位圖一致,經過 node.setImage('hightopo.json') 或者 node.setIcon('hightopo.json') 等設置到數據模型中。
矢量 json 描述必需包含 width、height 和 comps 參數信息:
因爲這張圖繪製的仍是比較複雜的,因此我就將設置了數據綁定的矩形部分的矢量繪製代碼粘貼出來:
{ "width": 48, //一個矢量圖標必備的寬度 矢量詳細內容請參考 HT for Web 矢量手冊 "height": 262,//一個矢量圖標必備的高度 "comps": [//一個矢量圖標必備的 Array 數組組件 {//數組組件中的第一個元素 "type": "rect",//類型爲矩形 "background": {//設置矩形背景色 "func": "attr@rectBg1", //HT 用一個帶func屬性的對象替換之前的參數值 這裏就是進行數據綁定的地方 "value": "rgb(255,0,0)"//若是 func 值爲 undefined 或者 null 時,採用這個值 }, "shadow": true,//設置「陰影」 "shadowColor": { //「陰影」顏色 "func": "attr@shadowColor1", // 這邊將「陰影」也進行了數據綁定,爲的是可以實現燈「發光」的效果 "value": "rgba(255,0,0,0.35)"//設置備選值 }, "shadowOffsetX": 0, //設置陰影橫軸偏移量 "shadowOffsetY": 0,//設置陰影縱軸偏移量 "rect": [//設置該組件的寬高以及座標 4.38544,//x 軸座標 23.52679,//y 軸座標 14.46481, //width 組件寬度 6.1554//height 組件高度 ] } ] }
由於一個矢量圖形中有 5 個「閃爍燈」,因此我添加了 5 個組件,也就是在 comps 參數裏面添加了五個元素,綁定的數據不一樣,爲了省事,我將綁定的數據名都設置爲「rectBg」後面加一個數字,這些數字依次遞增。
咱們就是將這樣一張矢量圖設置爲節點的 front.image 做爲節點正面顯示圖的:node.s('front.image', 'symbols/內部設備2.json')。
會不會有人好奇 json 文件裏面的內容是如何轉換成 3D 模型的?
說實在的,步驟依然很簡單:
ht.Default.xhrLoad('scene/service3d.json', function(text) {//xhrLoad 函數是一個異步加載文件的函數 dm.deserialize(text);//反序列化數據容器,解析用於生成對應的Data對象並添加到數據容器 這裏至關於把 json 文件中生成的 ht.Node 節點反序列化到數據容器中,這樣數據容器中就有這個節點了 });
因爲 xhrLoad 函數是一個異步加載函數,因此若是 dm 數據容器反序列化未完成就直接調用了其中的節點,那麼會形成數據獲取不到的結果,因此通常來講我是將一些邏輯代碼寫在這個函數內部,或者給邏輯代碼設置 timeout 錯開時間差。
首先,因爲數據都是存儲在 dm 數據容器中的(經過 dm.add(node) 添加的),因此咱們要獲取數據除了能夠經過 id、tag 等獨立的方式,還能夠經過遍歷數據容器來獲取多個元素:
var infos = [ { shadowColor: 'shadowColor1', background: 'rectBg1' }, { shadowColor: 'shadowColor2', background: 'rectBg2' }, { shadowColor: 'shadowColor3', background: 'rectBg3' }, { shadowColor: 'shadowColor4', background: 'rectBg4' }, { shadowColor: 'shadowColor5', background: 'rectBg5' } ]; var len = infos.length; //獲取數組中的長度 var datas = dm.toDatas(function(d) { return d.getDisplayName() === 'device'; });//以過濾函數構建新的元素集合並返回(ht.List 數組類型) setInterval(function() { datas.forEach(function(data) { var info = infos[Math.floor(Math.random() * len)]; var shadowName = info.shadowColor; var bgName = info.background; if (data.a(shadowName) === 'rgba(255, 0, 0, 0.35)') {//若節點業務屬性「陰影」顏色爲紅色,則設置爲綠色 data.a(shadowName, 'rgba(0, 255, 0, 0.35)'); } else {//若節點業務屬性「陰影」顏色爲綠色,則設置爲紅色 data.a(shadowName, 'rgba(255, 0, 0, 0.35)'); } if (data.a(bgName) === 'rgb(255, 0, 0)') {//若節點業務屬性背景顏色爲紅色,則設置爲綠色 data.a(bgName, 'rgb(0, 255, 0)'); } else {//若節點業務屬性背景顏色爲綠色,則設置爲紅色 data.a(bgName, 'rgb(255, 0, 0)'); } }); }, 1000);
總體來講這個 Demo 仍是比較有價值的,一是快速實現了 3D 機櫃模型,二是對模型上的元素進行了數據綁定。只是想讓大家知道,清晰的圖片繪製沒有那麼難~ 3D 的世界沒有那麼難~ 數據綁定也沒有那麼難!我但願也能讓您發現這並非件難事。