Click to add 2D 3D cabinet model based on HTML5 Canvas

Today, I returned to digest our data container DataModel, and here is a typical example of data model event processing for beginners as a reference. This example looks very simple, but it actually combines three very important parts of event processing in the data model: property change event listeners, selection change event listeners, and data model change event listeners.

To make this example concrete, I made a little change to this simple example, which I will explain below.

Example address: http://hightopo.com/guide/guide/core/datamodel/examples/example_datamodel.html

 

This is what I look like after the transformation. I shared the dataModel data container, obtained the current result by monitoring the addition and deletion events of the data container, and did a little "manipulation" on the display. Let's break down this example from scratch, and you'll know why I've brought up this simple example specifically.

First of all, we have to create a scene that will serve as the basis. The whole scene is divided into three parts, the top toolbar, the 2D part and the 3D part. Written in pure HTML using the top toolbar section:

<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>

Because there is a click event, we do it directly on the button button, and the following span tag displays plain text content.

We know that all components of HT are based on a root div. It is very simple to deploy this div to the html page, but the absolute positioning of this div is set inside HT, so when we add this div into the HTML page, To set the position in absolute positioning, I added a div to the page and added the HT part into this div:

<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 ); // Set the grid to be visible 
g3d.setEye(185, 50, 470); // Set the eye position of 3d 
g3d.setCenter(200, 47, 10); // Set the center position of 3d, These two properties are all to make the user look at the scene on 3d more comfortable and more direct 
g2d = new ht.graph.GraphView(dataModel);
g2d.setEditable( true ); // Set 2d primitives to be editable 
g2d.fitContent( true ); // Display all primitives on the page 
splitView = new ht.widget.SplitView(g2d, g3d, 'v', 0.3); // Split the component, with 2d and 3d scenes 
splitView.addToDOM(); // Add the split component to the body and set the absolute positioning position 
myDiv = document.getElementById('myDiv' );
myDiv.appendChild(splitView.getView()); // Add the split component to myDiv

Then add a node into the dataModel data model. What we are doing here is the cabinet of the computer room. What we originally wanted to do is the server. We only have this resource on hand, which is not bad. I encapsulated an add function, a delete function, and a clear function, which correspond to the three functions of "Add", "Remove" and "Clear" on the toolbar:

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;
}

Among them, the "data.setImage('cabinet')" that appears in the code is defined by me through ht.Default.setImage('cabinet', 'imageURL'), and directly data.setImage('imageName') when called That's it. For details, refer to the   image chapter of the HT for Web Getting Started Manual .

The 2D image display is definitely different from the 3D model display. In 2D, we can solve it directly with textures, and the model display that supports obj format in HT 3D is this part:

 

HT encapsulates the function ht.Default.loadObj that parses the obj format. The function is used to import the model. This function has three parameters. The first and second are the path of the obj file and the path of the mtl file. The third parameter is the control parameter of the json format. , please refer to the loadObj function chapter of the HT for Web OBJ manual for specific parameters (ps: using the obj model will cause cross-domain problems, and it must be run on the server):

ht.Default.loadObj('obj/cabinet assembly 1.obj', 'obj/cabinet assembly 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'
});

2. 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.propertyage
    • style类型属性名前加s:前缀以区分,如setStyle('age', 98)触发事件的e.propertys:age
    • attr类型属性名前加a:前缀以区分,如setAttr('age', 98)触发事件的e.propertya: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; 
});

3. 最后,我们对选中的节点进行增加监听器,监听选中变化事件。ht.SelectionModel管理 DataModel 模型中 Data 对象的选择状态, 每个 DataModel 对象都内置一个 SelectionModel 选择模型,控制这个 SelectionModel 即可控制所有绑定该 DataModel 的组件的对象选择状态, 这意味着共享同一 DataModel 的组件默认就具有选中联动功能。

如果希望某些组件不与其他组件选中联动,可通过调用 view.setSelectionModelShared(false), 这样该 view 将创建一个专属的 SelectionModel 实例。

综上所述有两种途径可得到 SelectionModel:

  • dataModel.getSelectionModel()获取数据容器中组件共享的选中模型。
  • view.getSelectionModel()获取当前组件使用的选中模型,selectionModelSharedfalse时,返回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维模型!

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326369826&siteId=291194637