当我将一个节点拖到另一个点时,我希望我的力导向图保持平静。此刻,拖动远离中心的小节点会导致整个图形无法控制地抖动。这可能是一团糟,因为节点很多(超过100)......
以下是描述问题的简短视频:https://gfycat.com/GleamingMellowHypacrosaurus
我知道setting the coordinate of all the nodes的方法,但它不可能,因为节点太多,可能会在以后增加。
这是我的代码:
function getNeighbors(node) {
return links.reduce(function(neighbors, link) {
if (link.target.id === node.id) {
neighbors.push(link.source.id)
} else if (link.source.id === node.id) {
neighbors.push(link.target.id)
}
return neighbors
}, [node.id])
}
function isNeighborLink(node, link) {
return link.target.id === node.id || link.source.id === node.id
}
function getNodeColor(node, neighbors) {
// If is neighbor
if (Array.isArray(neighbors) && neighbors.indexOf(node.id) > -1) {
return 'rgba(251, 130, 30, 1)'
// return node.level === 1 ? '#9C4A9C' : 'rgba(251, 130, 30, 1)'
} else {
// Check the node level
if (node.level === 0) {
return '#E72148'
} else if (node.level === 1) {
return '#9C4A9C'
} else {
return '#D8ABD8'
}
}
//return node.level === 0 ? '#91007B' : '#D8ABD8'
}
function getLinkColor(node, link) {
return isNeighborLink(node, link) ? 'rgba(251, 130, 30, .85)' : 'rgba(251, 130, 30, 0.25)'
}
function getTextColor(node, neighbors) {
return Array.isArray(neighbors) && neighbors.indexOf(node.id) > -1 ? '#333' : '#bbb'
}
function getLabelColor(node, link) {
return isNeighborLink(node, link) ? 'rgba(51, 51, 51, .9)' : 'rgba(51, 51, 51, 0)' // #333
}
var width = window.innerWidth
var height = window.innerHeight
var svg = d3.select('svg')
// svg.attr('width', width).attr('height', height)
svg.attr("width", '100%')
.attr("height", '500px')
.attr('viewBox', '250 0 800 600')
//.attr('viewBox','0 0 '+Math.min(width,height)+' '+Math.min(width,height))
.attr('preserveAspectRatio', 'xMidYMid')
.append("g")
.attr("transform", "translate(" + Math.min(width, height) / 2 + "," + Math.min(width, height) / 2 + ")");
//add zoom capabilities
var zoom_handler = d3.zoom()
.scaleExtent([1 / 2, 8])
.on("zoom", zoom_actions);
zoom_handler(svg);
function zoom_actions() {
g.attr("transform", d3.event.transform)
}
function button_zoom_in() {
zoom_handler.scaleBy(svg, 2);
}
function button_zoom_out() {
zoom_handler.scaleBy(svg, 0.5);
}
// simulation setup with all forces
var linkForce = d3
.forceLink()
.id(function(link) {
return link.id
})
// Alternative: using the distance from the data "strength"
//.distance(50).strength(function (link) { return link.strength })
// If don't want to use this, use default here:
.distance(50).strength(.7)
var simulation = d3
.forceSimulation()
.force('link', linkForce)
.force('charge', d3.forceManyBody().strength(-1500))
.force('radial', d3.forceRadial(function(d) {
return d.level * 50
}, width / 2, height / 2))
.force('center', d3.forceCenter(width / 2, height / 2))
var dragDrop = d3.drag().on('start', function(node) {
node.fx = node.x
node.fy = node.y
}).on('drag', function(node) {
simulation.alphaTarget(0.7).restart()
node.fx = d3.event.x
node.fy = d3.event.y
}).on('end', function(node) {
if (!d3.event.active) {
simulation.alphaTarget(0)
}
node.fx = null
node.fy = null
})
function selectNode(selectedNode) {
var neighbors = getNeighbors(selectedNode)
// we modify the styles to highlight selected nodes
nodeElements.attr('fill', function(node) {
return getNodeColor(node, neighbors)
})
textElements.attr('fill', function(node) {
return getTextColor(node, neighbors)
})
linkElements.attr('stroke', function(link) {
return getLinkColor(selectedNode, link)
})
labelElements.attr('fill', function(link) {
return getLabelColor(selectedNode, link)
}).attr("style", "-webkit-text-stroke: 1px rgba(255, 255, 255, 0.75); text-shadow: -1px -1px 0 rgba(255, 255, 255, 0.75), 1px -1px 0 rgba(255, 255, 255, 0.75), -1px 1px 0 rgba(255, 255, 255, 0.75), 1px 1px 0 rgba(255, 255, 255, 0.75)")
}
// Format the numbers to dots e.g. 100000 => 100.000
function commafy( num ) {
var str = num.toString().split('.');
if (str[0].length >= 5) {
str[0] = str[0].replace(/(\d)(?=(\d{3})+$)/g, '$1.');
}
if (str[1] && str[1].length >= 5) {
str[1] = str[1].replace(/(\d{3})/g, '$1 ');
}
return str.join('.');
}
// Enables zooming
var g = svg.append("g")
.attr("class", "everything");
// Enables zooming end
// Create circling orbit
var circles = g.selectAll(null) // use g.selectAll instead of svg.selectAll to enable zoom
.data([200, 350]) // sets the circle radius
.enter()
.append("circle")
.attr("cx", width / 2)
.attr("cy", height / 2)
.attr("r", d => d)
.style("fill", "none")
.style("stroke", "#ddd");
var linkElements = g.append("g") // use g.append instead of svg.append to enable zoom
.attr("class", "links")
.selectAll("line")
.data(links)
.enter().append("path")
.attr("id", function(d, i) {
return "linkId_" + i;
})
.attr("stroke-width", function(link) {
var linkValueNormalize = link.value / 100;
var linkValueNormalize = Math.ceil(linkValueNormalize);
if (linkValueNormalize >= 201) {
return 26;
} else if (linkValueNormalize >= 101 && linkValueNormalize <= 200) {
return 20;
} else if (linkValueNormalize >= 71 && linkValueNormalize <= 100) {
return 16;
} else if (linkValueNormalize >= 41 && linkValueNormalize <= 70) {
return 12;
} else if (linkValueNormalize >= 21 && linkValueNormalize <= 40) {
return 8;
} else if (linkValueNormalize >= 11 && linkValueNormalize <= 20) {
return 12;
} else if (linkValueNormalize >= 7 && linkValueNormalize <= 10) {
return 8;
} else if (linkValueNormalize >= 3 && linkValueNormalize <= 6) {
return 4;
} else {
return 2;
}
// return linkValueNormalize;
})
.attr("stroke", "rgba(251, 130, 30, 0.5)")
var labelElements = g.append("g")
.attr("class", "label")
.selectAll("text")
.data(links)
.enter().append("text")
.attr("font-size", 10)
.attr("font-family", "sans-serif")
.attr("fill", "rgba(51, 51, 51, 0)") // #333
.attr("x", "70")
.attr("y", "-20")
.attr("text-anchor", "start")
.append("textPath")
.attr("xlink:href", function(d, i) {
return "#linkId_" + i;
})
.text(function(link) {
var linkValueNormalize = link.value;
var linkValueNormalize = commafy(linkValueNormalize);
return "Rp "+ linkValueNormalize +" M";
})
var nodeElements = g.append("g") // use g.append instead of svg.append to enable zoom
.attr("class", "nodes")
.selectAll("circle")
.data(nodes)
.enter().append('a') // Append a first, then circle
.attr("xlink:href", function(node){return node.url;})
.attr("target", "_blank")
.append("circle")
.attr("r", function(dat, index, n) {
var linkItem = links.find(function(link) {
return link.target == dat.id;
});
var radius = 26;
var linkValueNormalize = (linkItem && linkItem.value) / 100; // in milyar
var linkValueNormalize = Math.ceil(linkValueNormalize);
if (linkValueNormalize >= 201) {
radius = 24;
} else if (linkValueNormalize >= 101 && linkValueNormalize <= 200) {
radius = 22;
} else if (linkValueNormalize >= 71 && linkValueNormalize <= 100) {
radius = 18;
} else if (linkValueNormalize >= 41 && linkValueNormalize <= 70) {
radius = 14;
} else if (linkValueNormalize >= 21 && linkValueNormalize <= 40) {
radius = 10;
} else if (linkValueNormalize >= 11 && linkValueNormalize <= 20) {
radius = 14;
} else if (linkValueNormalize >= 7 && linkValueNormalize <= 10) {
radius = 10;
} else if (linkValueNormalize <= 6) {
radius = 6;
}
if (dat.level === 0) {
radius = 26;
}
return radius;
})
.attr("fill", getNodeColor)
.attr("stroke", "#fff")
.attr('stroke-width', 2)
.call(dragDrop)
.on('mouseover', selectNode)
var textElements = g.append("g") // use g.append instead of svg.append to enable zoom
.attr("class", "texts")
.selectAll("text")
.data(nodes)
.enter().append("text")
.text(function(node) {
return node.label
})
.attr("font-size", 10)
.attr("font-family", "sans-serif")
.attr("text-anchor", "middle")
.attr("fill", "#333")
.attr("style", "font-weight:bold; -webkit-text-stroke: 1px #fff; text-shadow: 3px 3px 0 #fff, -1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, 1px 1px 0 #fff")
.attr("dx", 0)
.attr("dy", 20)
simulation.nodes(nodes).on('tick', () => {
nodeElements
.attr('cx', function(node) {
return node.x
})
.attr('cy', function(node) {
return node.y
})
textElements
.attr('x', function(node) {
return node.x
})
.attr('y', function(node) {
return node.y
})
linkElements.attr("d", function(link) {
return "M" + link.source.x + "," + link.source.y + " L" + link.target.x + "," + link.target.y;
});
labelElements
.attr('x', function(link) {
return link.target.x
})
.attr('y', function(link) {
return link.target.y
})
})
simulation.force("link").links(links)
答案 0 :(得分:2)
这是预期的行为,因为您正在重新启动模拟:
simulation.alphaTarget(0.7).restart()
除此之外,这一行应该在&#34; start&#34; 监听器中,而不是在&#34; drag&#34; 监听器中:没意思,每秒重启模拟几次,你看到了吗?
回到问题:你想要的结果(&#34;我希望我的力导向图在我拖动一个节点时保持冷静&#34; )并不完全清楚。什么是&#34;保持冷静&#34; ?但是,如果我理解正确,您可以简单地修复所有其他节点。
由于您未提供正在运行的代码,因此以下是使用this example by Mike Bostock的演示。我在这里做的只是:
node.each(function(d){
d.fx = d.x;
d.fy = d.y;
})
这是修改后的bl.ocks:https://bl.ocks.org/anonymous/93c0c9af8c729b62b1b194841298bc49/ede207278c873aa311133782e9fb70e7504ed622