如何使用d3js画分帧画一棵树

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qian99/article/details/71258399

最近用d3js写了一个把树可视化的工具,但是遇到一个问题,当树的结点非常多的时候,加载整棵树会卡一段时间,体验不是很好,所以想是不是能用分帧加载的方法,不要一次加载整棵树,而是隔一段时间加载一部分。
这是老套路了,本来觉得应该挺简单的,但是网上查了半天也找到符合需求的文章,又去查API,发现也没有可用的。期间也发现了一个看起来可以用的,先贴一下链接:http://jsfiddle.net/cyril123/sef9udod/ 。但是当我用过才发现,这是个花架子,中看不中用,它的缺点是每次都要重新生成数据,数据量小的时候当然怎样都好,但是对于我当前要解决的问题并没有什么帮助。
看了一下源码,没看懂……有些关键代码不知道隐藏到什么地方去了,而且我只是想做个工具,不想研究太深。没办法,只能看看有没有什么黑科技了,一搬生成树的数据的时候,会有这样一个代码:

var tree = d3.layout.tree().size([width - 50,height - 50])
                .separation(function(a,b){ return (a.parent == b.parent ? 1 : 2); });
var diagonal = d3.svg.diagonal()
                .projection(function(d){return [d.x, d.y]});
var svg = d3.select('#display').append('svg')
                .attr('width',width)
                .attr('height', height)
                .append('g')
                .attr('transform', 'translate(0,10)');
var nodes = tree.nodes(target);
var node = svg.selectAll('.node')
            .data(nodes)
            .enter()
            .append('g')
            .attr('class','node')
            .attr('transform', function(d) {return 'translate(' + d.x + ',' + d.y + ')';})

其实我的目的呢,就是分步去执行代码中append(‘g’)以及后面一系列操作,但是这个操作d3js是不支持的,所以我把执行到enter()的结果打印出来,发现这是一个数组+一些其他额外的属性,之前尝试把数组中的数拆开,未果,最后想了个招,把这个数组中的每个元素,也就是每个结点都拿出来,用形成一堆数组,只不过这些数组中只有一个元素罢了,这样我虽然不知道这个源码怎么实现,但是我只要每次去调对应的函数就好了,因为我把这些属性都copy下来,形成一个新的对象了。也得亏是js这种原型模式,其他语言不一定会出现什么奇怪的bug……

最后贴一下代码:

var NODE_NUM = 10000;
var MAX_CHILD = 50;
var updateTimeout = null;
var Node = function() {
    this.index = 0;
    this.parent = {};
    this.children = [];
    this.history = "";
}

//随机生成一棵树
function generalRandomTreeObject(total_number, max_child, idx) {
    var tree = {name:idx + 1};
    if (total_number == 1) {
        return tree;
    }
    tree.children = [];
    total_number -= 1;
    idx += 1;
    var child_number = Math.floor(Math.random()*max_child + 1);
    child_number = Math.min(child_number, total_number);
    var child_counts = new Array(child_number);
    for (var i = 0;i < child_number; ++i) {
        child_counts[i] = Math.floor(Math.random()*total_number + 1);
    }
    child_counts.sort();
    var selected_number = 0;
    for (var i = 0;i < child_number; i++) {
        var now_number = child_counts[i] - selected_number;
        now_number = Math.max(now_number, 1);
        if (total_number - now_number < child_number - i - 1) {
            now_number = total_number - (child_number - i - 1);
        }
        if (i == child_number - 1) {
            now_number = total_number;
        }
        var child = generalRandomTreeObject(now_number, max_child, idx);
        tree.children.push(child);
        total_number -= now_number;
        selected_number += now_number;
        idx += now_number;
    }
    return tree;
}

function drawTree(target, width, height, flag) {
    clearTimeout(self.updateTimeout);
    var ds = $('#display');
    $('#display')[0].innerHTML = "";
    console.log(ds);
    if (flag) {
        return;
    }
    var tree = d3.layout.tree().size([width - 50,height - 50])
                .separation(function(a,b){ return (a.parent == b.parent ? 1 : 2); });
    var diagonal = d3.svg.diagonal()
                .projection(function(d){return [d.x, d.y]});
    var svg = d3.select('#display').append('svg')
                .attr('width',width)
                .attr('height', height)
                .append('g')
                .attr('transform', 'translate(0,10)');
    var nodes = tree.nodes(target);
    var links = tree.links(nodes);
    ////---------------复制引用分帧加载---------------------
    var node = svg.selectAll('.node')
                    .data(nodes).enter();
    var link = svg.selectAll('.link')
                    .data(links)
                    .enter();
    var nodeSpliter = [];
    var linkSpliter = [];
    var addNodeOnceTime = 500;
    var addLinkOnceTime = 600;
    function autoOpenTree(pos,nodeAddCount, linkAddCount) {
        if(linkAddCount < link[0].length) {
            var ob1 = [];
            ob1.__proto__ = link.__proto__;
            for (var i = 0;i < addLinkOnceTime && linkAddCount < link[0].length; ++i) {
                var ob2 = [];
                ob2.__proto__ = link[0].__proto__;
                ob2.parentNode = link[0].parentNode;
                ob2.update = link[0].update;
                ob2.push(link[0][linkAddCount]);
                ob1.push(ob2);
                linkAddCount += 1;
            }
            linkSpliter.push(ob1);
            linkSpliter[pos].append('path')
                .attr('class','link')
                .attr('d', diagonal);
        }
        if (nodeAddCount < node[0].length) {
            var ob1 = [];
            ob1.__proto__ = node.__proto__;
            for (var i = 0;i < addNodeOnceTime && nodeAddCount < node[0].length; ++i) {
                var ob2 = [];
                ob2.__proto__ = node[0].__proto__;
                ob2.parentNode = node[0].parentNode;
                ob2.update = node[0].update;
                ob2.push(node[0][nodeAddCount]);
                ob1.push(ob2);
                nodeAddCount += 1;
            }
            nodeSpliter.push(ob1);
            var p = nodeSpliter[pos].append('g')
                    .attr('class','node')
                    .attr('transform', function(d) {
                        return 'translate(' + d.x + ',' + d.y + ')';});
            p.append('circle')
                .attr('r',4.5)
                .attr('stroke','steelblue')
                .attr('stroke-width','1.5px');
            p.append("text")
              .attr("dx", function(d) { return d.children ? 14 : 0; })
              .attr("dy", 16)
              .style("text-anchor", function(d) { return d.children ? "end" : "start"; })
              .text(function(d) { return d.name; });
        }
        if(linkAddCount < link[0].length || nodeAddCount < node[0].length) {
            self.updateTimeout=setTimeout(function(){
                autoOpenTree(pos + 1,nodeAddCount, linkAddCount);
            }, 30);
        }
    }
    autoOpenTree(0,0, 0);
    //--------正常画树方法------------------
    // var link = svg.selectAll('.link')
    //         .data(links)
    //         .enter()
    //         .append('path')
    //         .attr('class','link')
    //         .attr('d', diagonal);
    // var node = svg.selectAll('.node')
    //         .data(nodes)
    //         .enter()
    //         .append('g')
    //         .attr('class','node')
    //         .attr('transform', function(d) {return 'translate(' + d.x + ',' + d.y + ')';})
    // node.append('circle').attr('r',4.5);

    // node.append("text")
    //   .attr("dx", function(d) { return d.children ? 14 : 0; })
    //   .attr("dy", 16)
    //   .style("text-anchor", function(d) { return d.children ? "end" : "start"; })
    //   .text(function(d) { return d.name; });
}

function test() {
    var source_tree = generalRandomTreeObject(NODE_NUM,MAX_CHILD,0);
    $(document).ready(function(){
        drawTree(source_tree, 1000, 600);
        $('#btn_random').click(function(){
            source_tree = generalRandomTreeObject(NODE_NUM,MAX_CHILD, 0);
            drawTree(source_tree, 1000, 600);
        });
    });
}

test();

html代码也贴一下好了:

<!Doctype html>
<html xmlns:svg="http://www.w3.org/2000/svg">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <meta property="qc:admins" content="465267610762567726375" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />  
        <title>tree view</title>
    </head>
    <div>
        <button type='button' id='btn_random' style='width:100px;height: 33px'>draw new tree</button>
        <!-- <button type='button' id='btn_next' style='width:100px;height: 33px'>next</button> -->
    </div>
    <div name='all' id='display'>
    </div>
    <style>
        .node circle {
          fill: #fff;
          stroke: steelblue;
          stroke-width: 1.5px;
        }
        .node {
          font: 12px sans-serif;
        }
        .link {
          fill: none;
          stroke: #ccc;
          stroke-width: 1.5px;
        }
    </style>
    <script src="http://libs.baidu.com/jquery/1.9.0/jquery.js" type="text/javascript"></script>
    <script type="text/javascript" src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
    <script type="text/javascript" src="framing_load_tree.js"></script>
</html>

猜你喜欢

转载自blog.csdn.net/qian99/article/details/71258399