Javascript d3.js集群包含2600多个节点
我一直对节点的布局有问题。我复制了这个可观测的,它对较小的节点非常有效,但对于较大的节点,它似乎变得非常扭曲 我在一个带有typescript的react应用程序中使用这个。我没有可用的代码段,因为数据量很大 以下是一些问题和经验的屏幕广播: 这并不能迫使他们正确地进入他们的小组,而且动画效果也不太好 这是我的代码,如果有人能看到我是否缺少要更新/修复的内容:Javascript d3.js集群包含2600多个节点,javascript,reactjs,typescript,d3.js,Javascript,Reactjs,Typescript,D3.js,我一直对节点的布局有问题。我复制了这个可观测的,它对较小的节点非常有效,但对于较大的节点,它似乎变得非常扭曲 我在一个带有typescript的react应用程序中使用这个。我没有可用的代码段,因为数据量很大 以下是一些问题和经验的屏幕广播: 这并不能迫使他们正确地进入他们的小组,而且动画效果也不太好 这是我的代码,如果有人能看到我是否缺少要更新/修复的内容: const groups = {}, childGroups: any = {}; child
const groups = {},
childGroups: any = {};
childGroups.children = [];
data.forEach(function (item) {
const list = groups[item.group];
if (list) {
list.push(item);
} else {
groups[item.group] = [item];
}
});
for (const property in groups) {
if (groups.hasOwnProperty(property)) {
const children = {
children: groups[property]
};
childGroups.children.push(children);
}
}
const pack = () =>
d3.pack().size([width, height]).padding(1)(d3.hierarchy(childGroups).sum((d: any) => d.size));
let nodes: any = pack().leaves();
const simulation = d3
.forceSimulation(nodes)
.force('x', d3.forceX(width / 2).strength(0.1))
.force('y', d3.forceY(height / 2).strength(0.1))
.force('cluster', forceCluster())
.force('collide', forceCollide());
const svg = d3
.select('#chart')
.append('svg')
.attr('class', 'clusterGraph')
.attr('width', width)
.attr('height', height)
.attr('preserveAspectRatio', 'xMidYMid meet')
.attr('viewBox', `0 0 ${width} ${height}`)
.append('g')
.attr('transform', 'translate(0, 0)');
const tooltip = d3
.select('#chart')
.append('div')
.style('opacity', 0)
.attr('class', 'tooltip')
.style('background-color', '#ececec')
.style('border-radius', '5px')
.style('padding', '8px');
const showTooltip = function (this: any, d) {
tooltip.transition().duration(200);
tooltip
.style('opacity', 0.8)
.html(d.data.tooltip)
.style('left', d3.mouse(this)[0] + 30 + 'px')
.style('top', d3.mouse(this)[1] + 30 + 'px')
.style('color', d.data.color);
};
const moveTooltip = function (this: any, d) {
tooltip.style('left', d3.mouse(this)[0] + 30 + 'px').style('top', d3.mouse(this)[1] + 30 + 'px');
};
const hideTooltip = function (d) {
tooltip.transition().duration(200).style('opacity', 0);
};
const node = svg
.append('g')
.selectAll('circle')
.data(nodes)
.join('circle')
.attr('class', 'bubble')
.attr('cx', (d: any) => d.data.x)
.attr('cy', (d: any) => d.data.y)
.attr('fill', (d: any) => d.data.color)
.attr('r', (d: any) => d.r)
.on('mouseover', showTooltip)
.on('mousemove', moveTooltip)
.on('mouseleave', hideTooltip);
node.transition()
.delay((d, i) => Math.random() * 500)
.duration(750)
.attrTween('r', (d: any) => {
const i = d3.interpolate(0, d.r);
return (t) => (d.r = i(t));
});
simulation.on('tick', () => {
node.attr('cx', (d: any) => d.x).attr('cy', (d: any) => d.y);
});
function forceCluster() {
const strength = 0.2;
function force(alpha) {
const centroids: any = d3Array.rollup(nodes, centroid, (d: any) => d.group);
const l = alpha * strength;
for (const d of nodes) {
const { x: cx, y: cy } = centroids.get(d.group);
d.vx -= (d.x - cx) * l;
d.vy -= (d.y - cy) * l;
}
}
force.initialize = (_) => (nodes = _);
return force;
}
function forceCollide() {
const alpha = 0.4; // fixed for greater rigidity!
const padding1 = 2; // separation between same-color nodes
const padding2 = 6; // separation between different-color nodes
let maxRadius;
function force() {
const quadTree = d3.quadtree(
nodes,
(d: any) => d.x,
(d) => d.y
);
for (const d of nodes) {
const r = d.r + maxRadius;
const nx1 = d.x - r,
ny1 = d.y - r;
const nx2 = d.x + r,
ny2 = d.y + r;
quadTree.visit((q: any, x1, y1, x2, y2) => {
if (!q.length)
do {
if (q.data !== d) {
const r =
d.r + q.data.r + (d.data.group === q.data.data.group ? padding1 : padding2);
let x = d.x - q.data.x,
y = d.y - q.data.y,
l = Math.hypot(x, y);
if (l < r) {
l = ((l - r) / l) * alpha;
return (d.x -= x *= l), (d.y -= y *= l);
}
if (l < r) {
l = ((l - r) / l) * alpha;
return (q.data.x += x), (q.data.y += y);
}
}
} while ((q = q.next));
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
});
}
}
force.initialize = (_) => (maxRadius = Math.max(...nodes.map((o) => o.r)) + Math.max(padding1, padding2));
return force;
}
function centroid(nodes) {
let x = 0;
let y = 0;
let z = 0;
for (const d of nodes) {
const k = d.r ** 2;
x += d.x * k;
y += d.y * k;
z += k;
}
return { x: x / z, y: y / z };
const groups={},
子组:any={};
childGroups.children=[];
data.forEach(功能(项){
常量列表=组[项目组];
如果(列表){
列表。推送(项目);
}否则{
组[项目组]=[项目];
}
});
for(组中的常量属性){
if(组hasOwnProperty(属性)){
const children={
儿童:团体[财产]
};
childGroups.children.push(children);
}
}
常数包=()=>
d3.pack().size([width,height]).padding(1)(d3.hierarchy(childGroups).sum((d:any)=>d.size));
let节点:any=pack().leaves();
常数模拟=d3
.forceSimulation(节点)
.力('x',d3.力x(宽度/2).强度(0.1))
.力('y',d3.力(高度/2).强度(0.1))
.force('cluster',forceCluster())
.force('collide',forceCollide());
常量svg=d3
.选择(“#图表”)
.append('svg')
.attr('class','clusterGraph')
.attr('width',width)
.attr('height',height)
.attr('preserveAspectRatio','xMidYMid-meet')
.attr('viewBox','0 0${width}${height}`)
.append('g')
.attr('transform','translate(0,0)');
常量工具提示=d3
.选择(“#图表”)
.append('div'))
.style('opacity',0)
.attr('类','工具提示')
.style('background-color','#ececec')
.style('border-radius','5px')
.style('padding','8px');
const showTooltip=函数(此:any,d){
tooltip.transition().持续时间(200);
工具提示
.style(“不透明度”,0.8)
.html(d.data.tooltip)
.style('left',d3.鼠标(this)[0]+30+'px')
.style('top',d3.鼠标(this)[1]+30+'px')
.style('颜色',d.data.color);
};
const moveTooltip=函数(此:any,d){
tooltip.style('left',d3.mouse(this)[0]+30+'px')。style('top',d3.mouse(this)[1]+30+'px');
};
常量hideTooltip=函数(d){
tooltip.transition().duration(200).style('opacity',0);
};
const node=svg
.append('g')
.selectAll('圆圈')
.数据(节点)
.join('圆')
.attr('class','bubble')
.attr('cx',(d:any)=>d.data.x)
.attr('cy',(d:any)=>d.data.y)
.attr('fill',(d:any)=>d.data.color)
.attr('r',(d:any)=>d.r)
.on('mouseover',showTooltip)
.on('mousemove',moveTooltip)
.on('mouseleave',hidetoltip);
node.transition()
.delay((d,i)=>Math.random()*500)
.持续时间(750)
.attrween('r',(d:any)=>{
常数i=d3.插值(0,d.r);
返回(t)=>(d.r=i(t));
});
simulation.on('tick',()=>{
node.attr('cx',(d:any)=>d.x.attr('cy',(d:any)=>d.y);
});
函数forceCluster(){
恒强度=0.2;
作用力(α){
常量质心:any=d3Array.rollup(节点,质心,(d:any)=>d.group);
常数l=α*强度;
for(节点的常数d){
常数{x:cx,y:cy}=centroids.get(d.group);
d、 vx-=(d.x-cx)*l;
d、 vy-=(d.y-cy)*l;
}
}
force.initialize=(\u)=>(节点=\ux);
返回力;
}
函数forceCollide(){
常数alpha=0.4;//固定为更大的刚性!
常量padding1=2;//相同颜色节点之间的分隔
常量padding2=6;//不同颜色节点之间的分隔
设最大半径;
函数力(){
常量四叉树=d3.quadTree(
节点,
(d:any)=>d.x,
(d) =>d.y
);
for(节点的常数d){
常数r=d.r+maxRadius;
常数nx1=d.x-r,
ny1=d.y-r;
常数nx2=d.x+r,
ny2=d.y+r;
四叉树访问((q:any,x1,y1,x2,y2)=>{
如果(!q.length)
做{
如果(q.data!==d){
常数r=
d、 r+q.data.r+(d.data.group==q.data.data.group?填充1:padding2);
设x=d.x-q.data.x,
y=d.y-q.data.y,
l=数学形下压(x,y);
if(lfunction forceCluster(nodes) {
const strength = 0.2;
function force(alpha) {
const centroids: any = d3Array.rollup(nodes, centroid, (d: any) => d.data.group);
const l = alpha * strength;
for (const d of nodes) {
const { x: cx, y: cy } = centroids.get(d.data.group);
d.vx -= (d.x - cx) * l;
d.vy -= (d.y - cy) * l;
}
}
force.initialize = (_) => (nodes = _);
return force;
}
node.transition()
.delay((d, i) => 50)
.duration(750);