以真实设备为模型,搭建出设备面板,并实时获取设备运行参数,显示在设备面板上,这相比于纯数值的设备监控系统显得更加生动直观。今天我们就在HT for Web的3D技术上完成设备面板的搭建。

我们今天模拟的设备是机房设备,先来目睹下最终效果:

我来解释下这个模型,一个带有透明玻璃门的机柜,机柜里装有5台设备,门可以开合,设备可以插拔,那么我么该如何搭建这样的设备呢?方法不难,我们一步一步来。

我们先从设备开始,设备的示意图如下:

看起来有模有样的,其实呢,它就是一个长方体,然后在长方体的正面贴上一张图片,这样子设备的壳就出来了,创建代码如下:

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拓扑节点的代码封装起来,精简代码,避免相同的代码重复书写,具体的封装如下:

/**
* 创建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属性来控制贴图的翻转,当然贴图也是我们事先注册好了的。

好了,到这里我们的设备模型就构建出来了,那么接下来就是创建机柜了,机柜的创建就和设备外壳的创建基本相似,不一样的地方在于,机柜有一个门,这个门有开合的功能,由于拓扑节点无法单独对节点的某一面分离出来做旋转操作,所以门必须是一个单独的拓扑节点,我们先来看看机柜的效果图:

效果图种,我们把门稍微装饰了一下,在门的边缘上加上了蓝色的贴边,让门看起来更有质感,效果图和思路都有了,代码自然而然就出来了,瞧瞧下面的代码,有一点点小复杂哦。

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拓扑组件的双击事件,然后通过事件对象获取到对应的节点,然后通过判断节点上设置的自定义标识属性来做相应的处理,具体的代码如下:

// 监听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()方法来处理双击事件,具体的处理代码如下:

/**
* 节点双击处理函数
* @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

基于HT for Web 快速搭建3D机房设备面板的更多相关文章

  1. 基于HTML5快速搭建3D机房设备面板

    以真实设备为模型,搭建出设备面板,并实时获取设备运行参数,显示在设备面板上,这相比于纯数值的设备监控系统显得更加生动直观.今天我们就在HT for Web的3D技术上完成设备面板的搭建. 我们今天模拟 ...

  2. 基于HT for Web矢量实现3D叶轮旋转

    在上一篇<基于HT for Web矢量实现2D叶轮旋转>中讲述了叶轮旋转在2D上的应用,今天我们就来讲讲叶轮旋转在3D上的应用. 在3D拓扑上可以创建各种各样的图元,在HT for Web ...

  3. 基于HT for Web的3D呈现A* Search Algorithm

    最近搞个游戏遇到最短路径的常规游戏问题,正巧看到老同事写的3D机房最短路径巡线文章,一时起兴基于HT for Web写了个A*算法的WebGL 3D呈现,算法基于开源 https://github.c ...

  4. 基于HT for Web 3D呈现Box2DJS物理引擎

    上篇我们基于HT for Web呈现了A* Search Algorithm的3D寻路效果,这篇我们将采用HT for Web 3D来呈现Box2DJS物理引擎的碰撞效果,同上篇其实Box2DJS只是 ...

  5. 基于HT for Web的Web SCADA工控移动应用

    在电力.油田燃气.供水管网等工业自动化领域Web SCADA的概念已经提出了多年,早先年的Web SCADA前端技术大部分还是基于Flex.Silverlight甚至Applet这样的重客户端方案,在 ...

  6. 基于HT for Web矢量实现HTML5文件上传进度条

    在HTML中,在文件上传的过程中,很多情况都是没有任何的提示,这在体验上很不好,用户都不知道到时有没有在上传.上传成功了没有,所以今天给大家介绍的内容是通过HT for Web矢量来实现HTML5文件 ...

  7. Reactjs-generator-cli 一款基于Ink构建用于快速搭建React应用的CLI scaffolding工具

    Reactjs-generator-cli 一款基于Ink构建用于快速搭建React应用的CLI scaffolding工具 A simple CLI for scaffolding React.js ...

  8. 基于HT for Web 3D技术快速搭建设备面板

    以真实设备为模型,搭建出设备面板,并实时获取设备运行参数,显示在设备面板上,这相比于纯数值的设备监控系统显得更加生动直观.今天我们就在HT for Web的3D技术上完成设备面板的搭建. 我们今天模拟 ...

  9. 基于HT for Web的3D拓扑树的实现

    在HT for Web中2D和3D应用都支持树状结构数据的展示,展现效果各异,2D上的树状结构在展现层级关系明显,但是如果数据量大的话,看起来就没那么直观,找到指定的节点比较困难,而3D上的树状结构在 ...

随机推荐

  1. ListView用法总结

    前言 列表,它作为一种非常重要的显示形式,不管是在web端还是在移动平台上,都是一种非常友好的,功能强大的展现形式.在Android中,ListView就接管了这一重任.尽管在Android5.X时代 ...

  2. Java多线程11:ReentrantLock的使用和Condition

    ReentrantLock ReentrantLock,一个可重入的互斥锁,它具有与使用synchronized方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大. Reentran ...

  3. 《C#图解教程》读书笔记之三:方法

    本篇已收录至<C#图解教程>读书笔记目录贴,点击访问该目录可获取更多内容. 一.方法那些事儿 (1)方法的结构:方法头—指定方法的特征,方法体—可执行代码的语句序列: (2)方法的调用:参 ...

  4. [.net 面向对象编程基础] (8) 基础中的基础——修饰符

    [.net 面向对象编程基础] (8) 基础中的基础——修饰符 在进入C#面向对象核心之前,我们需要先对修饰符有所了解,其实我们在前面说到变量和常量的时候,已经使用了修饰符,并且说明了变量和常量的修改 ...

  5. 今天心情好,一起探讨下《送给大家的200兆SVN代码服务器》怎么管理我们的VS代码?

    前几天给大家免费送了个200兆SVN代码服务器(今天心情好,给各位免费呈上200兆SVN代码服务器一枚,不谢!),还木有领取的速度戳链接哦! 好几位园友拿到SVN服务器都对其赞不绝口,我也用这个服务器 ...

  6. TCP Server—Linux

    #include <stdio.h> #include <netinet/ip.h> #define BUFF_SIZE 1024 int main(int argc,char ...

  7. Atitit jsr规范有多少个  407个。Jsr规范大全

    Atitit jsr规范有多少个  407个.Jsr规范大全 1.1. JCP维护职能是发展和更新.1 1.2. Java技术规范.参考实现(RI).技术兼容包(TCK)1 1.3. JCP维护的规范 ...

  8. OutputCache属性详解(一)一Duration、VaryByParam

    目录 OutputCache概念学习 OutputCache属性详解(一) OutputCache属性详解(二) OutputCache属性详解(三) OutputCache属性详解(四)— SqlD ...

  9. spring之BeanFactory

    spring的IOC容器能够帮我们自动new对象,对象交给spring管之后我们不用自己手动去new对象了.那么它的原理是什么呢?是怎么实现的呢?下面我来简单的模拟一下spring的机制,相信看完之后 ...

  10. Mybatis中SqlMapper配置的扩展与应用(3)

    隔了两周,首先回顾一下,在Mybatis中的SqlMapper配置文件中引入的几个扩展机制: 1.引入SQL配置函数,简化配置.屏蔽DB底层差异性 2.引入自定义命名空间,允许自定义语句级元素.脚本级 ...