基於HTML5 Canvas 點擊添加 2D 3D 機櫃模型

今天又返回好好地消化了一下咱們的數據容器 DataModel,這裏給新手作一個典型的數據模型事件處理的例子做爲參考。這個例子看起來很簡單,實際上結合了數據模型中很是重要的三個事件處理的部分:屬性變化事件監聽、選中變化事件監聽以及數據模型變化事件監聽。html

爲了讓這個例子具現化,我將這個簡單的例子作了一點改動,下面我會一一解釋。node

例子地址:http://hightopo.com/guide/gui...
圖片描述json

這是我改造以後的模樣,將 dataModel 數據容器共享,經過對數據容器的增刪事件的監聽獲得的如今的結果,而且在顯示上作了一點「手腳」。下面咱們從頭解析這例子,大家會知道爲何我特意將這個簡單的例子提出來。跨域

首先,咱們得建立場景將做爲基礎,整個場景我算是分爲三個部分,頂部工具欄,2D 部分以及 3D 部分。頂部工具欄部分使用的純 HTML 寫的:服務器

<button onclick="addData()">Add</button>
<button onclick="removeData()">Remove</button>
<button onclick="clearDataModel()">Clear</button>
<span id="property" class="output"></span>
<span id="model" class="output"></span>
<span id="selection" class="output"></span>

由於有點擊事件,因此咱們直接在 button 按鈕上進行,後面的 span 標籤顯示純文本內容。app

咱們知道,HT 的全部組件都是基於一個根部 div 的,要將這個 div 部署到 html 頁面上很簡單,可是 HT 內部對這個 div 設置了絕對定位,因此咱們在添加這個 div 進 HTML 頁面中時,也要設置絕對定位中的位置,我在頁面中添加了一個 div,將 HT 的部分都添加進這個 div 中:ide

<div id="myDiv" style="border: 1px solid red; width: 800px; height: 600px;position: absolute; "></div>
dataModel = new ht.DataModel();
g3d = new ht.graph3d.Graph3dView(dataModel);
g3d.setGridVisible(true);//設置網格可見
g3d.setEye(185, 50, 470);//設置3d的眼睛位置
g3d.setCenter(200, 47, 10);//設置3d的中心位置, 這兩個屬性都是爲了讓用戶看3d上的場景更舒服,更直接
g2d = new ht.graph.GraphView(dataModel);
g2d.setEditable(true);//設置2d圖元可編輯
g2d.fitContent(true);//將全部的圖元顯示到頁面上
splitView = new ht.widget.SplitView(g2d, g3d, 'v', 0.3);//分割組件,裝了2d和3d兩個場景
splitView.addToDOM();//將分割組件添加進body中,並設置絕對定位的位置
myDiv = document.getElementById('myDiv');
myDiv.appendChild(splitView.getView());//將分割組件添加進myDiv中

接着添加節點進入 dataModel 數據模型之中,咱們這裏作的是機房的機櫃,原本想作的是服務器,手頭上暫時只有這個資源,也不賴。我封裝了一個增長函數,一個刪除函數,還有一個清楚函數,分別對應的是工具欄上的「Add」、「Remove」以及「Clear」三個功能:函數

function addData() {
    var data = new ht.Node();
    data.setPosition(index*60, 50);
    data.setName('node'+index);
    data.setSize(40, 40);
    data.setImage('cabinet');
    data.s({
        'image.stretch': 'centerUniform',
    'shape3d': 'cabinet'
    });
    index++;
    dataModel.add(data);
    return data;
}
function removeData() {
    if(!dataModel.sm().ld()) return;
    dataModel.remove(dataModel.sm().ld());
}
function clearDataModel(){
    dataModel.clear();
    index = 0;
}

其中,代碼中出現的「data.setImage('cabinet')」,是我經過 ht.Default.setImage('cabinet', 'imageURL') 方式定義的,調用的時候直接 data.setImage('imageName') 便可,具體參考 HT for Web 入門手冊 image 章節。工具

2D 的圖片顯示確定和 3D 的模型顯示是不同的,2D 中咱們直接用貼圖就能解決,而 HT 3D 中支持 obj 格式的模型顯示,就是這個部分:ui

圖片描述

HT 封裝瞭解析 obj 格式的函數 ht.Default.loadObj 函數用來導入模型,該函數有三個參數,第一第二分別爲 obj 文件的路徑和 mtl 文件的路徑,第三個參數爲 json 格式控制參數,具體參數請參考 HT for Web OBJ 手冊 loadObj 函數章節(ps:用 obj 模型會致使跨域問題,要放到服務器上運行):

ht.Default.loadObj('obj/機櫃組件1.obj', 'obj/機櫃組件1.mtl', {
     cube: true,//是否將模型縮放到單位1的尺寸範圍內,默認爲false
     center: true,//模型是否居中
     prefix: 'obj/',//路徑前綴,若是前面參數寫了路徑前綴,這個不寫也能夠
     shape3d: 'cabinet',//指定 shape3d 名稱
     finishFunc: function(modelMap, array, rawS3){//調用ht.Default.parseObj解析後的返回值,若加載或解析失敗則返回值爲空
         window.rawS3 = rawS3;//讓當前模型的尺寸爲原始尺寸
         if(modelMap){
             cabinet1 = addData();//添加兩個節點到 dataModel 中
             cabinet2 = addData();
         }
    } 
});

如今,節點和模型都已經導入到場景中了,終於來到了咱們今天的重點,事件交互部分。ht.DataModel 數據容器管理着 Data 數據的增刪以及變化事件的派發,這裏咱們就這兩種事件進行對 Data 數據的管理。

  1. addDataModelChangeListener(function(e) {}, scope) 增長數據模型增刪變化事件監聽器,可用簡寫 mm(func, scope), func 爲監聽器函數,scope 爲監聽器函數域(可選),在監聽器函數中的 event 有兩個屬性: kind 和 data,其中 kind 爲事件的類型:

e.kind === 'add'表明添加Data對象,e.data爲被添加的對象
e.kind === 'remove'表明刪除Data對象,e.data爲被刪除的對象
e.kind === 'clear'表明容器被清除
這裏咱們將對模型的增刪事件的監聽結果傳給 HTML 中的 id 爲 model 的 span 做爲內容:

var model = document.getElementById('model');
dataModel.addDataModelChangeListener(function(e) {
    if(e.kind === 'add')//若是事件類型爲 add 增長節點
        model.innerHTML = e.data + ' added';//就將model 的內容替換爲 添加的節點 added
    if(e.kind === 'remove')
        model.innerHTML = e.data + ' removed';
    if(e.kind === 'clear')
        model.innerHTML = 'dataModel cleared'
});
  1. addDataPropertyChangeListener(function(e) {}, scope) 增長模型中 Data 數據屬性變化事件監聽器,可用簡寫 md(func, scope),其中 event 事件有四種屬性:

e.data表明屬性變化的對象
e.property表明變化屬性的名字
e.newValue表明屬性的新值
e.oldValue表明屬性的老值
Data對象在設置屬性值函數內調用firePropertyChange(property, oldValue, newValue)觸發屬性變化事件:
get/set類型屬性,如setAge(98)觸發事件的e.property爲age
style類型屬性名前加s:前綴以區分,如setStyle('age', 98)觸發事件的e.property爲s:age
attr類型屬性名前加a:前綴以區分,如setAttr('age', 98)觸發事件的e.property爲a:age
這裏咱們將對模型中 Data 的屬性變化事件的監聽結果傳給 HTML 中的 id 爲 property 的 span 做爲內容:

var model = document.getElementById('model');
dataModel.addDataPropertyChangeListener(function(e) {
    property.innerHTML = e.data + '\'s ' + e.property + ' has changed, the old value is ' + e.oldValue + ' and the new value is ' + e.newValue; 
});
  1. 最後,咱們對選中的節點進行增長監聽器,監聽選中變化事件。ht.SelectionModel管理 DataModel 模型中 Data 對象的選擇狀態, 每一個 DataModel 對象都內置一個 SelectionModel 選擇模型,控制這個 SelectionModel 便可控制全部綁定該 DataModel 的組件的對象選擇狀態, 這意味着共享同一 DataModel 的組件默認就具備選中聯動功能。

若是但願某些組件不與其餘組件選中聯動,可經過調用 view.setSelectionModelShared(false), 這樣該 view 將建立一個專屬的 SelectionModel 實例。

綜上所述有兩種途徑可獲得 SelectionModel:

dataModel.getSelectionModel()獲取數據容器中組件共享的選中模型。
view.getSelectionModel()獲取當前組件使用的選中模型,selectionModelShared爲false時,返回view專用的選擇模型。
addSelectionChangeListener(function(e) {}, scope)增長監聽器,監聽選中變化事件,簡寫爲 ms(func, scope):

e.datas包含全部選中狀態變化的對象,以前選中如今取消選中,或以前沒選中如今被選中的對象
e.kind === 'set'表明此事件由setSelection(datas)引起
e.kind === 'remove'表明此事件由removeSelection(datas)引起
e.kind === 'append'表明此事件由appendSelection(datas)引起
e.kind === 'clear'表明此事件由clearSelection(datas)引起
這裏咱們將對模型中 Data 的選中變化事件的監聽結果傳給 HTML 中的 id 爲 selection 的 span 做爲內容:

var selection = document.getElementById('selection');
dataModel.sm().addSelectionChangeListener(function(e){
    if(dataModel.sm().size() === 0) selection.innerHTML = 'Nothing selected';//若是選中模型的「長度」爲0,即沒有選中內容
    else if(dataModel.sm().size() === 1) selection.innerHTML = e.datas + ' selected';
    else selection.innerHTML = dataModel.sm().size() + ' datas selected';
});

以上,全部的代碼所有分析完畢!你們能夠天馬行空,建立出屬於你本身的3維模型!

相關文章
相關標籤/搜索