Javascript 多个力布局会导致勾号函数发生冲突
我试图在一个页面上同时放置多个D3Force布局。强制布局的数量在理想情况下是可变的,这取决于从动态API返回的根的数量。我遵循了答案,并成功地将每个布局放在单独的div中,放在单独的svg中 然而,问题有两个方面: 1) SVG似乎同时绘制,导致alpha冷却参数冲突(在每个图形的“勾号”上)。因此,页面上绘制的最后一个svg是按预期方式定位的唯一布局。tick函数包含类似于垂柳树的力布局形状代码,根节点位于顶部,子节点位于其下方 2) 将循环设置为在API的完整结果列表上迭代会导致D3崩溃,并出现错误“UncaughtTypeError:无法读取null的属性'textContent' 我认为理想的解决方案是,在成功渲染前一个力布局后绘制每个力布局,这样做不会导致alpha冷却参数(在“勾选”上)冲突,或者一次使用过多的力布局实例重载D3库。有人对这个问题有见解吗?这是我的密码:Javascript 多个力布局会导致勾号函数发生冲突,javascript,d3.js,force-layout,Javascript,D3.js,Force Layout,我试图在一个页面上同时放置多个D3Force布局。强制布局的数量在理想情况下是可变的,这取决于从动态API返回的根的数量。我遵循了答案,并成功地将每个布局放在单独的div中,放在单独的svg中 然而,问题有两个方面: 1) SVG似乎同时绘制,导致alpha冷却参数冲突(在每个图形的“勾号”上)。因此,页面上绘制的最后一个svg是按预期方式定位的唯一布局。tick函数包含类似于垂柳树的力布局形状代码,根节点位于顶部,子节点位于其下方 2) 将循环设置为在API的完整结果列表上迭代会导致D3崩溃,
/* ... GET THE RESULTS FROM THE API ...*/
function handleRequest2(json) {
allroots = json[1]['data']['children'];
(function() {
var index = 0;
function LoopThrough() {
currentRoot = allroots[index];
if (index < allroots.length) {
/* DRAW THE GRAPH */
draw_graphs(currentRoot, index);
++index;
LoopThrough();
};
}
LoopThrough();
})();
}
//Force Layout Code
function draw_graphs(root, id) {
var root_id = "map-" + id.toString();
var force;
var vis;
var link;
var node;
var w = 980;
var h = 1000;
var k = 0;
// Create a separate div to house each SVG graph
div = document.createElement("div");
div.style.width = "980px";
div.style.height = "1000px";
div.style.cssFloat="left";
div.id = root_id;
$(div).addClass("chattermap-map");
// Append the div to the chart container
$('#chart').append(div);
force = d3.layout.force()
.size([w, h])
.charge(-250)
.gravity(0)
.on("tick", tick);
// Create the SVG and append it to the created div
vis = d3.select("#"+root_id)
.append("svg:svg")
.attr("width", w)
.attr("height", h)
.attr("id",root_id);
// Put the Reddit JSON in the correct format for the Force Layout
nodes = flatten(root),
links = optimize(d3.layout.tree().links(nodes));
// Calculations for the sizing of the nodes
avgNetPositive = getAvgNetPositive();
maxNetPositive = d3.max(netPositiveArray);
minNetPositive = d3.min(netPositiveArray);
// Create a logarithmic scale that sizes the nodes
radius = d3.scale.pow().exponent(.3).domain([minNetPositive,maxNetPositive]).range([5,30]);
// Fix the root node to the top of the svg
root.data.fixed = true;
root.data.x = w/2;
root.data.y = 50;
// Start the force layout.
force
.nodes(nodes)
.links(links)
.start();
// Update the links
link = vis.selectAll("line.link")
.data(links, function(d) { return d.target.id; });
// Enter any new links.
link.enter().insert("svg:line", ".node")
.attr("class", "link")
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
// Exit any old links.
link.exit().remove();
// Update the nodes
node = vis.selectAll("circle.node")
.data(nodes, function(d) {return d.id; })
.style("fill", function(d) {
return '#2960b5';
});
// Enter any new nodes.
node.enter().append("svg:circle")
.attr("class", "node")
.attr("cx", function(d) {return d.x; })
.attr("cy", function(d) {return d.y; })
.attr("r", function(d) {
//Get the net positive reaction
var netPositive = d.ups - d.downs;
var relativePositivity = netPositive/avgNetPositive;
//Scale the radii based on the logarithmic scale defined earlier
return radius(netPositive);
})
.style("fill", function(d) {
return '#2960b5';
})
// Allow dragging on click
.call(force.drag);
// Exit any old nodes.
node.exit().remove();
//This will add the name of the author to the node HTML
node.append("author").text(function(d) {return d.author});
//Add the body of the comment to the node
node.append("comment").text(function(d) {return Encoder.htmlDecode(d.body_html)});
//Add the UNIX timestamp to the node
node.append("timestamp").text(function(d) {return moment.unix(d.created_utc).fromNow();})
//On load, assign the root node to the tooltip
numberOfNodes = node[0].length;
rootNode = d3.select(node[0][parseInt(numberOfNodes) - 1]);
rootNodeComment = rootNode.select("comment").text();
rootNodeAuthor = rootNode.select("author").text();
rootNodeTimestamp = rootNode.select("timestamp").text();
// Create the tooltip div for the comments
tooltip_div = d3.select("#"+root_id).append("div")
.attr("class", "tooltip")
.style("opacity", 1);
//Add the HTML to the tooltip for the root
tooltip_div .html("<span class='commentAuthor'>" + rootNodeAuthor + "</span><span class='bulletTimeAgo'>•</span><span class='timestamp'>" + rootNodeTimestamp + "</span><br>" + rootNodeComment)
//Position the tooltip based on the position of the current node, and it's size
.style("left", (rootNode.attr("cx") - (-rootNode.attr("r")) - (-9)) + "px")
.style("top", (rootNode.attr("cy") - 15) + "px");
node.on("mouseover", function() {
currentNode = d3.select(this);
currentTitle = currentNode.select("comment").text();
currentAuthor = currentNode.select("author").text();
currentTimestamp = currentNode.select("timestamp").text();
tooltip_div.transition()
.duration(200)
.style("opacity", 1);
// Add the HTML for all other tooltips on mouseover
tooltip_div .html("<span class='commentAuthor'>" + currentAuthor + "</span><span class='bulletTimeAgo'>•</span><span class='timestamp'>" + currentTimestamp + "</span><br>" + currentTitle)
//Position the tooltip based on the position of the current node, and it's size
.style("left", (currentNode.attr("cx") - (-currentNode.attr("r")) - (-9)) + "px")
.style("top", (currentNode.attr("cy") - 15) + "px");
});
// Fade out the tooltip on mouseout
node.on("mouseout", function(d) {
tooltip_div.transition()
.duration(500)
.style("opacity", 1);
});
// Optimize the JSON output of Reddit for D3
function flatten(root) {
var nodes = [], i = 0, j = 0;
function recurse(node) {
if (node['data']['replies'] != "" && node['kind'] != "more") {
node['data']['replies']['data']['children'].forEach(recurse);
}
if (node['kind'] !="more") {
//Add an ID value to the node starting at 1
node.data.id = ++i;
node.data.name = node.data.body;
//Put the replies in the key 'children' to work with the tree layout
if (node.data.replies != "") {
node.data.children = node.data.replies.data.children;
//Remove the extra 'data' layer for each child
for (j=0; j < node.data.children.length; j++) {
node.data.children[j] = node.data.children[j].data;
}
} else {
node.data.children = "";
}
var comment = node.data;
nodes.push(comment);
}
}
recurse(root);
return nodes;
}
// Optimize the JSON for use with Links
function optimize(linkArray) {
optimizedArray = [];
for (k=0; k < linkArray.length; k++) {
if(typeof linkArray[k].target.count == 'undefined') {
optimizedArray.push(linkArray[k]);
}
}
return optimizedArray;
}
// Get the average net positive upvotes for use in sizing
function getAvgNetPositive() {
var sum = 0;
netPositiveArray = []
//Select all the nodes
var allNodes = d3.selectAll(nodes)[0];
//For each node, get the net positive votes and add it to the sum
for (i=0; i < allNodes.length; i++) {
var netPositiveEach = allNodes[i]["ups"] - allNodes[i]["downs"];
sum += netPositiveEach;
netPositiveArray.push(netPositiveEach);
}
var avgNetPositive = sum/allNodes.length;
return avgNetPositive;
}
function tick(e) {
var kx = .4 * e.alpha, ky = 1.4 * e.alpha;
links.forEach(function(d, i) {
d.target.x += (d.source.x - d.target.x) * kx;
d.target.y += (d.source.y + 80 - d.target.y) * ky;
});
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
// // Remove the animation effect of the force layout
// while ((force.alpha() > 1e-2) && (k < 150)) {
// force.tick(),
// k = k + 1;
// }
}
/*。。。从API获取结果*/
函数handleRequest2(json){
allroots=json[1]['data']['children'];
(功能(){
var指数=0;
函数LoopThrough(){
currentRoot=所有根[索引];
if(索引<所有根长度){
/*画图表*/
绘制图表(currentRoot,索引);
++指数;
LoopThrough();
};
}
LoopThrough();
})();
}
//部队布局代码
函数绘制图(根,id){
var root_id=“map-”+id.toString();
无功功率;
var-vis;
var-link;
var节点;
var w=980;
var h=1000;
var k=0;
//创建一个单独的div来容纳每个SVG图形
div=document.createElement(“div”);
div.style.width=“980px”;
div.style.height=“1000px”;
div.style.cssFloat=“左”;
div.id=root\u id;
$(div.addClass(“chattermap映射”);
//将div附加到图表容器中
$('图表')。追加(div);
force=d3.layout.force()
.尺寸([w,h])
。收费(-250)
.重力(0)
.在(“滴答”,滴答)上;
//创建SVG并将其附加到创建的div中
vis=d3。选择(“#”+根id)
.append(“svg:svg”)
.attr(“宽度”,w)
.attr(“高度”,h)
.attr(“id”,root\u id);
//将Reddit JSON设置为Force布局的正确格式
节点=展平(根),
links=优化(d3.layout.tree().links(节点));
//计算节点的大小
avgNetPositive=getAvgNetPositive();
maxNetPositive=d3.max(netPositiveArray);
minNetPositive=d3.min(netPositiveArray);
//创建用于调整节点大小的对数比例
半径=d3.scale.pow().指数(.3).domain([minNetPositive,maxNetPositive])。范围([5,30]);
//将根节点固定到svg的顶部
root.data.fixed=true;
根.data.x=w/2;
root.data.y=50;
//启动force布局。
力
.节点(节点)
.链接(links)
.start();
//更新链接
link=vis.selectAll(“line.link”)
.data(链接,函数(d){返回d.target.id;});
//输入任何新链接。
link.enter().insert(“svg:line”,“.node”)
.attr(“类”、“链接”)
.attr(“x1”,函数(d){返回d.source.x;})
.attr(“y1”,函数(d){返回d.source.y;})
.attr(“x2”,函数(d){返回d.target.x;})
.attr(“y2”,函数(d){返回d.target.y;});
//退出所有旧链接。
link.exit().remove();
//更新节点
node=vis.selectAll(“circle.node”)
.data(节点,函数(d){return d.id;})
.样式(“填充”,功能(d){
返回“#2960b5”;
});
//输入任何新节点。
node.enter().append(“svg:circle”)
.attr(“类”、“节点”)
.attr(“cx”,函数(d){return d.x;})
.attr(“cy”,函数(d){返回d.y;})
.attr(“r”,函数(d){
//得到净阳性反应
var净正=d.上升-下降;
var相对正性=净正/avgNetPositive;
//根据前面定义的对数比例缩放半径
返回半径(净正);
})
.样式(“填充”,功能(d){
返回“#2960b5”;
})
//允许单击时拖动
.呼叫(强制拖动);
//退出所有旧节点。
node.exit().remove();
//这将把作者的名字添加到HTML节点
node.append(“author”).text(函数(d){return d.author});
//将注释主体添加到节点
node.append(“comment”).text(函数(d){return Encoder.htmlDecode(d.body_html)});
//将UNIX时间戳添加到节点
node.append(“timestamp”).text(函数(d){return moment.unix(d.created_utc.fromNow();})
//加载时,将根节点指定给工具提示
numberOfNodes=节点[0]。长度;
rootNode=d3.选择(节点[0][parseInt(numberOfNodes)-1]);
rootNodeComment=rootNode.select(“comment”).text();
rootNodeAuthor=rootNode.select(“author”).text();
rootNodeTimestamp=rootNode.select(“timestamp”).text();
//为注释创建工具提示div
工具提示_div=d3。选择(“#”+根id)。追加(“div”)
.attr(“类”、“工具提示”)
.样式(“不透明”,1);
//将HTML添加到根目录的工具提示中
工具提示\u div.html(“+rootNodeAuthor+”&bull;“+rootNodeTimestamp+”
“+rootNodeComment)
//根据当前节点的位置及其大小定位工具提示
.style(“左”,(rootNode.attr(“cx”)-(-rootNode.attr(“r”)-(-9))+“px”)
.style(“top”,(rootNode.attr(“cy”)-15)+“px”);
on(“mouseover”,function()){
currentNode=d3。选择(此);
currentTitle=currentNode.select(“comment”).text();
currentAuthor=currentNode。选择(“作者”).text();
currentTimestamp=currentNode.select(“timestamp”).text();
工具提示\u div.transi