拓扑图线条流动效果

jopen 10年前

        图论中边是重要元素,它连接各个顶点构成拓扑图,有向图中,边具有方向性,在画布中表现为箭头,在实际应用中,边可以代表链路,链路 上不只是有方向,还有流量,信号种类等信息,光用箭头表现力就不够了,可增加线条线型,以及流动效果来体现,这里介绍 Qunee 1.6 中线条流动效果的实现 拓扑图线条流动效果

        虚线流动效果

        虚线流动效果在 连线示例中有演示,使用虚线偏移量样式,不断增大,实现线条的流动

        虚线流动代码

var offset = ;var index = ;var timer = setInterval (function (){      offset += -1;//    edge2.setStyle (Q.Styles.EDGE_LINE_DASH_OFFSET, offset);           edge1.setStyle (Q.Styles.EDGE_LINE_DASH_OFFSET, offset);      index++;      index = index%20;      edge2.setStyle (Q.Styles.ARROW_TO_OFFSET, -0.3 -0.02 * (20 - index));      edge1.setStyle (Q.Styles.ARROW_FROM_OFFSET, {x: 0.3 + 0.02 * (20 - index), y: -10});  }, 150);

        运行效果

拓扑图线条流动效果

 

        定制流动效果

        对于更高级的流动需求,需要定制来实现,原理是在线条上挂载一个小图标,让这个图标沿线移动,从而形成动画效果

        实现代码

        下面的代码实现在线条上移动小图标

var line = graph.createShapeNode ("Line");  line.moveTo (-100, );  line.lineTo (200, );  line.curveTo (300, , 300, 100, 200, 100);  line.lineTo (, 100);  line.closePath ();  line.setStyle (Q.Styles.SHAPE_STROKE_STYLE, "#2898E0");  line.setStyle (Q.Styles.SHAPE_LINE_DASH, [8, 5, 0.1, 6]);  line.setStyle (Q.Styles.SHAPE_STROKE, 3);  line.setStyle (Q.Styles.LINE_CAP, "round");  line.setStyle (Q.Styles.SHAPE_OUTLINE_STYLE, "#fcfb9b");var ui = new Q.ImageUI ("images/flow.png");  ui.position = {x: , y: };  ui.size = {width: 20};  ui.renderColor = "#F00";  line.addUI (ui);    setTimeout (function A (){      var x = ui.position.x + 20;      ui.position = {x: x % (ui.parent.length || 1), y: };      line.invalidate ();      setTimeout (A, 300);  }, 100)

        运行效果 

拓扑图线条流动效果

        进一步封装

        上面的实现太随意,实际使用不太方便,可以进一步封装成专门用于流动支持的类,这样可以通过一个定时器实现所有的流动支持,我们创建一个 FlowingSupport 的类,详细代码如下

        FlowingSupport 类代码

 function FlowingSupport (graph) {      this.flowMap = {};      this.graph = graph;  }  FlowingSupport.prototype = {      flowMap: null,      length: ,      gap: 40,      graph: null,      addFlowing: function (edgeOrLine, count, byPercent) {          var flowList = this.flowMap[edgeOrLine.id];          if(!flowList){              flowList = this.flowMap[edgeOrLine.id] = [];              this.length++;          }          count = count || 1;          while(--count >= ){              var ui = new Q.ImageUI ("network/images/flow.png");              ui.position = {x: , y: };              ui.size = {width: 20};              ui.renderColor = "#F00";              flowList.push (ui);              flowList.byPercent = byPercent;              edgeOrLine.addUI (ui);          }      },      removeFlowing: function (id){          var flowList = this.flowMap[id];          if(!flowList){              return;          }          var edgeOrLine = this.graph.getElement (id);          if(edgeOrLine){              flowList.forEach (function (ui){                  edgeOrLine.removeUI (ui);              })          }          this._doRemove (id);      },      _doRemove: function (id){          delete this.flowMap[id];          this.length--;      },      timer: null,      perStep: 10,      stop: function (){          clearTimeout (this.timer);      },      start: function (){          if(this.timer){              clearTimeout (this.timer);          }          var offset = ;          var scope = this;          scope.timer = setTimeout (function A () {              if (!scope.length) {                  scope.timer = setTimeout (A, 2000);                  offset = ;                  return;              }              offset += 1;              for(var id in scope.flowMap){                  var ui = scope.graph.getUI (id);                  if(!ui){                      scope._doRemove (id);                      continue;                  }                  var lineLength = ui.length;                  if(!lineLength){                      continue;                  }                  var flowList = scope.flowMap[id];                  if(flowList.byPercent){                      //按百分比,0 - 1 跑完整条线,线长度不同,速度也不同,跑完一圈的时间相同 var x = offset * 2;                      var gap = 15;                      scope.flowMap[id].forEach (function (ui){                          ui.position = {x: (x % 100) / 100, y: };                          x += gap;                      });                  }else{                      //按固定距离移动,速度相同,线条越长跑完一圈的时间越长 var x = offset * scope.perStep;                      scope.flowMap[id].forEach (function (ui){                          ui.position = {x: x % lineLength, y: };                          x += scope.gap;                      });                  }                  scope.graph.invalidateUI (ui);                    //dashed line var data = ui.data;                  if(data instanceof Q.Edge){                      if(data.getStyle (Q.Styles.EDGE_LINE_DASH)){                          data.setStyle (Q.Styles.EDGE_LINE_DASH_OFFSET, -offset);                      }                  }else if(data instanceof Q.ShapeNode){                      if(data.getStyle (Q.Styles.SHAPE_LINE_DASH)) {                          data.setStyle (Q.Styles.SHAPE_LINE_DASH_OFFSET, -offset);                      }                  }              }              scope.timer = setTimeout (A, 200);          }, 200);      }  }

        使用如下

        这里的第二个参数为图标数量,第三个参数为是否按百分比流动,如果按百分比流动,每次移动的距离为线条长度的百分之二,这意味着不同长度的线条流动一圈,花费的时间相同

 var flowingSupport = new FlowingSupport (graph);  flowingSupport.addFlowing (edge, 3);  flowingSupport.addFlowing (edge2, 1);  flowingSupport.addFlowing (line, 1, true);  flowingSupport.addFlowing (line2, 2, true);    graph.callLater (function (){      flowingSupport.start ();  })

        运行效果

拓扑图线条流动效果

        在线演示

        http://demo.qunee.com/#Flowing%20Demo