详解快速开发基于 HTML5 网络拓扑图应用,html5拓扑图

要实现这样的效果,该从何下手呢?接下来我们就将这个问题拆解成若干个小问题来解决。

详解快速开发基于 HTML5 网络拓扑图应用,html5拓扑图

今天开始我们就从最基础解析如何构建 HTML5 Canvas 拓扑图应用,HT
内部封装了一个拓扑图形组件 ht.graph.GraphView(以下简称 GraphView)是 HT
框架中 2D 功能最丰富的组件,其相关类库都在 ht.graph 包下。GraphView
具有基本图形的呈现和编辑功能,拓扑节点连线及自动布局功能,电力和电信等行业预定义对象,具有动画渲染等特效,因此其应用面很广泛,可作为监控领域的绘图工具和人机界面,可作为一般性的图形化编辑工具,可扩展成工作流和组织图等企业应用。简单说来就是:拓扑图是泛化的说法,电信网管的网络拓扑图、电力的电网拓扑图、工业控制的监控图、工作流程图、思维脑图等等,简单说就是节点连线构成的这些都是这里指的拓扑图。

用 HT
开发一个网络拓扑图是非常容易的一件事,只需要短短几行代码就能完成一个简单的服务器与客户端的拓扑图:

奥门新浦京官方网站 1

这个例子非常基础,几乎完成了服务器与客户端在拓扑上的所有功能。话不多说,猜猜看这个例子包括
HTML 标签的所有部分总共花了多少行代码?减去空行也就 50
行,我还做了很多样式部分的设计,毕竟给大家看的例子不能太丑嘛~

大家可以在 tuputu_jb51.rar 自行下载代码,注意因为有 json
文件,会存在图片跨域问题,需要用 Firefox 或者本地服务器跑起来。

我们在最开始就说明一下,HT 是基于 HTML5
标准的企业应用图形界面一站式解决方案, 其包含通用组件、拓扑组件和 3D
渲染引擎等丰富的图形界面开发类库,用户只需要引入 ht.js
即可,而且跟别的任何东西完全不冲突,因为 HT 只是声明了一个全局变量
ht,仅此而已。

接下来解析代码部分,首先,搭建拓扑图场景:

dm = new ht.DataModel();//数据容器
gv = new ht.graph.GraphView(dm);//拓扑组件 参数为dm 绑定的数据模型
gv.addToDOM();//将拓扑图添加进body体中

HT 的所有组件的根部都是一个 div,通过 getView() 方法获取,我们在
addToDOM 方法中就用到了这个方法:

addToDOM = function(){   
    var self = this,
        view = self.getView(),  //获取组件底层 div
        style = view.style; 
    document.body.appendChild(view);//将底层 div 添加进 body 体中           
    style.left = '0';//HT 一般将组件都设置为 absolute 的绝对定位
    style.right = '0';
    style.top = '0';
    style.bottom = '0';      
    window.addEventListener('resize', function () { self.iv(); }, false); //事件监听窗口大小变化,iv 为延时刷新组件         
}

然后向拓扑场景中添加“服务器”以及“客户端”节点:

var server = new ht.Node();
server.setName('server');//设置节点名称,显示在节点下方
server.setImage('serverImage');//设置节点图片
server.setSize(20, 60);//设置节点大小
dm.add(server);//将节点添加进数据容器dm中
server.setPosition(100, 100);//设置节点坐标(x, y)

var group = new ht.Group();//组,组中可以有多个节点
group.setImage('groupImage');//设置图片
dm.add(group);
var client = new ht.Node();//这个节点是添加进组中的
client.setName('client');
client.setImage('clientImage');
dm.add(client);
group.addChild(client);//组添加孩子
group.setExpanded(true);//设置组为展开模式
client.setPosition(200, 100);//设置节点位置 如果组中只有一个节点,那么这个节点的位置可以为组的位置

服务端与客户端的连线?2 行代码搞定!其实 HT
中添加节点的方法非常简单,一般就 2
行代码能结束:先声明实例变量,然后将这个实例变量添加进数据容器中。

var edge = new ht.Edge(server, client);
dm.add(edge);

我们很好奇虚线是怎么做出来的?虚线的形成是搭建在连线之上的,步骤有 3
个:

  1. 引入 ht-dashflow.js 文件 ;
  2. 将连线的样式属性 edge.dash.flow 设置为 true;
  3. 在场景组件中打开虚线流动的开关,这里就是 gv.enableDashFlow(true);

是不是非常简单!接下来我们看看怎么设置:

edge.s({//节点设置样式属性
    'edge.dash': true,//显示虚线
    'edge.dash.flow': true,//开启虚线流动
    'edge.dash.color': 'yellow',//虚线颜色
    'edge.dash.pattern': [8, 8],//虚线样式
    'label': 'flow',//节点注释
    'label.background': 'pink',//节点注释背景颜色        
});

奥门新浦京官方网站 ,这样所有的显示部分就介绍完毕啦~等等,好像还少点什么?对了,我忘了介绍 HT
中的 ht.Group
类了,顾名思义,就是“组”的意思,组中可以包含很多节点,双击可显示或隐藏组内的所有节点,上面代码有写到,但是我还做了一点小动作,就是组右上角的显示部分,其实就是一个标注,用来提示说明的:

group.s({
    'group.background': 'rgba(255, 255, 0, 0.1)',//设置组的背景颜色
    'note': "Double click me!",//标注 显示的内容
    'note.position': 13,//标注位置
    'note.offset.y': 10,//标注位置y轴偏移
});

我们可以通过 note.position 来改变标注的位置(具体位置信息请参考HT for
Web 位置手册 ),也可以使用 note.offset.x 和 note.offset.y
来改变标注的位置。

全部代码解析完毕!我会尽快更新,如果大家觉得进度慢的话,可以自己去我们官网(
HT for Web
)上学习,希望大家能有更多的收获,学习是自己的事情,快动手实践将这片文章的内容变成你自己的知识吧!
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持帮客之家。

HTML5
网络拓扑图应用,html5拓扑图 今天开始我们就从最基础解析如何构建 HTML5
Canvas 拓扑图应用,HT 内部封装了一个拓扑…

现在,3D
模型已经用于各种不同的领域。在医疗行业使用它们制作器官的精确模型;电影行业将它们用于活动的人物、物体以及现实电影;视频游戏产业将它们作为计算机与视频游戏中的资源;在科学领域将它们作为化合物的精确模型;建筑业将它们用来展示提议的建筑物或者风景表现;工程界将它们用于设计新设备、交通工具、结构以及其它应用领域;在最近几十年,地球科学领域开始构建三维地质模型,而且
3D
模型经常做成动画,例如,在故事片电影以及计算机与视频游戏中大量地应用三维模型。它们可以在三维建模工具中使用或者单独使用。为了容易形成动画,通常在模型中加入一些额外的数据,例如,一些人类或者动物的三维模型中有完整的骨骼系统,这样运动时看起来会更加真实,并且可以通过关节与骨骼控制运动。

1. 创建一个树状结构

有了解过HT for
Web的朋友,对树状结构数据的创建应该都不陌生,在这里我就不做深入的探讨了。树状结构数据的创建很简单,在这里为了让代码更简洁,我封装了三个方法来创建树状结构数据,具体代码如下:

/**
 * 创建连线
 * @param {ht.DataModel} dataModel - 数据容器
 * @param {ht.Node} source - 起点
 * @param {ht.Node} target - 终点
 */
function createEdge(dataModel, source, target) {
    // 创建连线,链接父亲节点及孩子节点
    var edge = new ht.Edge();
    edge.setSource(source);
    edge.setTarget(target);
    dataModel.add(edge);
}

/**
 * 创建节点对象
 * @param {ht.DataModel} dataModel - 数据容器
 * @param {ht.Node} [parent] - 父亲节点
 * @returns {ht.Node} 节点对象
 */
function createNode(dataModel, parent) {
    var node = new ht.Node();
    if (parent) {
        // 设置父亲节点
        node.setParent(parent);

        createEdge(dataModel, parent, node);
    }
    // 添加到数据容器中
    dataModel.add(node);
    return node;
}

/**
 * 创建结构树
 * @param {ht.DataModel} dataModel - 数据容器
 * @param {ht.Node} parent - 父亲节点
 * @param {Number} level - 深度
 * @param {Array} count - 每层节点个数
 * @param {function(ht.Node, Number, Number)} callback - 回调函数(节点对象,节点对应的层级,节点在层级中的编号)
 */
function createTreeNodes(dataModel, parent, level, count, callback) {
    level--;
    var num = (typeof count === 'number' ? count : count[level]);

    while (num--) {
        var node = createNode(dataModel, parent);
        // 调用回调函数,用户可以在回调里面设置节点相关属性
        callback(node, level, num);
        if (level === 0) continue;
        // 递归调用创建孩子节点
        createTreeNodes(dataModel, node, level, count, callback);
    }
}

嘿嘿,代码写得可能有些复杂了,简单的做法就是嵌套几个for循环来创建树状结构数据,在这里我就不多说了,接下来我们来探究第二个问题。

底层的一个环形的效果是通过一个算法来实现的,环形得确认这个环形上有多少个元素,然后算每两个之间的角度,在通过
sin、cos 来计算每一个元素的位置,得出了如下代码:

奥门新浦京官方网站 2

if(formPane){ alarmNode.scaleFunc = function() {//设置大小变化动画 var size = alarmNode.s3();//获取节点的大小 if (size[0] === 2  size[1] === 2  size[2] === 2) alarmNode.s3([1, 1, 1]); else alarmNode.s3([2, 2, 2]); alarmNode.scaleTimer = setTimeout(alarmNode.scaleFunc, formPane.v('scaleInterval'));//设置动画 } alarmNode.blinkFunc = function(){//设置闪烁的动画 var color = alarmNode.s('all.blend');//获取节点的style样式 if (color === 'red') alarmNode.s({'all.blend': 'yellow'});//如果节点颜色为红色,那么设置为黄色 else alarmNode.s({'all.blend': 'red'}); alarmNode.blinkTimer = setTimeout(alarmNode.blinkFunc, formPane.v('blinkInterval')); } alarmNode.rotateFunc = function() {//设置旋转动画 alarmNode.setRotation(alarmNode.getRotation() + Math.PI/20);//获取节点当前的旋转角度,在这个旋转角度之上添加 Math.PI/20 个角度 alarmNode.rotateTimer = setTimeout(alarmNode.rotateFunc, formPane.v('rotInterval')); }}

在HT for
Web中2D和3D应用都支持树状结构数据的展示,展现效果各异,2D上的树状结构在展现层级关系明显,但是如果数据量大的话,看起来就没那么直观,找到指定的节点比较困难,而3D上的树状结构在展现上配合HT
for
Web的弹力布局组件会显得比较直观,一眼望去可以把整个树状结构数据看个大概,但是在弹力布局的作用下,其层次结构看得就不是那么清晰了。所以这时候结构清晰的3D树的需求就来了,那么这个3D树具体长成啥样呢,我们来一起目睹下~

var ball = new ht.Node();//创建小球节点ball.s({//设置小球节点的样式 'shape3d': 'sphere',//设置小球的3d模型为球形 'shape3d.color': 'rgba(40, 90, 240, 0.4)'//设置3d模型的颜色});var delta = 10, flag = 0;setInterval(function(){ flag++; var length = (polyline.a('total') || 0) % polyline.a('length') + delta;//小球当前走过的曲线长度 var cache = ht.Default.getLineCacheInfo(list, polyline.s('edge.segments'));//获取曲线上的点的信息 var lineLength = ht.Default.getLineLength(cache);//获取曲线的总长度 polyline.a('length', lineLength - 50);//因为我设置了edge的t3(相当于2d中的offset),所以线段长度实际没有那么长 var offset = ht.Default.getLineOffset(cache, length);//曲线根据曲线上点的信息的偏移量 ball.setPosition3d(offset.point.x + 10, offset.point.y, offset.point.z);//设置节点的坐标 polyline.a('total', length); if(flag === 1) dataModel.add(ball);//这时候节点已经有了坐标了,可以添加进数据容器中了}, 10);

3. 加入z轴坐标,呈现3D下的树状结构

3D拓扑上面布局无非就是多加了一个坐标系,而且这个坐标系只是控制节点的高度而已,并不会影响到节点之间的重叠,所以接下来我们来改造下我们的程序,让其能够在3D上正常布局。

也不需要太大的改造,我们只需要修改下布局器并且将2D拓扑组件改成3D拓扑组件就可以了。

/**
 * 布局树
 * @param {ht.Node} root - 根节点
 */
function layout(root) {
    // 获取到所有的孩子节点对象数组
    var children = root.getChildren().toArray();
    // 获取孩子节点个数
    var len = children.length;
    // 计算张角
    var degree = root.a('degree');
    // 根据三角函数计算绕父亲节点的半径
    var r = root.a('radius');
    // 获取父亲节点的位置坐标
    var rootPosition = root.p3();

    children.forEach(function(child, index) {
        // 根据三角函数计算每个节点相对于父亲节点的偏移量
        var s = Math.sin(degree * index),
            c = Math.cos(degree * index),
            x = s * r,
            z = c * r;

        // 设置孩子节点的位置坐标
        child.p3(x + rootPosition[0], rootPosition[1] - 100, z + rootPosition[2]);

        // 递归调用布局孩子节点
        layout(child);
    });
}

上面是改造成3D布局后的布局器代码,你会发现和2D的布局器代码就差一个坐标系的的计算,其他的都一样,看下在3D上布局的效果:

奥门新浦京官方网站 3

恩,有模有样的了,在文章的开头,我们可以看到每一层的节点都有不同的颜色及大小,这些都是比较简单,在这里我就不做深入的讲解,具体的代码实现如下:

var level = 4,
    size = (level + 1) * 20;

var root = createNode(dataModel);
root.setName('root');
root.p(100, 100);

root.s('shape3d', 'sphere');
root.s('shape3d.color', randomColor());
root.s3(size, size, size);

var colors = {},
    sizes = {};
createTreeNodes(dataModel, root, level - 1, 5, function(data, level, num) {
    if (!colors[level]) {
        colors[level] = randomColor();
        sizes[level] = (level + 1) * 20;
    }

    size = sizes[level];

    data.setName('item-' + level + '-' + num);
    // 设置节点形状为球形
    data.s('shape3d', 'sphere');
    data.s('shape3d.color', colors[level]);
    data.s3(size, size, size);
});

在这里引入了一个随机生成颜色值的方法,对每一层随机生成一种颜色,并将节点的形状改成了球形,让页面看起来美观些(其实很丑)。

奥门新浦京官方网站 4

提个外话,节点上可以贴上图片,还可以设置文字的朝向,可以根据用户的视角动态调整位置,等等一系列的拓展,这些大家都可以去尝试,相信都可以做出一个很漂亮的3D树出来。

到此,整个Demo的制作就结束了,今天的篇幅有些长,感谢大家的耐心阅读,在设计上或则是表达上有什么建议或意见欢迎大家提出,点击这里可以访问HT
for Web官网上的手册。

formPane.addRow([//向form表单面板上添加一行元素 { checkBox: {//复选框 label: 'Enable Blink',//复选框对应的文本内容 selected: true,//设置选中复选框 onValueChanged: function(){//复选框值变化时回调的函数 var data = dataModel.getDataByTag('colorAlarm');//通过tag标签获取节点 if (this.getValue()) {//获取复选框当前值true/false data.blinkTimer = setTimeout(data.blinkFunc, formPane.v('blinkInterval'));//直接通过设置节点的blinkTimer来设置动画 } else { clearTimeout(data.blinkTimer);//清除动画 } } } }, { id: 'blinkInterval',//form可以通过getValue(简写为v)来获取这个item的值 slider: {//设置了该属性后HT将根据属性值自动构建ht.widget.Slider对象,并保存在element属性上 min: 0,//滑动条最小值 max: 1000,//滑动条最大值 step: 50,//滑动条步进 value: 500,//当前滑动条的值 } }], [0.1, 0.1]);//设置这行的两个item元素的宽度小于1的值为比例

2. 在2D拓扑下模拟3D树状结构每层的半径计算

在3D下的树状结构体最大的问题就在于,每个节点的层次及每层节点围绕其父亲节点的半径计算。现在树状结构数据已经有了,那么接下来就该开始计算半径了,我们从两层树状结构开始推算:

奥门新浦京官方网站 5

我现在先创建了两层的树状结构,所有的子节点是一字排开,并没有环绕其父亲节点,那么我们该如何去确定这些孩子节点的位置呢?

首先我们得知道,每个末端节点都有一圈属于自己的领域,不然节点与节点之间将会存在重叠的情况,所以在这里,我们假定末端节点的领域半径为25,那么两个相邻节点之间的最短距离将是两倍的节点领域半径,也就是50,而这些末端节点将均匀地围绕在其父亲节点四周,那么相邻两个节点的张角就可以确认出来,有了张角,有了两点间的距离,那么节点绕其父亲节点的最短半径也就能计算出来了,假设张角为a,两点间最小距离为b,那么最小半径r的计算公式为:

r = b / 2 / sin(a / 2);

那么接下来我么就来布局下这个树,代码是这样写的:

/**
 * 布局树
 * @param {ht.Node} root - 根节点
 * @param {Number} [minR] - 末端节点的最小半径
 */
function layout(root, minR) {
    // 设置默认半径
    minR = (minR == null ? 25 : minR);
    // 获取到所有的孩子节点对象数组
    var children = root.getChildren().toArray();
    // 获取孩子节点个数
    var len = children.length;
    // 计算张角
    var degree = Math.PI * 2 / len;
    // 根据三角函数计算绕父亲节点的半径
    var sin = Math.sin(degree / 2),
        r = minR / sin;
    // 获取父亲节点的位置坐标
    var rootPosition = root.p();

    children.forEach(function(child, index) {
        // 根据三角函数计算每个节点相对于父亲节点的偏移量
        var s = Math.sin(degree * index),
            c = Math.cos(degree * index),
            x = s * r,
            y = c * r;

        // 设置孩子节点的位置坐标
        child.p(x + rootPosition.x, y + rootPosition.y);
    });
}

在代码中,你会发现我将末端半径默认设置为25了,如此,我们通过调用layout()方法就可以对结构树进行布局了,其布局效果如下:

奥门新浦京官方网站 6

从效果图可以看得出,末端节点的默认半径并不是很理想,布局出来的效果连线都快看不到了,因此我们可以增加末端节点的默认半径来解决布局太密的问题,如将默认半径设置成40的效果图如下:

奥门新浦京官方网站 7

现在两层的树状分布解决了,那么我们来看看三层的树状分布该如何处理。

将第二层和第三层看成一个整体,那么其实三层的树状结构跟两层是一样的,不同的是在处理第二层节点时,应该将其看做一个两层的树状结构来处理,那么像这种规律的处理用递归最好不过了,因此我们将代码稍微该着下,在看看效果如何:

奥门新浦京官方网站 8

不行,节点都重叠在一起了,看来简单的递归是不行的,那么具体的问题出在哪里呢?

仔细分析了下,发现父亲节点的领域半径是由其孩子节点的领域半径决定的,因此在布局时需要知道自身节点的领域半径,而且节点的位置取决于父亲节点的领域半径及位置信息,这样一来就无法边计算半径边布局节点位置了。

那么现在只能将半径的计算和布局分开来,做两步操作了,我们先来分析下节点半径的计算:

首先需要明确最关键的条件,父亲节点的半径取决于其孩子节点的半径,这个条件告诉我们,只能从下往上计算节点半径,因此我们设计的递归函数必须是先递归后计算,废话不多说,我们来看下具体的代码实现:

/**
 * 就按节点领域半径
 * @param {ht.Node} root - 根节点对象
 * @param {Number} minR - 最小半径
 */
function countRadius(root, minR) {
    minR = (minR == null ? 25 : minR);

    // 若果是末端节点,则设置其半径为最小半径
    if (!root.hasChildren()) {
        root.a('radius', minR);
        return;
    }

    // 遍历孩子节点递归计算半径
    var children = root.getChildren();
    children.each(function(child) {
        countRadius(child, minR);
    });

    var child0 = root.getChildAt(0);
    // 获取孩子节点半径
    var radius = child0.a('radius');

    // 计算子节点的1/2张角
    var degree = Math.PI / children.size();
    // 计算父亲节点的半径
    var pRadius = radius / Math.sin(degree);

    // 设置父亲节点的半径及其孩子节点的布局张角
    root.a('radius', pRadius);
    root.a('degree', degree * 2);
}

OK,半径的计算解决了,那么接下来就该解决布局问题了,布局树状结构数据需要明确:孩子节点的坐标位置取决于其父亲节点的坐标位置,因此布局的递归方式和计算半径的递归方式不同,我们需要先布局父亲节点再递归布局孩子节点,具体看看代码吧:

/**
 * 布局树
 * @param {ht.Node} root - 根节点
 */
function layout(root) {
    // 获取到所有的孩子节点对象数组
    var children = root.getChildren().toArray();
    // 获取孩子节点个数
    var len = children.length;
    // 计算张角
    var degree = root.a('degree');
    // 根据三角函数计算绕父亲节点的半径
    var r = root.a('radius');
    // 获取父亲节点的位置坐标
    var rootPosition = root.p();

    children.forEach(function(child, index) {
        // 根据三角函数计算每个节点相对于父亲节点的偏移量
        var s = Math.sin(degree * index),
            c = Math.cos(degree * index),
            x = s * r,
            y = c * r;

        // 设置孩子节点的位置坐标
        child.p(x + rootPosition.x, y + rootPosition.y);

        // 递归调用布局孩子节点
        layout(child);
    });
}

代码写完了,接下来就是见证奇迹的时刻了,我们来看看效果图吧:

奥门新浦京官方网站 9

不对呀,代码应该是没问题的呀,为什么显示出来的效果还是会重叠呢?不过仔细观察我们可以发现相比上个版本的布局会好很多,至少这次只是末端节点重叠了,那么问题出在哪里呢?

不知道大家有没有发现,排除节点自身的大小,倒数第二层节点与节点之间的领域是相切的,那么也就是说节点的半径不仅和其孩子节点的半径有关,还与其孙子节点的半径有关,那我们把计算节点半径的方法改造下,将孙子节点的半径也考虑进去再看看效果如何,改造后的代码如下:

/**
 * 就按节点领域半径
 * @param {ht.Node} root - 根节点对象
 * @param {Number} minR - 最小半径
 */
function countRadius(root, minR) {
   ……

    var child0 = root.getChildAt(0);
    // 获取孩子节点半径
    var radius = child0.a('radius');

    var child00 = child0.getChildAt(0);
    // 半径加上孙子节点半径,避免节点重叠
    if (child00) radius += child00.a('radius');

   ……
}

下面就来看看效果吧~

奥门新浦京官方网站 10

哈哈,看来我们分析对了,果然就不再重叠了,那我们来看看再多一层节点会是怎么样的壮观场景呢?

奥门新浦京官方网站 11

哦,NO!这不是我想看到的效果,又重叠了,好讨厌。

不要着急,我们再来仔细分析分析下,在前面,我们提到过一个名词——领域半径,什么是领域半径呢?很简单,就是可以容纳下自身及其所有孩子节点的最小半径,那么问题就来了,末端节点的领域半径为我们指定的最小半径,那么倒数第二层的领域半径是多少呢?并不是我们前面计算出来的半径,而应该加上末端节点自身的领域半径,因为它们之间存在着包含关系,子节点的领域必须包含于其父亲节点的领域中,那我们在看看上图,是不是感觉末端节点的领域被侵占了。那么我们前面计算出来的半径代表着什么呢?前面计算出来的半径其实代表着孩子节点的布局半径,在布局的时候是通过该半径来布局的。

OK,那我们来总结下,节点的领域半径是其下每层节点的布局半径之和,而布局半径需要根据其孩子节点个数及其领域半径共同决定。

好了,我们现在知道问题的所在了,那么我们的代码该如何去实现呢?接着往下看:

/**
 * 就按节点领域半径及布局半径
 * @param {ht.Node} root - 根节点对象
 * @param {Number} minR - 最小半径
 */
function countRadius(root, minR) {
    minR = (minR == null ? 25 : minR);

    // 若果是末端节点,则设置其布局半径及领域半径为最小半径
    if (!root.hasChildren()) {
        root.a('radius', minR);
        root.a('totalRadius', minR);
        return;
    }

    // 遍历孩子节点递归计算半径
    var children = root.getChildren();
    children.each(function(child) {
        countRadius(child, minR);
    });

    var child0 = root.getChildAt(0);
    // 获取孩子节点半径
    var radius = child0.a('radius'),
        totalRadius = child0.a('totalRadius');

    // 计算子节点的1/2张角
    var degree = Math.PI / children.size();
    // 计算父亲节点的布局半径
    var pRadius = totalRadius / Math.sin(degree);

    // 缓存父亲节点的布局半径
    root.a('radius', pRadius);
    // 缓存父亲节点的领域半径
    root.a('totalRadius', pRadius + totalRadius);
    // 缓存其孩子节点的布局张角
    root.a('degree', degree * 2);
}

在代码中我们将节点的领域半径缓存起来,从下往上一层一层地叠加上去。接下来我们一起验证其正确性:

奥门新浦京官方网站 12

搞定,就是这样子了,2D拓扑上面的布局搞定了,那么接下来该出动3D拓扑啦~

var polyline = new ht.Edge(source, target);//创建连线dataModel.add(polyline);//将连线添加进数据容器中polyline.s({ 'edge.width': 5,//连线宽度 'edge.type': 'points',//连线类型 为points时连线走向将由edge.points属性决定,用于绘制折线 'edge.points': [//可设置类型为ht.List的{x:100, y:100}格式的点对象数组,当edge.type为points时起作用 {x: source.getPosition3d()[0]+200, y: source.getPosition3d()[2], e: source.getPosition3d()[1]}, {x: target.getPosition3d()[0]+400, y: target.getPosition3d()[2], e: target.getPosition3d()[1]} ], 'edge.segments': [1, 4],//用于描述点连接样式,数组元素为整型值 'shape3d': 'cylinder',//圆柱 'shape3d.color': 'rgba(242, 200, 40, 0.4)', 'shape3d.resolution': 30,//微分段数,可以决定曲线的平滑度 'edge.source.t3': [20, 0, 0],//连线source端偏移,[tx, ty, tz]格式,默认为空 'edge.target.t3': [20, 0, 0]//连线target端偏移,[tx, ty, tz]格式,默认为空});

这些种种都让我们前端开发者觉得如果我们可以不用学习 unity3d
或者其他游戏开发工具就能实现 3D
效果,而且能够精准的靠代码来控制移动或者方向就好了。。。于是我利用 HT
For Web 中的 3D组件 来实现了一个小例子,用了 HT 中 3D组件
的大部分功能,做这个例子就是想把 3D
组件好好的掌握,尽量放进一个例子中,到时候别人有需要就可以参考了。

names = ['设备2', '设备3', '设备4', '设备5', '设备6', '设备7', '设备8', '设备9']; names.forEach(function(name, index) { x = 400, y = 200, angle = 45, r = 120; x = x3 + Math.sin((2 * Math.PI / 360) * angle * index) * r; y = z3 + Math.cos((2 * Math.PI / 360) * angle * index) * r; device = createRect([x, y3 + 15, y], [w * 0.1, 15, h * 0.1], '', '', floor3); createEdge(device5, device); }); 

最后来说说 3D
管线上的小球流动的部分,这个功能确实非常实用,而且做出来的效果也确实不错,跟大家分享~

首先,创建一条连线连接起始节点和结束节点并设置这个连线的样式,用 ht.Edge
可以将连线吸附在起始节点和结束节点上,这样移动这两个节点中的任意一个节点连线都会跟着节点移动的位置变化,非常方便:

接下来看看怎么让这个“告警”节点“闪烁”,我是直接将这个动画跟节点绑定,这样可以直接通过节点来控制动画。所以在上面我们创建
alarm 的模型时就可以直接将动画绑在节点上:

其他如果还有不懂的部分可以去官网()查看对应…,或者留言私信都可以。

ht.Default.loadObj('obj/机柜组件1.obj', 'obj/机柜组件1.mtl', { //加载 obj 文件 cube: true, //是否将模型缩放到单位1的尺寸范围内,默认为false center: true, //模型是否居中,默认为false,设置为true则会移动模型位置使其内容居中 shape3d: 'box', //如果指定了shape3d名称,则HT将自动将加载解析后的所有材质模型构建成数组的方式,以该名称进行注册 finishFunc: function(modelMap, array, rawS3){ //用于加载后的回调处理 if(modelMap){ device2 = createNode('box', floor1); //创建一个节点,在第一层“地板”上 device2.p3([x1-120, y1+13, z1+60]); //设置这个节点坐标 device2.s3(rawS3); //设置这个节点大小 createEdge(device1, device2); //创建连线 device3 = createNode('box', floor1); device3.s3(rawS3); device3.p3([x1+120, y1+13, z1+60]); createEdge(device1, device3); } } }); 

来源:

var list = new ht.List();list.push({x: source.getPosition3d()[0], y: source.getPosition3d()[2], e: source.getPosition3d()[1]});//向数组中添加source点polyline.s('edge.points').each(function(item){//添加style属性中已设置的两个点 list.push(item);});list.push({x: target.getPosition3d()[0], y: target.getPosition3d()[2], e: target.getPosition3d()[1]});//添加target点

“电脑”上方有个红色的立体能旋转的“警告”,是依靠
ht.Default.setShape3dModel 函数(HT for Web 建模手册 模型,在 ht
中,封装好的建模函数有很多,比较基础的就是球体,圆柱,立方体等等,这边我用的是构造环形的方法
createRingModel 来生成“警告”最外面的环,感叹号的上部分就是用的
createSmoothSphereModel 构造的球体,感叹号的下部分就是用
createSmoothCylinderModel 来构造的圆柱。我一开始直接使用了 3d
模型中封装好的函数,导致后来根本不知道函数中使用的参数是做什么用的,而且也不明白
3d 模型是怎么构成的,然后自己又重新看了前面的“模型基础”,才知道原来 3d
模型采用的一个面,最基础的是三角面,之后复杂的面也是由多个三角面来形成的,然后绕着一根特定的轴旋转之后形成的,当然,这个轴是你来决定的,不同的轴可以生成不同的形状,对于颜色等风格方面的设置可以参考
HT for Web 风格手册(。至于如何让这个 3d 模型旋转起来,ht 中封装了
addScheduleTask(Task) 方法,我在第三层 Task 中调用了 ht
封装的一个旋转函数 setRotation
来设置旋转的顺序和方向,并且指定了旋转的对象。以下是自定义“警告”的 3d
模型的方法(注意:因为本例的模型是自定义组合的,如果要设置整体模型的颜色要用
“all.blend” style 属性):

用 HT for Web,现有的 3d
模板创建三层底板不是问题,问题是要如何将图中第一层的“电脑”和“机柜组件”放上去?我是在网上
down 下来的 obj 格式的文件,然后我利用 HT 中的
ht.Default.loadObj(objUrl, mtlUrl, params) 函数将模型加载进去,其中的
params 部分可以参考,代码如下:

其中 finishiFunc 函数中的三个参数定义如下:

附上本文例子:

function createAlarm(device, formPane) { var ringModel = ht.Default.createRingModel([ 8, 1, 10, 1, 10, -1, 8, -1, 8, 1 ], null, null, false, false, 100);//根据xy平面的曲线,环绕一周形成3D模型。 var sphereModel = ht.Default.createSmoothSphereModel(8, 8, 0, Math.PI*2, 0, Math.PI, 2);//构建光滑球体模型 var cylinderModel = ht.Default.createSmoothCylinderModel(8, true, true, 1, 2, 0, Math.PI*2, 8);//构建光滑圆柱体模型 var alarmArr = [//组合模型 由三个模型ringModel、sphereModel、cylinderModel组合而成 { shape3d: ringModel,//定义模型类型 r3: [Math.PI/2, 0, 0],//设置旋转角度 color: {//设置模型颜色 func: 'style@all.blend',//数据绑定style样式中的all.blend属性,可通过data.s()获取和设置这个属性 } },{ shape3d: sphereModel, t3: [0, 4, 0], color: { func: 'style@all.blend', } },{ shape3d: cylinderModel, t3: [0, -3, 0], color: { func: 'style@all.blend', } } ]; ht.Default.setShape3dModel('alarm', {//注册自定义3D模型 shape3d: alarmArr }); var alarmTip = createNode('alarm', device);//创建shape3d为alarm的节点 alarmTip.s3([2, 2, 2]);//设置节点大小 alarmTip.p3(device.p3()[0], device.p3()[1]+60, device.p3()[2]); alarmTip.s('all.blend', 'red');//改变此属性可改变模型的颜色,因为模型创建的时候已经数据绑定了 return alarmTip;}

modelMap:调用 ht.Default.parseObj
解析后的返回值,若加载或解析失败则返回值为空array:所有材质模型组成的数组rawS3:包含所有模型的原始尺寸

上面的动画我设置了可以通过 form
表单面板上的属性来控制节点闪烁的速度,以及闪烁节点的动画等等,主要说一下这个功能在
form 表单上的实现:

因为我们在创建连线的时候设置的 points
仅为曲线上的两个点,所以如果要获取曲线目前形成的点,是缺少 source 和
target
两个点的,我们重新设置一个数组,将这两个点添加进去,后面获取曲线上所有点时会用上:

然后创建一个在管线上滑动的小球节点,这是仅是设置节点,真正添加进数据容器
dataModel
中需要设置完小球的坐标时再添加,如果没有给节点设置位置就将节点添加进数据容器中,节点的初始位置就是
3D 场景的正中心 [0, 0, 0] 的位置。小球滑动的动画代码如下:

时间: 2018-01-29阅读: 738标签: WebGL

先来看看整体实现的效果图:

我们还可以看到第二层上有两个特殊的多边形“平行四边形”和“梯形”,平行四边形是靠
createParallelogramModel
模型函数,这个函数比较简单,createExtrusionModel(array, segments, top,
bottom, resolution, repeatUVLength, tall, elevation),array
是你要形成的图形的坐标点,这边只是针对于 xz 轴上画的平面图形,segments
指的是如何连接这几个坐标点,可参考 HT for Web 形状手册(),top 和
bottom 就是让你选择是否有顶部或者底部,resolution
微分段数,我们描绘一段曲线的时候可能只要确认几个个别的点然后在每两个点之间的连线上把它分成多个段,这样这条线段就会变得平滑,ht
为了用户能够轻松操作这些线段,就封装了这一个参数,repeatUVLength
默认为空,设置值后顶部和底部的贴图将根据制定长度值进行重复,tall
模型的高度,默认为 5,elevation 模型中心的 y 轴位置,默认值为
0,设置这个值可以使 xz 上的平面绕着 y 轴旋转。

一般在实际应用中我们都会将图元的大小设置为模型的原始尺寸。

发表评论

电子邮件地址不会被公开。 必填项已用*标注