告别混乱连线:mxGraph自定义连接线全攻略

告别混乱连线:mxGraph自定义连接线全攻略

【免费下载链接】mxgraph mxGraph is a fully client side JavaScript diagramming library 【免费下载链接】mxgraph 项目地址: https://gitcode.com/gh_mirrors/mx/mxgraph

你是否还在为流程图中杂乱无章的连接线烦恼?是否需要实现符合业务逻辑的特殊连线样式?本文将从基础的mxConnector入手,逐步深入到复杂路由算法,帮助你掌握mxGraph中连接线的全自定义方案。读完本文,你将能够:定制箭头样式、实现弯曲路径、应用正交布局、解决连线交叉问题,以及开发自己的路由算法。

核心概念与基础架构

mxGraph作为完全客户端的JavaScript图表库,其连接线系统建立在几个核心组件之上。mxConnector是所有连接线的基础类,定义了线的绘制、箭头标记和基本样式。布局系统则通过mxGraphLayout控制整体连线分布,确保图表的可读性。

mxGraph架构

官方文档:docs/manual.html 核心布局类:javascript/src/js/layout/mxGraphLayout.js

mxConnector基础结构

mxConnector类继承自mxPolyline,主要负责连接线的视觉呈现。其核心方法包括:

  • paintEdgeShape:绘制线条和箭头
  • createMarker:创建箭头标记
  • updateBoundingBox:更新边界框以适应线条和标记
// 简化的mxConnector核心结构
function mxConnector(points, stroke, strokewidth) {
  mxPolyline.call(this, points, stroke, strokewidth);
};

mxUtils.extend(mxConnector, mxPolyline);

mxConnector.prototype.paintEdgeShape = function(c, pts) {
  var sourceMarker = this.createMarker(c, pts, true);
  var targetMarker = this.createMarker(c, pts, false);
  
  mxPolyline.prototype.paintEdgeShape.apply(this, arguments);
  
  // 绘制箭头标记
  if (sourceMarker != null) sourceMarker();
  if (targetMarker != null) targetMarker();
};

源码位置:javascript/src/js/shape/mxConnector.js

自定义箭头与样式

连接线的视觉定制是提升图表专业性的关键。mxGraph提供了灵活的样式系统,允许通过CSS类或内联样式定义连接线的外观。

箭头样式定制

通过STYLE_STARTARROW和STYLE_ENDARROW样式可以定义连接线两端的箭头类型。系统内置了多种箭头样式,如箭头、菱形、圆形等,也支持自定义SVG路径。

连接线样式示例

// 定义带箭头和菱形标记的连接线样式
var style = graph.getStylesheet().getDefaultEdgeStyle();
style[mxConstants.STYLE_STARTARROW] = mxConstants.ARROW_CLASSIC;
style[mxConstants.STYLE_ENDARROW] = mxConstants.ARROW_DIAMOND;
style[mxConstants.STYLE_STROKEWIDTH] = 2;
style[mxConstants.STYLE_STROKECOLOR] = '#3498db';

箭头创建逻辑在createMarker方法中实现,该方法根据样式计算箭头位置和大小,并返回绘制函数:

mxConnector.prototype.createMarker = function(c, pts, source) {
  // 计算箭头位置、大小和方向
  // ...
  
  return mxMarker.createMarker(c, this, type, pe, unitX, unitY, size, source, this.strokewidth, filled);
};

线条样式与动画效果

除了箭头,还可以定制线条的颜色、宽度、虚线样式等。通过结合mxGraph的事件系统,还能实现悬停效果、点击高亮等交互效果。

// 虚线动画效果实现
style[mxConstants.STYLE_DASHED] = '1';
style[mxConstants.STYLE_DASH_PATTERN] = '5,5';

// 为连接线添加悬停效果
graph.addListener(mxEvent.MOUSE_OVER, function(sender, evt) {
  var cell = evt.getProperty('cell');
  if (graph.getModel().isEdge(cell)) {
    graph.setCellStyles(mxConstants.STYLE_STROKEWIDTH, 3, [cell]);
  }
});

graph.addListener(mxEvent.MOUSE_OUT, function(sender, evt) {
  var cell = evt.getProperty('cell');
  if (graph.getModel().isEdge(cell)) {
    graph.setCellStyles(mxConstants.STYLE_STROKEWIDTH, 2, [cell]);
  }
});

样式参考文档:docs/manual.html#Styles

路径自定义与弯曲算法

mxGraph支持多种路径类型,从简单的直线到复杂的曲线。通过STYLE_CURVED样式可以启用贝塞尔曲线,使连接线更加平滑自然。

弯曲连接线示例

弯曲路径实现

启用弯曲路径非常简单,只需设置STYLE_CURVED样式为1:

style[mxConstants.STYLE_CURVED] = 1;
style[mxConstants.STYLE_CURVE_FACTOR] = 0.6; // 控制弯曲程度,0-1之间

当启用弯曲路径时,mxConnector会使用SVG的贝塞尔曲线命令来绘制路径:

// 弯曲路径绘制逻辑简化版
if (this.style[mxConstants.STYLE_CURVED] == 1) {
  // 使用贝塞尔曲线生成平滑路径
  c.begin();
  this.addPoints(c, pts, this.isRounded, this.style);
  c.stroke();
}

自定义路径计算

对于特殊需求,可以重写mxConnector的paintEdgeShape方法,实现完全自定义的路径计算。例如实现一个遵循特定业务规则的连接线:

var MyConnector = function(points, stroke, strokewidth) {
  mxConnector.call(this, points, stroke, strokewidth);
};

mxUtils.extend(MyConnector, mxConnector);

MyConnector.prototype.paintEdgeShape = function(c, pts) {
  // 自定义路径计算逻辑
  c.begin();
  
  // 例如:实现一个先水平后垂直的L形路径
  c.moveTo(pts[0].x, pts[0].y);
  c.lineTo(pts[0].x + (pts[1].x - pts[0].x)/2, pts[0].y);
  c.lineTo(pts[0].x + (pts[1].x - pts[0].x)/2, pts[1].y);
  c.lineTo(pts[1].x, pts[1].y);
  
  c.stroke();
  
  // 绘制箭头标记
  this.createMarker(c, pts, false)();
};

// 注册自定义连接器
mxCellRenderer.registerShape('myConnector', MyConnector);

示例参考:javascript/examples/orthogonal.html

布局算法与路由优化

良好的布局是确保连接线清晰可读的关键。mxGraph提供了多种布局算法,如层次布局、圆形布局、正交布局等,可以根据不同的图表类型选择合适的布局。

布局系统基础

mxGraphLayout是所有布局算法的基类,定义了布局的基本接口:

function mxGraphLayout(graph) {
  this.graph = graph;
};

mxGraphLayout.prototype.execute = function(parent) {
  // 布局算法实现
};

mxGraphLayout.prototype.setVertexLocation = function(cell, x, y) {
  // 设置顶点位置
};

源码位置:javascript/src/js/layout/mxGraphLayout.js

常用布局算法

mxGraph提供了多种开箱即用的布局算法:

  • mxHierarchicalLayout:层次布局,适合流程图和组织结构图
  • mxOrthogonalLayout:正交布局,确保连接线为水平或垂直线
  • mxCircleLayout:圆形布局,适合关系图
  • mxCompactTreeLayout:紧凑树布局,适合树状结构

布局算法示例

// 应用正交布局
var layout = new mxOrthogonalLayout(graph);
layout.orientation = mxConstants.DIRECTION_EAST;
layout.spacing = 20;
layout.execute(graph.getDefaultParent());

布局示例:javascript/examples/hierarchicallayout.html

自定义路由算法

对于复杂场景,可以通过扩展mxGraphLayout实现自定义路由算法。关键是重写getEdgePoints方法来计算连接线的路径点。

var MyRoutingLayout = function(graph) {
  mxGraphLayout.call(this, graph);
};

mxUtils.extend(MyRoutingLayout, mxGraphLayout);

MyRoutingLayout.prototype.execute = function(parent) {
  var model = this.graph.getModel();
  
  model.beginUpdate();
  try {
    // 获取所有边
    var edges = [];
    var children = model.getChildren(parent);
    
    for (var i = 0; i < children.length; i++) {
      if (model.isEdge(children[i])) {
        edges.push(children[i]);
      }
    }
    
    // 为每条边计算路径
    for (var i = 0; i < edges.length; i++) {
      var edge = edges[i];
      var src = model.getTerminal(edge, true);
      var trg = model.getTerminal(edge, false);
      
      var srcGeo = model.getGeometry(src);
      var trgGeo = model.getGeometry(trg);
      
      // 计算自定义路径点
      var points = this.calculateCustomRoute(srcGeo, trgGeo);
      
      // 设置边的路径
      this.setEdgePoints(edge, points);
    }
  } finally {
    model.endUpdate();
  }
};

MyRoutingLayout.prototype.calculateCustomRoute = function(srcGeo, trgGeo) {
  // 实现自定义路径计算逻辑
  var points = [];
  
  // 例如:创建一个避开某些区域的路径
  points.push(new mxPoint(srcGeo.x + srcGeo.width/2, srcGeo.y + srcGeo.height/2));
  points.push(new mxPoint((srcGeo.x + trgGeo.x)/2, srcGeo.y + srcGeo.height/2));
  points.push(new mxPoint((srcGeo.x + trgGeo.x)/2, trgGeo.y + trgGeo.height/2));
  points.push(new mxPoint(trgGeo.x + trgGeo.width/2, trgGeo.y + trgGeo.height/2));
  
  return points;
};

实战案例:工作流程图优化

让我们通过一个实际案例来综合应用所学知识。假设我们需要创建一个工作流程图,要求:

  • 使用正交连接线
  • 避免连线交叉
  • 支持自定义箭头和颜色
  • 实现悬停高亮效果

工作流程图示例

完整实现代码:

<!DOCTYPE html>
<html>
<head>
  <title>自定义工作流程图</title>
  <script src="https://cdn.jsdelivr.net/npm/mxgraph@4.2.2/javascript/mxClient.min.js"></script>
  <style>
    .mxgraph {
      border: 1px solid #ccc;
      width: 100%;
      height: 600px;
    }
  </style>
</head>
<body>
  <div id="graphContainer" class="mxgraph"></div>
  
  <script>
    // 检查浏览器支持
    if (!mxClient.isBrowserSupported()) {
      mxUtils.error('浏览器不支持mxGraph', 200, false);
    } else {
      // 创建容器
      var container = document.getElementById('graphContainer');
      
      // 创建图表
      var graph = new mxGraph(container);
      
      // 启用网格
      graph.gridSize = 20;
      graph.setGridEnabled(true);
      
      // 配置样式
      var style = graph.getStylesheet().getDefaultEdgeStyle();
      style[mxConstants.STYLE_EDGE] = 'orthogonalEdge';
      style[mxConstants.STYLE_ENDARROW] = mxConstants.ARROW_CLASSIC;
      style[mxConstants.STYLE_STROKEWIDTH] = 2;
      style[mxConstants.STYLE_STROKECOLOR] = '#3498db';
      style[mxConstants.STYLE_FONTSIZE] = 12;
      
      // 自定义开始节点样式
      var startStyle = graph.getStylesheet().getDefaultVertexStyle();
      startStyle[mxConstants.STYLE_FILLCOLOR] = '#2ecc71';
      startStyle[mxConstants.STYLE_STROKECOLOR] = '#27ae60';
      startStyle[mxConstants.STYLE_ROUNDED] = 1;
      startStyle[mxConstants.STYLE_FONTCOLOR] = 'white';
      
      // 自定义结束节点样式
      var endStyle = mxUtils.clone(startStyle);
      endStyle[mxConstants.STYLE_FILLCOLOR] = '#e74c3c';
      endStyle[mxConstants.STYLE_STROKECOLOR] = '#c0392b';
      graph.getStylesheet().putCellStyle('end', endStyle);
      
      // 创建示例图
      var parent = graph.getDefaultParent();
      
      graph.getModel().beginUpdate();
      try {
        // 创建节点
        var v1 = graph.insertVertex(parent, null, '开始', 20, 20, 80, 40, 'shape=ellipse');
        var v2 = graph.insertVertex(parent, null, '处理数据', 200, 20, 100, 40);
        var v3 = graph.insertVertex(parent, null, '验证结果', 200, 120, 100, 40);
        var v4 = graph.insertVertex(parent, null, '生成报告', 200, 220, 100, 40);
        var v5 = graph.insertVertex(parent, null, '结束', 400, 120, 80, 40, 'shape=ellipse;style=end');
        
        // 创建连接
        graph.insertEdge(parent, null, '启动', v1, v2);
        graph.insertEdge(parent, null, '处理', v2, v3);
        graph.insertEdge(parent, null, '验证', v3, v4);
        graph.insertEdge(parent, null, '完成', v4, v5);
        graph.insertEdge(parent, null, '重试', v3, v2, 'strokeColor=#e67e22;endArrow=classic');
      } finally {
        // 应用布局
        var layout = new mxOrthogonalLayout(graph);
        layout.spacing = 40;
        layout.execute(parent);
        
        graph.getModel().endUpdate();
      }
      
      // 添加连接线悬停效果
      graph.addListener(mxEvent.MOUSE_OVER, function(sender, evt) {
        var cell = evt.getProperty('cell');
        if (cell && graph.getModel().isEdge(cell)) {
          graph.setCellStyles(mxConstants.STYLE_STROKEWIDTH, 3, [cell]);
        }
      });
      
      graph.addListener(mxEvent.MOUSE_OUT, function(sender, evt) {
        var cell = evt.getProperty('cell');
        if (cell && graph.getModel().isEdge(cell)) {
          graph.setCellStyles(mxConstants.STYLE_STROKEWIDTH, 2, [cell]);
        }
      });
    }
  </script>
</body>
</html>

完整示例:javascript/examples/swimlanes.html

高级技巧与性能优化

处理大量节点和连接

当图表包含大量元素时,连接线的计算可能会影响性能。可以通过以下方法优化:

  1. 使用缓存:缓存已经计算好的路径
  2. 分级渲染:根据缩放级别调整连接线的细节程度
  3. 异步布局:在Web Worker中执行复杂的布局计算
  4. 按需加载:初始只加载可见区域的连接线,滚动时再加载其他部分

解决连线交叉问题

连线交叉会降低图表的可读性,可以通过以下策略解决:

  1. 调整节点位置:使用mxGraphLayout的自定义约束
  2. 添加交叉点:通过插入额外的折点来避免交叉
  3. 层级路由:将不同层级的连接线放在不同Z轴位置
  4. 使用连接线优先级:为重要连接线分配更高优先级
// 调整布局约束避免交叉
layout.getConstraint = function(key, cell) {
  if (key === 'priority' && cell.id === 'critical-edge') {
    return 10; // 高优先级
  }
  return 1; // 默认优先级
};

实现动态连线调整

结合mxGraph的事件系统,可以实现连接线的动态调整,响应用户交互或数据变化:

// 节点移动时优化连接线
graph.addListener(mxEvent.CELL_MOVED, function(sender, evt) {
  var cell = evt.getProperty('cell');
  
  if (graph.getModel().isVertex(cell)) {
    // 获取与节点相连的所有边
    var edges = graph.getModel().getEdges(cell);
    
    // 重新路由这些边
    graph.getModel().beginUpdate();
    try {
      for (var i = 0; i < edges.length; i++) {
        graph.routeEdges([edges[i]]);
      }
    } finally {
      graph.getModel().endUpdate();
    }
  }
});

总结与展望

mxGraph提供了强大而灵活的连接线定制能力,从简单的样式修改到复杂的路由算法实现。通过mxConnector和mxGraphLayout的扩展,可以满足各种业务场景的需求。

mxGraph功能概览

未来发展方向:

  • 结合WebGL提升大规模图表的渲染性能
  • 引入机器学习算法优化自动路由
  • 增强AR/VR环境中的3D连接线支持

完整API文档:docs/js-api/index.html 更多示例:javascript/examples/

通过本文介绍的技术,你现在应该能够构建出专业、清晰的流程图和图表。无论是简单的连接线样式修改,还是复杂的自定义路由算法,mxGraph都提供了必要的基础组件和扩展机制。建议深入研究源码中的mxConnector和mxGraphLayout实现,以充分发挥mxGraph的潜力。

【免费下载链接】mxgraph mxGraph is a fully client side JavaScript diagramming library 【免费下载链接】mxgraph 项目地址: https://gitcode.com/gh_mirrors/mx/mxgraph

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值