我们上一篇《基于 WebSocket 实现 WebGL 3D 拓扑图实时数据通讯同步(一)》主要讲解了如何搭建一个实时数据通讯服务器,客户端与服务端是如何通讯的,相信通过上一篇的讲解,再配合上数据库的数据储存,我们就可以实现一个简易版的 Web 聊天工具了,有空的朋友可以自己尝试下实现,那么我们今天的主要内容真的是实现 WebGL 3D 拓扑图实时数据通讯了,请大家接着往下看。

有了前面的知识储备,我们就可以来真正实现我们 3D 拓扑图组件上节点位置信息的实时数据同步了,毋庸置疑,节点的位置信息必须是在服务端统筹控制,才能达到实时数据同步,也就是说,我们必须在服务端创建 DataModel 来管理节点,创建 ForceLayout 弹力布局节点位置,并在节点位置改变的过程中,实时地将位置信息推送到客户端,让每个客户端都更新各自页面上面的节点位置。

在服务端我们该如何创建 HT 的 DataModel 和 ForceLayout 呢?其实也很简单,我们可以看看下面的代码:

var ht = global.ht = this.ht = require('../../../build/ht-debug.js').ht,
dataModel = new ht.DataModel(),
reloadModel = require("../util.js").reloadModel;
reloadModel(dataModel, { A: 3, B: 5 }); require("../../../build/ht-forcelayout-debug.js");
var forceLayout = new ht.layout.Force3dLayout(dataModel);
forceLayout.onRelaxed = function() {
var result = {};
dataModel.each(function(data) {
if (data instanceof ht.Node) {
result[data.getTag()] = data.p3();
}
});
io.emit('result', result);
};
forceLayout.start();

我们通过 require 将非 Node.js 模块包引入到程序中,并加以使用。在上面的代码中,我们确实创建了 HT 的拓扑节点,是通过 util.js 文件中的 relowdModel 方法创建的节点,那这个文件中到底是怎么实现创建 HT 拓扑节点的呢?接下来就来看看具体的实现:

function createNode(dataModel, id){
var node = new ht.Node();
node.setId(id);
node.setTag(id);
node.s3(40, 40, 40);
node.s({
'shape3d': 'sphere',
'note': id,
'note.position': 17,
'note.background': 'yellow',
'note.color': 'black',
'note.autorotate': true,
'note.face': 'top'
});
dataModel.add(node);
return node;
} function createEdge(dataModel, source, target){
var edge = new ht.Edge(source, target);
edge.s({
'edge.width': 10,
'shape3d.color': '#E74C3C',
'edge.3d': true
});
dataModel.add(edge);
return edge;
} function reloadModel(dataModel, info){
dataModel.clear(); var ip = "192.168.1.";
var count = 0;
var root = createNode(dataModel, ip + count++); for (var i = 0; i < info.A; i++) {
var iNode = createNode(dataModel, ip + count++);
createEdge(dataModel, root, iNode); for (var j = 0; j < info.B; j++) {
var jNode = createNode(dataModel, ip + count++);
createEdge(dataModel, iNode, jNode);
}
}
} this.reloadModel = reloadModel;

在这个文件中,封装了创建节点的方法 createNode,和创建连线的方法 createEdge,最后是通过 reloadModel 方法将前面的两个方法连接起来,在这个文件的最后,我们可以看到,只公开了 reloadModel 的函数接口。

当然光这些是不够的,这些还不能够达成实时数据通讯的功能,我们还需要监听和派发一些事件才能够达到效果,那么我们都监听了什么借口,派发了什么事件呢?

io.on('connection', function(socket) {
socket.emit('ready', dataModel.serialize(0)); console.log('a user connected');
socket.on('disconnect', function() {
console.log('user disconnected');
}); socket.on('moveMap', function(moveMap) {
dataModel.sm().cs();
for (var id in moveMap) {
var data = dataModel.getDataByTag(id);
if (data) {
data.p3(moveMap[id]);
dataModel.sm().as(data);
}
}
});
});

上面那串代码是我们的事件监听,我们通过监听 moveMap 的事件,并获取从客户端传递上来的移动的节点坐标信息,根据参数的内容,我们将其改变服务端的 DataModel 中对应节点的坐标,改变后 ForceLayout 就会根据当前的状态去调整整个拓扑上所有节点的位置。那么在调节的过程中,我们是怎么知道 ForceLayout 是正在调整的呢?在前面介绍如何在 Node.js 上面创建 HT 相关的组件时贴出来的代码中就告诉我么怎么做了。

在创建 ForceLayout 组件的代码后面,紧跟着就是重载 ForceLayout 组件的 onRelaxed 方法,每次布局玩后,都会调用这个方法,这样我们就可以在这个方法中,编辑获取到 DataModel 中的所有节点的当前位置,并通过 io.emit 方法通知给所有的客户端,让客户端去实时更新对应节点的坐标位置。

但是还有一个问题,我们要怎么样让客户端显示的节点和服务端上的节点一一对应呢?首先不能让客户端自己创建节点,我们的做法其实也很简单,虽然不能保证客户端的节点 ID 会和服务端的节点 ID 一模一样,但是我们可以保证其他关键属性是一样,因为我们利用了 HT 的序列化功能,当有客户端连接到服务器时,就会向客户端派发 ready 事件,将 DataModel 序列化的结果返回到客户端,让客户端反序列化,从而达到数据基本一致的效果。

那么客户端和服务端的节点是如何保持一一对应的呢?首先我们得了解 HT 在获取节点对象上提供了几个方法,熟悉的朋友应该知道,有 getDataById 和 getDataByTag 两个方法,其中 ID 是 HT 系统自己维护的属性,Tag 是提供给用户自己维护其唯一性的属性,一般不建议使用 ID 作为业务上面的唯一标识,因为在序列化和反序列化时候可能会有细微的差别,很难保证反序列话后的节点 ID 和序列化前的 ID 是一样的。因此在本文中,我们是通过 Tag 属性来控制服务器和客户端的节点一一对应的。

接下来我们来看看客户端的实现吧:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<script src="/socket.io/socket.io.js"></script>
<script src="/build/ht-debug.js"></script>
<script>
var socket = io();
var init = function() {
var dm = window.dataModel = new ht.DataModel(),
sm = dm.sm(),
g3d = new ht.graph3d.Graph3dView(dm);
g3d.setGridSize(100);
g3d.setGridGap(100);
g3d.setGridVisible(true);
g3d.addToDOM(); var moveNodes = null;
g3d.mi(function(evt){
if ( evt.kind === 'beginMove'){
moveNodes = sm.getSelection();
}
else if (evt.kind === 'betweenMove'){
moveMap = {};
g3d.sm().each(function(data){
if(data instanceof ht.Node){
moveMap[data.getTag()] = data.p3();
console.info(data.p3());
}
});
socket.emit('moveMap', moveMap);
}
else if (evt.kind === 'endMove') {
moveNodes = null;
}
}); socket.on('ready', function(json) {
dm.clear();
dm.deserialize(json);
}); socket.on('result', function (result) {
for(var id in result){
var data = dm.getDataByTag([id]);
if (!data)
continue;
if (moveNodes && moveNodes.indexOf(data) >= 0)
continue;
data.p3(result[id]);
}
});
};
</script>
</head>
<body onload="init();"> </body>
</html>

代码并不长,我来介绍下具体的实现。首先是创建 3D 拓扑图组件,并做一些设置,让场景上出现线条,然后就是监听拓扑图上面的操作,当监听到 betweenMove 时,或许当前被移动的节点位置信息,向服务器派发该信息;接下来是监听服务器的 ready 事件,在事件回调中做了反序列化的操作,但是在反序列化之前,为什么要将场景中的所有节点 Clear 掉呢?是因为页面有可能是断线重连,如果是断线重连的话,没有将场景中的节点都 Clear 掉的话,反序列化后就会有节点重叠了,而且 Tag 属性也不再是唯一的了,所以这时候操作节点的话,将会很混乱;最后呢,就是监听服务器的 result 事件,在事件的回调中,跟新回调参数中对应节点的位置信息,但是其中做了些过滤,这是过滤正在移动的节点,因为正在移动的节点位置是认为控制的,所有不需要更新其节点位置信息。

那么实时数据通讯系列到这里就介绍完了,如有什么问题,欢迎批评指正。

基于 WebSocket 实现 WebGL 3D 拓扑图实时数据通讯同步(二)的更多相关文章

  1. 基于 WebSocket 实现 WebGL 3D 拓扑图实时数据通讯同步(一)

    今天没有延续上一篇讲的内容,穿插一段小插曲,WebSocket 实时数据通讯同步的问题,今天我们并不是很纯粹地讲 WebSocket 相关知识,我们通过 WebGL 3D 拓扑图来呈现一个有趣的 De ...

  2. 通过 WebSocket 实现 WebGL 3D 拓扑图实时数据通讯同步(二)

    我们上一篇<基于 WebSocket 实现 WebGL 3D 拓扑图实时数据通讯同步(一)>主要讲解了如何搭建一个实时数据通讯服务器,客户端与服务端是如何通讯的,相信通过上一篇的讲解,再配 ...

  3. Socket.IO – 基于 WebSocket 构建跨浏览器的实时应用

     Socket.IO 是一个功能非常强大的框架,能够帮助你构建基于 WebSocket 的跨浏览器的实时应用.支持主流浏览器,多种平台,多种传输模式,还可以集合 Exppress 框架构建各种功能复杂 ...

  4. 基于 WebSocket 构建跨浏览器的实时应用

    Socket.IO – 基于 WebSocket 构建跨浏览器的实时应用 Socket.IO 是一个功能非常强大的框架,能够帮助你构建基于 WebSocket 的跨浏览器的实时应用.支持主流浏览器,多 ...

  5. Sqlserver2000联系Oracle11G数据库进行实时数据的同步

    Sqlserver2000联系Oracle11G数据库进行实时数据的同步 1,前提条件 我有sqlserver2000环境,已经存在oracle11g环境,准备这两个数据库,建立各自的訪问账号,两者之 ...

  6. 基于Kafka Connect框架DataPipeline在实时数据集成上做了哪些提升?

    在不断满足当前企业客户数据集成需求的同时,DataPipeline也基于Kafka Connect 框架做了很多非常重要的提升. 1. 系统架构层面. DataPipeline引入DataPipeli ...

  7. 基于 HTML5 的 WebGL 3D 档案馆可视化管理系统

    前言 档案管理系统是通过建立统一的标准以规范整个文件管理,包括规范各业务系统的文件管理的完整的档案资源信息共享服务平台,主要实现档案流水化采集功能.为企事业单位的档案现代化管理,提供完整的解决方案,档 ...

  8. 基于 HTML5 的 WebGL 3D 智能楼宇监控系统

    前言 智能监控的领域已经涉及到了各大领域,工控.电信.电力.轨道交通.航天航空等等,为了减少人员的消耗,监控系统必不可少.之前我写过一篇 2D 的智能地铁监控系统广受好评,突然觉得,既然 2D 的这么 ...

  9. 根据矩阵变化实现基于 HTML5 的 WebGL 3D 自动布局

    在数学中,矩阵是以行和列排列的数字,符号或表达式的矩形阵列,任何矩阵都可以通过相关字段的标量乘以元素.矩阵的主要应用是表示线性变换,即f(x)= 4 x等线性函数的推广.例如,旋转的载体在三维空间是一 ...

随机推荐

  1. 让 windows 下的命令行程序 cmd.exe 用起来更顺手

    在 Windows 下使用 Larave 框架做开发,从 Composer 到 artisan 总是避免不了和 cmd.exe 打交道,系统默认的命令行界面却是不怎么好看,且每行显示的字符数是做了限制 ...

  2. 获取Canvas当前坐标系矩阵

    前言 在我的另一篇博文 Canvas坐标系转换 中,我们知道了所有的平移缩放旋转操作都会影响到画布坐标系.那在我们对画布进行了一系列操作之后,怎么再知道当前矩阵数据状态呢. 具体代码 首先请看下面的一 ...

  3. 【NLP】前戏:一起走进条件随机场(一)

    前戏:一起走进条件随机场 作者:白宁超 2016年8月2日13:59:46 [摘要]:条件随机场用于序列标注,数据分割等自然语言处理中,表现出很好的效果.在中文分词.中文人名识别和歧义消解等任务中都有 ...

  4. 工行ICBC_WAPB_B2C支付接口

    一. 前期准备 手机银行(WAP)B2C在线支付接口说明V1.0.0.6.doc 手机银行移动生活商户及门户网站js接口API.doc 支付组件ICBCEBankUtil.dll和infosecapi ...

  5. MSYS2环境下编译X265

    HEVC(High Efficiency Video Coding),是一种新的视频压缩标准.可以替代H.264/ AVC编码,使得保持相同质量的情况下,体积减少40%左右.目前有多种实现版本,x26 ...

  6. java观察者模式

      像activeMQ等消息队列中,我们经常会使用发布订阅模式,但是你有没有想过,客户端时如何及时得到订阅的主题的信息?其实就里就用到了观察者模式.在软件系统中,当一个对象的行为依赖于另一个对象的状态 ...

  7. 【JS基础】循环

    for 循环的语法: for (语句 1; 语句 2; 语句 3) { 被执行的代码块 } 语句 1 在循环(代码块)开始前执行 语句 2 定义运行循环(代码块)的条件 语句 3 在循环(代码块)已被 ...

  8. 从贝叶斯到粒子滤波——Round 1

    粒子滤波确实是一个挺复杂的东西,从接触粒子滤波到现在半个多月,博主哦勒哇看了N多篇文章,查略了嗨多资料,很多内容都是看了又看,细细斟酌.今日,便在这里验证一下自己的修炼成果,请各位英雄好汉多多指教. ...

  9. shell之sort命令

    1 sort的工作原理 sort将文件的每一行作为一个单位,相互比较,比较原则是从首字符向后,依次按ASCII码值进行比较,最后将他们按升序输出. [rocrocket@rocrocket progr ...

  10. ubuntu+mono+jexus 搭建.net的web平台 实现.net跨平台

    准备工作: vmware 用来安装 ubuntu 下载地址:VMware-workstation-9.0.1-894247.exe.tar 注册码: 1A4P8-DMK0N-FZ431-7K8NH-2 ...