clay教程二之布局的使用

做者:心叶
时间:2018-11-06 14:47node

舒适提示:clay.js已经中止维护,项目迁移到 https://github.com/yelloxing/...

喜欢本项目的能够在github上给给star。git

在绘制一些常见图形的时候,好比关系图,单个结点或连线并不难,麻烦的是位置的计算等,和图形模块不一样,布局就是专门计算一些特殊图形位置的模块,用一句通俗的话说就是:决定什么元素绘制在哪里。所以,布局应该和具体的绘图方法无关,她只关心位置的计算。github

下面,咱们将经过介绍一个最简单的树图的绘制过程来讲明布局的设计理念,让咱们开始吧!数组

绘制图形

图片描述

$$('<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布局对象:app

// 建立布局对象
var tree = clay.treeLayout();

理论上来讲,原始数据能够是任意格式,只要保证总体是一个数组便可。这主要是考虑到实际开发复杂的环境,所以,提供了数据格式配置接口,对于上面的数据,进行以下配置:svg

tree
// 获取根结点
.root(function (initTree) {
    return initTree[0];
})

// 获取孩子结点
.child(function (parentTree, initTree) {
    var children = [], i;
    for (i = 0; i < nodes.length; i++) {
        if (initTree[i][2] == parentTree[0])
            children.push(initTree[i]);
    }
    return children;
})

// 获取结点标志id
.id(function (treedata) {
    return treedata[0];
})

不一样布局须要配置的项不同,对于tree布局而言,关于数据结构的部分,只须要告诉她如何根据父结点获取子结点,一个结点的惟一标识怎么肯定,根节点是谁。布局

开头就说明的很清楚了,布局应该是和具体绘图方式无关的存在,所以,这里咱们选择了svg绘图,就须要再配置一下具体的绘图方法。this

tree.drawer(function (nodes, rootid, size) {
    var i, node;
    for (i in nodes) {
        node = nodes[i];

        // 绘制结点
        $$('<circle target=' + node.data[0] + ' r="3" fill="white" stroke="red" stroke-width="1"/>')
            .appendTo('.node')
            .attr('cx', node.left * 100)
            .attr('cy', node.top * 100);

        // 绘制文字
        $$('<text style="font-size:10px;">' + node.data[0] + '</text>')
            .appendTo('.text')
            .attr('x', node.left * 100)
            .attr('y', (node.top * 100 - 14));

        // 绘制连线
        if (node.pid)
            $$('<path source=' + node.data[1] + ' target=' + node.data[0] + ' stroke-width="1" stroke="gray" fill="none"></path>')
                .appendTo('.line')
                .attr('d',
                    'M' + nodes[node.pid].left * 100 + " " + nodes[node.pid].top * 100 + "C" +
                    (nodes[node.pid].left * 100 + 50) + "," + nodes[node.pid].top * 100 + " " +
                    (node.left * 100 - 50) + "," + node.top * 100 + " " +
                    node.left * 100 + "," + node.top * 100 + " ");
    }
});

到这里,全部必须的配置都写好了,添加下面这行代码,启动绘图:spa

tree(nodes);

至此,运行代码就能够看见一棵记录着结点关系的树图了,绘图结束!

计算模型

由于布局不会知道最终绘制的图形具体是什么样子,好比这里的树布局,也许你想绘制的是旋转的树或倒树,也可能你就是想要和这里同样的简单树。为了防止问题复杂化,布局在计算位置的时候,都会统一选择一种最朴素的场景做为计算模型,从该模型出发,任何别的模型借助clay提供的一些计算方法实现起来就很容易了。

图片描述

上图是tree布局的计算模型。右边的每一个红色矩形都是一个1x1的正方形,坐标原心位于左上角绿色顶点。

配置具体绘图方式的时候,其中第一个参数nodes就记录了每一个结点通过布局计算后的位置信息,让咱们打印一下其中的一条数据看看:

"油画":{
    children: [];
    data: (2) ["油画", "手绘"];
    id: "油画";
    left: 1.5;
    pid: "手绘";
    show: true;
    top: 1.5
}

其中data记录着结点的原始数据,咱们主要看看left和top,这显示该结点应该绘制的坐标为(1.5,1.5),对照上图,是否是就很清晰了。别的布局的设计思想也是如此,请耐心体会一下!

添加交互

// 添加交互用例
$$('circle').bind('click', function () {
    // 删除结点
    tree.delete($$(this).attr('target'));
});

其实交互和布局应该是没有关系的,始终强调,布局只负责计算绘图的位置,好比上面的结点删除例子,绑定点击事件,调用结点的删除结点方法便可。删除方法删除的只是布局保存的数据,而后重绘画面,若是你须要画面平滑改变,修改绘图实现方法便可,这里再也不赘述。

相关文章
相关标签/搜索