版权声明:本文为博主原创文章,未经博主允许不得转载。 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>