在绘制一些常见图形的时候,比如关系图,单个结点或连线并不难,麻烦的是位置的计算等,和图形模块不同,布局就是专门计算一些特殊图形位置的模块,用一句通俗的话说就是:决定什么元素绘制在哪里。因此,布局应该和具体的绘图方法无关,她只关心位置的计算。
下面,我们将通过介绍一个最简单的树图的绘制过程来说明布局的设计理念,让我们开始吧!
$$('<svg>' +
// 绘制连线
'<g class="line"></g>' +
// 绘制结点
'<g class="node"></g>' +
// 绘制文字
'<g class="text"></g>' +
'</svg>').appendTo('body')
.attr('width', '400')
.attr('height', '400');
右图是最终运行结果,上面我们准备好了画布,需要绘图的原始数据如下:
var nodes = [
// 结点名称、父节点名称
["手绘", null],
["水粉", "手绘"],
["油画", "手绘"],
["素描", "手绘"],
["中国画", "手绘"],
["空间透视", "素描"],
["色彩五大调", "素描"],
];
使用布局绘图的第一步是创建布局对象,这里是创建tree布局对象:
// 创建布局对象 var tree = clay.treeLayout();
理论上来说,原始数据可以是任意格式,只要保证提供的解析方法是有效的即可。这主要是考虑到实际开发复杂的环境,因此,提供了数据格式配置接口,对于上面的数据,进行如下配置:
tree
// 获取根结点
.root(function (initTree) {
return initTree[0];
})
// 获取孩子结点
.child(function (parentTree, initTree) {
var children = [], i;
for (i = 0; i < initTree.length; i++) {
if (initTree[i][1] == parentTree[0])
children.push(initTree[i]);
}
return children;
})
// 获取结点标志id
.id(function (treedata) {
return treedata[0];
})
不同布局需要配置的项不一样,对于tree布局而言,关于数据结构的部分,只需要告诉她如何根据父结点获取子结点,一个结点的唯一标识怎么确定,根节点是谁。
到这里,所有必须的配置都写好了,添加下面这行代码,启动计算:
var result=tree(nodes);
至此,每个结点的位置就计算出来了,具体的绘图和布局无关,不再说明!
因为布局不会知道最终绘制的图形具体是什么样子,比如这里的树布局,也许你想绘制的是旋转的树或倒树,也可能你就是想要和这里一样的简单树。为了防止问题复杂化,布局在计算位置的时候,都会统一选择一种最朴素的场景作为计算模型,从该模型出发,任何别的模型借助clay.js提供的一些计算方法实现起来就很容易了。
右图是tree布局的计算模型。右边的每个红色矩形都是一个1x1的正方形,坐标原心位于左上角绿色顶点。
配置具体绘图方式的时候,其中第一个参数nodes就记录了每个结点经过布局计算后的位置信息,让我们打印一下其中的一条数据看看:
"油画":{
children: [];
data: (2) ["油画", "手绘"];
id: "油画";
left: 1.5;
pid: "手绘";
show: true;
top: 1.5
}
其中data记录着结点的原始数据,我们主要看看left和top,这显示该结点应该绘制的坐标为(1.5,1.5),对照右图,是不是就很清晰了。别的布局的设计思想也是如此,请耐心体会一下!