基于 HTML5 Canvas 2D 拓扑图和 3D 机柜模型的增删操作

2024-08-30 11:08

本文主要是介绍基于 HTML5 Canvas 2D 拓扑图和 3D 机柜模型的增删操作,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

今天好好地消化了一下我们的数据容器 DataModel,这里给初学者做一个典型的数据模型事件处理的例子作为参考。这个例子看起来很简单,实际上结合了数据模型中非常重要的三个事件处理的部分:属性变化事件监听、选中变化事件监听以及数据模型变化事件监听。

为了让这个例子具现化,我将这个简单的例子做了一点改动,下面我会一一解释。

例子地址:http://hightopo.com/guide/guide/core/datamodel/examples/example_datamodel.html
图片描述

这是我改造之后的模样,将 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 标签显示纯文本内容。

我们知道,HT 的所有组件都是基于一个根部 div 的,要将这个 div 部署到 html 页面上很简单,但是 HT 内部对这个 div 设置了绝对定位,所以我们在添加这个 div 进 HTML 页面中时,也要设置绝对定位中的位置,我在页面中添加了一个 div,将 HT 的部分都添加进这个 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); // 设置网格可见
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 格式的模型显示,就是这个部分:

图片描述

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的尺寸范围内,默认为 falsecenter: 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 的内容替换为添加的节点 addedif(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 维模型!

这篇关于基于 HTML5 Canvas 2D 拓扑图和 3D 机柜模型的增删操作的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1120643

相关文章

0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型的操作流程

《0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeekR1模型的操作流程》DeepSeekR1模型凭借其强大的自然语言处理能力,在未来具有广阔的应用前景,有望在多个领域发... 目录0基础租个硬件玩deepseek,蓝耘元生代智算云|本地部署DeepSeek R1模型,3步搞定一个应

Deepseek R1模型本地化部署+API接口调用详细教程(释放AI生产力)

《DeepseekR1模型本地化部署+API接口调用详细教程(释放AI生产力)》本文介绍了本地部署DeepSeekR1模型和通过API调用将其集成到VSCode中的过程,作者详细步骤展示了如何下载和... 目录前言一、deepseek R1模型与chatGPT o1系列模型对比二、本地部署步骤1.安装oll

Spring AI Alibaba接入大模型时的依赖问题小结

《SpringAIAlibaba接入大模型时的依赖问题小结》文章介绍了如何在pom.xml文件中配置SpringAIAlibaba依赖,并提供了一个示例pom.xml文件,同时,建议将Maven仓... 目录(一)pom.XML文件:(二)application.yml配置文件(一)pom.xml文件:首

CSS弹性布局常用设置方式

《CSS弹性布局常用设置方式》文章总结了CSS布局与样式的常用属性和技巧,包括视口单位、弹性盒子布局、浮动元素、背景和边框样式、文本和阴影效果、溢出隐藏、定位以及背景渐变等,通过这些技巧,可以实现复杂... 一、单位元素vm 1vm 为视口的1%vh 视口高的1%vmin 参照长边vmax 参照长边re

轻松上手MYSQL之JSON函数实现高效数据查询与操作

《轻松上手MYSQL之JSON函数实现高效数据查询与操作》:本文主要介绍轻松上手MYSQL之JSON函数实现高效数据查询与操作的相关资料,MySQL提供了多个JSON函数,用于处理和查询JSON数... 目录一、jsON_EXTRACT 提取指定数据二、JSON_UNQUOTE 取消双引号三、JSON_KE

CSS3中使用flex和grid实现等高元素布局的示例代码

《CSS3中使用flex和grid实现等高元素布局的示例代码》:本文主要介绍了使用CSS3中的Flexbox和Grid布局实现等高元素布局的方法,通过简单的两列实现、每行放置3列以及全部代码的展示,展示了这两种布局方式的实现细节和效果,详细内容请阅读本文,希望能对你有所帮助... 过往的实现方法是使用浮动加

css渐变色背景|<gradient示例详解

《css渐变色背景|<gradient示例详解》CSS渐变是一种从一种颜色平滑过渡到另一种颜色的效果,可以作为元素的背景,它包括线性渐变、径向渐变和锥形渐变,本文介绍css渐变色背景|<gradien... 使用渐变色作为背景可以直接将渐China编程变色用作元素的背景,可以看做是一种特殊的背景图片。(是作为背

C++实现封装的顺序表的操作与实践

《C++实现封装的顺序表的操作与实践》在程序设计中,顺序表是一种常见的线性数据结构,通常用于存储具有固定顺序的元素,与链表不同,顺序表中的元素是连续存储的,因此访问速度较快,但插入和删除操作的效率可能... 目录一、顺序表的基本概念二、顺序表类的设计1. 顺序表类的成员变量2. 构造函数和析构函数三、顺序表

使用C++实现单链表的操作与实践

《使用C++实现单链表的操作与实践》在程序设计中,链表是一种常见的数据结构,特别是在动态数据管理、频繁插入和删除元素的场景中,链表相比于数组,具有更高的灵活性和高效性,尤其是在需要频繁修改数据结构的应... 目录一、单链表的基本概念二、单链表类的设计1. 节点的定义2. 链表的类定义三、单链表的操作实现四、

如何在本地部署 DeepSeek Janus Pro 文生图大模型

《如何在本地部署DeepSeekJanusPro文生图大模型》DeepSeekJanusPro模型在本地成功部署,支持图片理解和文生图功能,通过Gradio界面进行交互,展示了其强大的多模态处... 目录什么是 Janus Pro1. 安装 conda2. 创建 python 虚拟环境3. 克隆 janus