在多焦点d3力布局中重新定位节点

Con*_*pak 4 javascript d3.js force-layout d3v4

我在多焦点布局中有三组节点.每个节点都已经以HTML格式呈现.

唯一改变的是绑定到每个小人物的数据. 在此输入图像描述

我想要做的是当一个事件发生时(在这种情况下是一个下拉列表),节点将自己重新定位到他们的新集群.

我的意思是,如果新数据左边只有10个蓝色,右边有90个红色,中间有2个紫色,左边的蓝色将变为红色并转换到右边的簇.

从我到目前为止所读到的,我认为它可能正在改变引力.但是,我不认为这是D3 V4中的一个选项.

我能找到的最接近的例子就是我想要完成的事情.

这是我的强制布局代码的样子:

var node = this.svg.selectAll('path')
    .data(data);

// foci is a dictionary that assigns the x and y value based
// on what group a node belongs to.
var foci = {
    "Blue" : {
         "x" : xScale(0),
         "y": height / 2
    },
    "Red": {
         "x" : xScale(1),
         "y": height / 2
    },
    "Purple": {
         "x" : xScale(2),
         "y": height / 2
    },
};

// This helped me position the nodes to their assigned clusters.
var forceX = d3.forceX((d) => foci[d.group].x);
var forceY = d3.forceY((d) => foci[d.group].y);

var force = d3.forceSimulation(data)
    .force('x', forceX)
    .force('y', forceY)
    .force("collide", d3.forceCollide(8))
    .on('tick', function() {
         node
            .attr('transform', (d) => {
                return 'translate(' + (d.x - 100) + ',' + (-d.y + 25) + ')';
            });
        });
Run Code Online (Sandbox Code Playgroud)

到目前为止我能够完成的是根据下拉列表中的更改重新绘制布局,重新启动d3.forceSimulation()并使群集在页面上重新捕捉,如下面的gif所示.

在此输入图像描述

那不是我想要的.我正在努力使重新排列尽可能无缝.

更新:通过不重新初始化d3.forceSimulation(),我可以将新数据绑定到节点并更改其颜色.现在只是将它们移动到适当的集群. 在此输入图像描述

Ger*_*ado 6

d3.forceSimulation()您可以使用以下命令重新设置模拟,而不是重新初始化restart():

重新启动模拟的内部计时器并返回模拟.结合simulation.alphaTarget或simulation.alpha,此方法可用于在交互期间"重新加热"模拟,例如拖动节点时,或在暂时将模拟与模拟暂停后恢复模拟.

我创建了一个演示,使用部分代码向您展示.在此演示中,按钮随机化每个数据点的颜色.之后,我们重新模拟模拟:

force.alpha(0.8).restart();
Run Code Online (Sandbox Code Playgroud)

检查一下,点击"随机化":

var width = 500, height = 200;

var svg = d3.select("#svgdiv")
	.append("svg")
	.attr("width", width)
	.attr("height", height);
	
var data = d3.range(100).map(function(d, i){
	return {
	group: Math.random()*2 > 1 ? "blue" : "red",
	id: i
	}
});

var xScale = d3.scaleOrdinal()
	.domain([0, 1])
	.range([100, width-100]);

var foci = {
    "blue" : {
         "x" : xScale(0),
         "y": height / 2
    },
    "red": {
         "x" : xScale(1),
         "y": height / 2
    }
};

var forceX = d3.forceX((d) => foci[d.group].x);
var forceY = d3.forceY((d) => foci[d.group].y);

var node = svg.append("g")
            .attr("class", "nodes")
            .selectAll("circle")
            .data(data)
            .enter().append("circle")
            .attr("r", 5)
						.attr("fill", (d)=>d.group);


var force = d3.forceSimulation(data)
    .velocityDecay(0.65)
    .force('x', forceX)
    .force('y', forceY)
    .force("collide", d3.forceCollide(8));
		
force.nodes(data)
    .on('tick', function() {
         node
            .attr('transform', (d) => {
                return 'translate(' + (d.x) + ',' + (d.y) + ')';
            });
        });
				
d3.select("#btn").on("click", function(){
    data.forEach(function(d){
		d.group = Math.random()*2 > 1 ? "blue" : "red"
		})
		node.transition().duration(500).attr("fill", (d)=>d.group);
		setTimeout(function(){
		force.nodes(data);
		force.alpha(0.8).restart();
		}, 1500)
})
Run Code Online (Sandbox Code Playgroud)
<script src="https://d3js.org/d3.v4.min.js"></script>
<button id="btn">Randomize</button>
<div id="svgdiv"><div>
Run Code Online (Sandbox Code Playgroud)

PS:我把再加热 放在里面setTimeout,所以你可以先看到圆圈改变颜色,然后转移到焦点位置.