学习 kityminder (十六) 连线 connect

今天学习下 kityminder 中 connect 部分, 我觉得它表示节点间的连线实现. 以前看的时候略过了它.
首先看下 core/connect.js:

// 连线提供方. 看起来是可以 `注册' 连线的 name->provider.
// 在 src/connect 目录下有大约 7 种连线 provider.
var _connectProviders = {};

// 注册 name->provider. 这里 provider 是一个函数.
exports.register = function(name, provider) {
  ... 将 name->provider 放到集合 _connectProviders 中.
}
// 这里会注册一个缺省的(default)连线提供方, 我们稍后研究.

// 扩展方法到类 MinderNode
extend class MinderNode {
  getConnect(): 获取当前节点的连线类型. 值放在 .data.connect 中, 缺省为 'default'.
  getConnectProvider(): 根据 getConnect() 得到 name, 然后 -> provider.
  getConnection(): 获取当前节点的连线对象. 是一个 svg <path> 的 kity.Path 包装.
}

// 扩展方法到类 Minder
extend class Minder {
  getConnectContainer(): ?可能是获得一个 <g> 容器, 其容纳所有 <path> 连线.

  // 为节点 node 创建连线 connection 对象.
  createConnect(node): {
    if (node is root) return;  // 根节点不创建.
    var conn = new kity.Path(); // 即 svg <path> 对象
    node._connection = conn;    // 可认为是 node.setConnection().

    this._connectContainer.addShape(conn);  // 添加到 <g> 中.
    this.updateConnect(node);
  }

  removeConnect(node): 删除 node 所有子孙节点的连线对象.

  updateConnect(node): 稍后研究.
}

 

在浏览器的调试控制台中观察一幅脑图的 HTML, 的确可以找到 <g id='minder_connect_group1'> 的 svg
元素, 它应该就是 minder.getConnectContainer() 获得的那个元素. 连线使用 svg <path> 实现, 所有连线
都放在 <g> 容器中展现.

每个子节点(即除了根节点) 都有(且只有)一条连线 连接到其父节点. 估计连线的形状 (即 path 中的 d 属性/数据)
由连线提供者 (provider) 负责计算出来. 这里猜测不对没关系, 下面改正就是了, (俺)脸皮就要厚一点!

这不, 又观察了一下, 天盘图中子节点的连线是连接到其前一个兄节点, 第一个子节点连接其父节点! 瞬间推翻估计...

不论连接到哪里, 子节点 (node) 中一定有一个属性 ._connection 用于记录一个 kity.Path 对象表示此连线.
其通过 get/setConnection() 访问; 另一个属性 .data.connect 记录连线的 provider 类型(也可叫连线形式?).
因此合理猜测 minder.updateConnect(node) 是用该 node 的 connect provider 去计算出 connection
(连线) 的路径数据, 好用 svg 显示出一条曲线(或"直的"曲线). 下面分析 updateConnect() 函数.

minder.updateConnect = function(node) {
  var conn = node.getConnection();  // 得到连线对象, 可认为是 svg<path>
  if (!conn) return;  // 没有则不处理, 合理检查.
  
  var parent = node.parent;  // 可认为是 node 的连接目标.
  if (!parent) return;  // 没有父节点则不处理, 看似合理的检查.
  
  if (parent 是收起状态, 此时子节点不可见) 则设置连线不可见, return;

  var provider = node.getConnectProvider();  // 获取 provider, 是一个函数.

  // 这里 display-style 是这个连线的显示样式, 先略去; 
  // 调用函数, 看参数相关的3方有 start=node, end=parent, conn=connection.
  provider(node, parent, connection, ...display-style);
  
  // 后面的显示样式部分略.
}

 

这个函数可以分为两部分, 前面获取计算连线所需的3方信息:
   1. 开始位置 start = 本节点 node,
   2. 结束位置 end = 目标节点 parent,
   3. 连线本身 connection, 计算出的 <path d> 数据肯定放这里.

下面以 default provider 为例, 分析该 provider 函数都做了什么:

// 根据上面的分析, 现在知道这三个参数表示 3方对象.
default-connect-provider = function(node, parent, connection) {
  connection.setPathData([
    'M', parent.getLayoutVertexOut(),
    'L', node.getLayoutVertexIn()
  ]);
}

从底层开始分析起, parent.getLayoutVertexOut() 看名字是父节点 out 顶点位置. 以前在研究 layout 的时候
碰到过, 但那时没有深入进去看 vertex-out, in 的概念含义. 让我们在浏览器 console 中为 root 等节点调用
一下该函数看看结果, 该结果是一个 Point 点对象. 根据图上观察, root 的 vertex-out 点位于中心, 子节点的这个
vertex-in 点位于左侧中间(如果此节点布局在右侧的话), 即 vertex 位置与布局有关, 那么下次第N次回顾布局的
时候可以更仔细的分析下 vertex 位置是如何计算的, 现在就让我们先假设它们都已经完美地计算出来即可.

这样我们就知道调用 .setPathData() 函数的参数型为 ['M', out-point, 'L', in-point], 查书或网络根据 svg
的知识, 我们知道 'M' 是 MoveTo 的意思, 'L' 是 LineTo 的意思, 则我们知道 'default' 缺省连线是用直线
连接 parent->node.

实验: 随便弄一个脑图, 在浏览器 console 中执行一些的简单的 js:
1. var root_node = minder.root;  // 得到脑图的根节点
2. var child_0 = root_node.children[0];  // 得到脑图的第一个字节点. 得到其它子节点也没问题.
3. child_0.getConnect()  // 看看该子节点使用的 connect provider 是什么, 结果是 'arc'
4. 为测试 'default' provider 我们造假: child_0.getConnect = function() { return 'default'; }
   让其总是放回 'default'.
5. 挪动一下该子节点, 结果如下图:

学习 kityminder (十六) 连线 connect_第1张图片

 

我们还可以继续造假将 child_0, 或者 1,2,3 等子节点的 connect provider 设置为别的实验. 这里就不做了.
下面继续看代码, 位于 src/connect 下的几种 connect provider: arc, arc_tp, bezier, fish-bone-master,
l, poly, under 共 7 种. 大概看看吧, 没那么多时间(闲心).

arc 是圆弧连线, 如图的弧状连线的那些子节点的 connect 就是 'arc'.

// src/connect/arc.js
// 这里创建了 svg 的 path marker 对象.
var connectMarker = new kity.Marker().pipe(function() {
  // 初始化这种 marker 对象是一个小的 circle.
  // 这里需要看 svg 的书细节了解 marker 的概念与实现.
  ...
}

// 任务是建立一个 parent->node 的弧形连线. 可按照 node 所在四个象限分别讨论.
register-provider 'arc'-> function(node, parent, connection) {
  var side = left or right 根据 node,parent 相对位置;
  var start = 计算开始点位置, 
      end = 计算结束点位置, 和 side 有关;
  var path = ['MoveTo', start,
              'ArcTo', ... end];  // 弧线的 path 数据, 格式查 svg 文档.
 
  connection.setPathData(path);  // 设置 <path> 数据.
  connection.setMarker(connectMarker); // 为曲线设置小圆点 marker .
}

 

这里设置的 marker 根据图上看是在 path 的结束点. 领导说想动画显示从 start->end 的那个点, 那是不是我们
改改 marker 就能实现?

再以鱼骨图连线 fish-bone-master.js 为例, 上面的 path 大致为:
    path-data = ['MoveTo', start-pt, 'horz-line-to?', dxy?, 'LineTo', end-pt];

其它 connect 估计差不多主要是各种不同的计算 path-data 算法, 这里需要细心+测试+时间, 没时间细看的
就先略过了.

最后, 有一个小小灵感, 这里天盘图都是右旋的, 也许我们还能整一个左旋版本的, 仿佛镜像世界来的~

你可能感兴趣的:(学习 kityminder (十六) 连线 connect)