zoukankan      html  css  js  c++  java
  • TWaver HTML5 + Node.js + express + socket.io + redis(五)

    接上一回TWaver HTML5 + Node.js + express + socket.io + redis(四), 这一篇您将了解到

    1. 如何保存更改后的拓扑数据 (包括新增的, 修改的, 删除的)
    2. 如何广播更改后的拓扑数据 (仅仅广播更改的数据)

    下面是mac和iphone上的效果图, mac或iphone上的修改都将及时互相同步:

    一. 先来看后台如何实现

    后台需要做两件事情: 保存修改以及广播修改; 其中修改又分为是新增, 修改, 还是删除. 保存修改很容易, 无非就是对数据库的增删改, 广播数据也很容易, 调用Socket.emit之前, 先设置广播标记就ok了: Socket.broadcast.emit. 而且这个广播只会通知其他客户端, 发送这个广播的客户端不会收到广播, 这正好是我们需要的, 所以后台代码就好写了:

    添加保存数据的socket.io监听, 里面保存数据后, 广播之:

     1     //保存数据
    2 client.on('saveData', function (datas) {
    3 if (!datas) {
    4 return;
    5 }
    6 //保存新增网元
    7 save(datas.add);
    8 //保存修改网元
    9 save(datas.change);
    10 //删除网元
    11 remove(datas.remove);
    12 //广播更新
    13 client.broadcast.emit('broadcast', datas);
    14 });

    保存数据的函数如下:

     1 //保存网元
    2 function save (datas) {
    3 if(!datas){
    4 return;
    5 }
    6 var elements = {};
    7 for (var i=0,n=datas.length,data; i<n; i++) {
    8 data = datas[i];
    9 elements[data.id] = JSON.stringify(data);
    10 }
    11 redis.hmset('datas', elements);
    12 };
    13
    14 //删除网元
    15 function remove (datas) {
    16 if(!datas){
    17 return;
    18 }
    19 var ids = [];
    20 for (var i=0,n=datas.length; i<n; i++) {
    21 ids.push(datas[i].id);
    22 }
    23 redis.hdel('datas', ids);
    24 };

    二. 前台实现

    分两步: 监听数据的增删改并自动保存, 响应广播更新
    1. 监听数据的增删改并自动保存

    TWaver HTML5的数据模型提供了各种监听器, 以便在数据更改后做响应处理. 其中:
    DataBox.addDataPropertyChangeListener用于监听数据容器里数据的属性变化, 也即对数据的修改, 其回调函数的参数包含属性: property(发生变化的属性), oldValue(旧值), newValue(新值), source(发生变化的数据)

    DataBox.addDataBoxChangeListener用于监听数据容器的变化, 也即数据的添加, 删除以及清空, 其回调函数的参数包含属性: kind(容器变化类型, 可选值为add(新增), remove(删除), clear(清空)), data(新增或删除的数据), datas(清空的数据集合)

    这里实现保存数据的思路是:
    i> 如果有网元被修改, 则将被修改的网元加上change标记, 其中判断isChanging标志很重要, 因为需要用此标志区分是用户更改还是响应广播更新(也即程序更新)

     1     //添加网元属性更改监听器
    2 box.addDataPropertyChangeListener(function (e) {
    3 //如果正在响应广播更新,则返回
    4 if (isChanging) {
    5 return;
    6 }
    7 //如果属性为add, change 则返回
    8 if (e.property === 'C:add' || e.property === 'C:change') {
    9 return;
    10 }
    11 //设置保存数据标记
    12 needSave = true;
    13 //将有属性更改的节点置上change标记
    14 e.source.setClient('change', true);
    15 });

    ii> 如果有新增网元, 则将新增网元加上add标记, 如果有网元被删除, 则存入map中

     1     //添加数据容器更改监听器
    2 box.addDataBoxChangeListener( function (e) {
    3 //如果正在响应广播更新,则返回
    4 if (isChanging) {
    5 return;
    6 }
    7 //设置保存数据标记
    8 needSave = true;
    9 if (e.kind === 'add') {
    10 //将新增的节点置上add标记
    11 e.data.setClient('add', true);
    12 } else if (e.kind === 'remove') {
    13 //如果网元没有新增标记, 则存储到被删除网元列表中
    14 if (!e.data.getClient('add')) {
    15 //存储被删除网元
    16 removedElements[e.data.getId()] = e.data;
    17 }
    18 }
    19 });

    iii> 启动定时器, 保存更改
    定时器代码如下:

     1 //自动保存
    2 function setAutoSave (value) {
    3 if(value){
    4 //定时器,每隔1秒钟保存修改
    5 timer = setInterval(function(){
    6 save();
    7 }, 1000);
    8 }else{
    9 window.clearTimeout(timer);
    10 }
    11 }

    保存数据代码如下, 需要注意的是, 得到要添加和被修改的网元后, 需要将其add和change标记置为false, 以避免重复保存:

     1 //保存数据
    2 function save () {
    3 //如果无数据添加、修改、删除,则直接返回
    4 if(!needSave){
    5 return;
    6 }
    7
    8 var add = [];
    9 var change = [];
    10 var remove = [];
    11 isChanging = true;
    12 box.forEach(function(data){
    13 //新增的网元
    14 if(data.getClient('add')){
    15 add.push(toData(data));
    16 }
    17 //修改的网元
    18 else if(data.getClient('change')){
    19 change.push(toData(data));
    20 }
    21 //清除网元新增和修改标记
    22 data.setClient('add', false);
    23 data.setClient('change', false);
    24 });
    25 isChanging = false;
    26 //删除的网元
    27 for(var id in removedElements){
    28 remove.push(toData(removedElements[id]));
    29 }
    30
    31 if(add.length == 0 && change.length == 0 && remove.length == 0){
    32 return;
    33 }
    34
    35 //初始化待删除数据
    36 removedElements = {};
    37 //还原保存数据标记
    38 needSave = false;
    39 //构造更新数据
    40 var datas = {};
    41 if(add.length != 0){
    42 datas.add = add;
    43 }
    44 if(remove.length != 0){
    45 datas.remove = remove;
    46 }
    47 if(change.length != 0){
    48 datas.change = change;
    49 }
    50 socket.emit('saveData', datas);
    51 }

    从网元获取要持久化的数据代码如下:

     1 //从网元获取持久化数据
    2 function toData (element) {
    3 if(element instanceof twaver.Node) {
    4 return {
    5 id: element.getId(),
    6 name: element.getName(),
    7 location: element.getLocation()
    8 };
    9 } else {
    10 return {
    11 id: element.getId(),
    12 name: element.getName(),
    13 from: element.getFromNode() ? element.getFromNode().getId() : null,
    14 to: element.getToNode() ? element.getToNode().getId() : null
    15 };
    16 }
    17 }

    2. 响应广播更新

    分为新增, 删除以及修改三种情况, 其中很重要的是在做这些事情之前先设置isChanging为true, 以防止重复广播更新

     1 //响应广播更新
    2 function onBroadcast (data) {
    3 if (!data) {
    4 return;
    5 }
    6 //设置响应广播更新标记,防止重复更新
    7 isChanging = true;
    8 var i, c, add=data.add, change=data.change, remove=data.remove;
    9 //添加节点
    10 if (add) {
    11 for (i=0,c=add.length; i<c; i++) {
    12 if (add[i].from) {
    13 addLink(add[i]);
    14 } else {
    15 addNode(add[i]);
    16 }
    17 }
    18 }
    19 //修改节点
    20 if (change) {
    21 for (i=0,c=change.length; i<c; i++) {
    22 changeData(change[i]);
    23 }
    24 }
    25 //删除节点
    26 if (remove) {
    27 for (i=0,c=remove.length; i<c; i++) {
    28 removeData(remove[i]);
    29 }
    30 }
    31 //还原更新广播数据
    32 isChanging = false;
    33 }

    增删改网元代码如下:

     1 //添加节点
    2 function addNode (data) {
    3 //构造节点
    4 var node = new twaver.Node(data);
    5 //添加节点
    6 box.add(node);
    7 }
    8
    9 //添加节点
    10 function addLink (data) {
    11 //查找from节点
    12 var from = box.getDataById(data.from);
    13 //查找to节点
    14 var to = box.getDataById(data.to);
    15 //构造连线
    16 var link = new twaver.Link({id: data.id, name: data.name}, from, to);
    17 //添加连线
    18 box.add(link);
    19 }
    20
    21 //修改网元
    22 function changeData (data) {
    23 var element = box.getDataById(data.id);
    24 //修改节点
    25 if (element instanceof twaver.Node) {
    26 element.setLocation(data.location.x, data.location.y);
    27 element.setName(data.name);
    28 }
    29 //修改连线
    30 else if (element instanceof twaver.Link) {
    31 element.setName(data.name);
    32 element.setFromNode(box.getDataById(data.from));
    33 element.setToNode(box.getDataById(data.to));
    34 }
    35 //网元被删除
    36 else {
    37 //网元被删除, 无需修改
    38 }
    39 }
    40
    41 //删除网元
    42 function removeData (data) {
    43 box.removeById(data.id);
    44 }

    最后, 附上本文的完整demo:TWaverHTML5Demo








  • 相关阅读:
    Unity3D Editor模式下批量修改prefab
    3D touch在Unity3D中的使用
    Unity中的协程是什么?
    Unity3D脚本调用Objective C代码实现游戏内购买
    WindowsPhone8拍照功能实现简介
    WindowsPhone App如何扩展能够使用的内存
    SVN 提交代码时提示文件已经存在解决办法
    iOS检查App新版本并更新新版本
    iOS存储数据字典到沙盒
    统计整个Xcode工程代码行数
  • 原文地址:https://www.cnblogs.com/twaver/p/2380731.html
Copyright © 2011-2022 走看看