D3.js D3 wForceSimulation的多个实例
希望有人能帮助我的第一个D3可视化。我有两个数据集,作为单独的组元素选择加载。我试图模拟气泡上的力,但是我只能让其中一组同时起作用。我觉得这与多次调用d3.forceSimulation有关,但我想不出解决办法。感谢您的帮助!(为草率的编码表示歉意,游戏时间不长!Github repo:)D3.js D3 wForceSimulation的多个实例,d3.js,D3.js,希望有人能帮助我的第一个D3可视化。我有两个数据集,作为单独的组元素选择加载。我试图模拟气泡上的力,但是我只能让其中一组同时起作用。我觉得这与多次调用d3.forceSimulation有关,但我想不出解决办法。感谢您的帮助!(为草率的编码表示歉意,游戏时间不长!Github repo:) 您必须更新强制勾选中封闭的g的变换,而不是cx和cy t_circles .attr("transform", function(d) {return `translate(${d.x}
您必须更新强制勾选中封闭的
g
的变换,而不是cx
和cy
t_circles
.attr("transform", function(d) {return `translate(${d.x},${d.y})`;})
// .attr("cx", function(d) {return d.x;})
// .attr("cy", function(d) {return d.y;});
还必须将要素对象添加到力模拟中,现在仅对案例节点设置动画并进行更新
设置新的alpha
值以再次启动强制,否则sim卡将永远无法结束
function updateForces(forces) {
forceSimulation
.force("x", forces.x)
.force("y", forces.y)
.force("collide", d3.forceCollide(forceCollide))
// .alphaTarget(0.5)
.alpha(0.5)
.restart();
}
编辑
不可移动节点的根本原因是不允许在模拟之间共享力。原因:力跟踪模拟中使用的节点。您可以共享访问器函数
在某些情况下,位置也会改变
function createForces() {
var forceStrength = 0.15;
forces = {
features: createFeaturesForces(),
cases: createCasesForces(),
timeScales: createTimeScalesForces()
};
function createFeaturesForces() {
return {
x: d3.forceX(featurePositionX).strength(forceStrength),
y: d3.forceY(featurePositionY).strength(forceStrength),
xpos: featurePositionX,
ypos: featurePositionY
};
function featurePositionX(d) {
if ("Importance" in d) {
return (width /2);
}
return (width + 1000);
}
function featurePositionY(d) {
if ("Importance" in d) {
return (height /2);
}
return (height /2);
}
}
function createCasesForces() {
return {
x: d3.forceX(casesPositionX).strength(forceStrength),
y: d3.forceY(casesPositionY).strength(forceStrength),
xpos: casesPositionX,
ypos: casesPositionY
};
function casesPositionX(d) {
if ("Technology" in d) {
return (width /2);
}
return (0 - 1000);
}
function casesPositionY(d) {
if ("Technology" in d) {
return (height /2);
}
return (height / 2);
}
}
function createTimeScalesForces() {
var columnNamesDomain = columns.values();
var timeScalesDomain = timeScales.values();
var scaledTimeMargin = circleSize.max;
timeScaleX = d3.scaleBand()
.domain(columnNamesDomain)
.range([scaledTimeMargin, width - scaledTimeMargin*2]);
timeScaleY = d3.scaleBand()
.domain(timeScalesDomain)
.range([height - scaledTimeMargin, scaledTimeMargin*2]);
var centerCirclesInScaleBandOffset = timeScaleX.bandwidth() / 2;
return {
x: d3.forceX().strength(forceStrength),
y: d3.forceY().strength(forceStrength),
xpos: timePositionX,
ypos: timePositionY
};
function timePositionX(d) {
if ("Technology" in d){
return timeScaleX(d.Column) + centerCirclesInScaleBandOffset;
}
return (0 - width/2);
}
function timePositionY(d) {
if("Technology" in d){
return timeScaleY(d.Timescale);
}
return height/2;
}
}
}
function createForceSimulation() {
//Create forces for cases
forceSimulation_t = d3.forceSimulation();
forceSimulation_t
.force("x", forces.cases.x)
.force("y", forces.cases.y)
.force("collide", d3.forceCollide(forceCollide));
forceSimulation_t
.nodes(dummydata)
.on("tick", function() {
t_circles
.attr("transform", function(d) {return "translate(" + d.x + "," + d.y + ")"});
});
// we use the wrong position functions so update
updateForces(forceSimulation_t, forces.features);
//Create forces for features
forceSimulation_f = d3.forceSimulation();
forceSimulation_f
.force("x", forces.features.x)
.force("y", forces.features.y)
.force("collide", d3.forceCollide(forceCollide));
forceSimulation_f
.nodes(features)
.on("tick", function() {
f_circles
.attr("transform", function(d) {return "translate(" + d.x + "," + d.y + ")"});
});
}
function forceCollide(d) {
if ("Technology" in d){
return circleRadiusScale_t(d.Impact)+2;
}else{
return importance[d.Importance]+2;
}
}
function featureGrouping() {
return isChecked("#Features");
}
function casesGrouping() {
return isChecked("#Cases")
}
function timeScaleGrouping() {
return isChecked("#Time-Scales");
}
function updateForces(forceSim, forces) {
forceSim.force("x").x(forces.xpos);
forceSim.force("y").y(forces.ypos);
forceSim.alpha(0.5).restart();
}
function addGroupingListeners() {
addListener("#Features", forces.features);
addListener("#Cases", forces.cases);
addListener("#Time-Scales", forces.timeScales);
function addListener(selector, forces) {
d3.select(selector).on("click", function() {
updateForces(forceSimulation_t, forces);
updateForces(forceSimulation_f, forces);
toggleTimeScaleAxis(timeScaleGrouping());
//toggleFeaturesKey();
});
}
function toggleTimeScaleAxis(showAxes) {
var onScreenXOffset = 100,
offScreenXOffset = -40;
var onScreenYOffset = 120,
offScreenYOffset = 100;
if (d3.select(".x-axis").empty()) {
createAxes();
}
var xAxis = d3.select(".x-axis"),
yAxis = d3.select(".y-axis");
if (showAxes) {
translateAxis(xAxis, "translate(20," + (height - onScreenYOffset) + ")");
translateAxis(yAxis, "translate(" + onScreenXOffset + ",-50)");
} else {
translateAxis(xAxis, "translate(0," + (height + offScreenYOffset) + ")");
translateAxis(yAxis, "translate(" + offScreenXOffset + ",0)");
}
function createAxes() {
var numberOfTicks = 10,
tickFormat = ".0s";
var xAxis = d3.axisBottom(timeScaleX)
.ticks(numberOfTicks, tickFormat);
svg.append("g")
.attr("class", "x-axis")
.attr("transform", "translate(0," + (height + offScreenYOffset) + ")")
.call(xAxis)
.selectAll(".tick text")
.attr("font-size", "16px");
var yAxis = d3.axisLeft(timeScaleY)
.ticks(numberOfTicks, tickFormat);
svg.append("g")
.attr("class", "y-axis")
.attr("transform", "translate(" + offScreenXOffset + ",0)")
.call(yAxis);
}
function translateAxis(axis, translation) {
axis
.transition()
.duration(100)
.attr("transform", translation);
}
}
}
我的跑步结果是所有圆圈都在原点。为什么有一个中心力,同时也有X和Y力?forcesim的唯一节点是虚拟圆,您只更新力场中的案例圆。您的模拟永远不会结束,因为您设置了
alphaTarget
使用alpha
当选择功能或案例
选项时,您希望发生什么?其他组的节点会发生什么情况?在时间刻度
选项中是否两个组都可见?一次只能看到一组节点。功能节点应仅位于“功能”选项上,然后在选择其他选项时滑出屏幕。当选择“案例”选项时,“案例”节点应在屏幕上滑动,或者“时间刻度”f_圈
和t_圈
是不存在的SVG元素,选择将始终为空。#clipCircleXXX
在哪里?感谢rioV8,我已经在GitHub repo中更新了这些内容,但是仍然只有一组节点响应。另一个只是在初始力放置到中心后不移动。@SamKemp:在这三种情况下(3个单选按钮),您想要什么?
function createForces() {
var forceStrength = 0.15;
forces = {
features: createFeaturesForces(),
cases: createCasesForces(),
timeScales: createTimeScalesForces()
};
function createFeaturesForces() {
return {
x: d3.forceX(featurePositionX).strength(forceStrength),
y: d3.forceY(featurePositionY).strength(forceStrength),
xpos: featurePositionX,
ypos: featurePositionY
};
function featurePositionX(d) {
if ("Importance" in d) {
return (width /2);
}
return (width + 1000);
}
function featurePositionY(d) {
if ("Importance" in d) {
return (height /2);
}
return (height /2);
}
}
function createCasesForces() {
return {
x: d3.forceX(casesPositionX).strength(forceStrength),
y: d3.forceY(casesPositionY).strength(forceStrength),
xpos: casesPositionX,
ypos: casesPositionY
};
function casesPositionX(d) {
if ("Technology" in d) {
return (width /2);
}
return (0 - 1000);
}
function casesPositionY(d) {
if ("Technology" in d) {
return (height /2);
}
return (height / 2);
}
}
function createTimeScalesForces() {
var columnNamesDomain = columns.values();
var timeScalesDomain = timeScales.values();
var scaledTimeMargin = circleSize.max;
timeScaleX = d3.scaleBand()
.domain(columnNamesDomain)
.range([scaledTimeMargin, width - scaledTimeMargin*2]);
timeScaleY = d3.scaleBand()
.domain(timeScalesDomain)
.range([height - scaledTimeMargin, scaledTimeMargin*2]);
var centerCirclesInScaleBandOffset = timeScaleX.bandwidth() / 2;
return {
x: d3.forceX().strength(forceStrength),
y: d3.forceY().strength(forceStrength),
xpos: timePositionX,
ypos: timePositionY
};
function timePositionX(d) {
if ("Technology" in d){
return timeScaleX(d.Column) + centerCirclesInScaleBandOffset;
}
return (0 - width/2);
}
function timePositionY(d) {
if("Technology" in d){
return timeScaleY(d.Timescale);
}
return height/2;
}
}
}
function createForceSimulation() {
//Create forces for cases
forceSimulation_t = d3.forceSimulation();
forceSimulation_t
.force("x", forces.cases.x)
.force("y", forces.cases.y)
.force("collide", d3.forceCollide(forceCollide));
forceSimulation_t
.nodes(dummydata)
.on("tick", function() {
t_circles
.attr("transform", function(d) {return "translate(" + d.x + "," + d.y + ")"});
});
// we use the wrong position functions so update
updateForces(forceSimulation_t, forces.features);
//Create forces for features
forceSimulation_f = d3.forceSimulation();
forceSimulation_f
.force("x", forces.features.x)
.force("y", forces.features.y)
.force("collide", d3.forceCollide(forceCollide));
forceSimulation_f
.nodes(features)
.on("tick", function() {
f_circles
.attr("transform", function(d) {return "translate(" + d.x + "," + d.y + ")"});
});
}
function forceCollide(d) {
if ("Technology" in d){
return circleRadiusScale_t(d.Impact)+2;
}else{
return importance[d.Importance]+2;
}
}
function featureGrouping() {
return isChecked("#Features");
}
function casesGrouping() {
return isChecked("#Cases")
}
function timeScaleGrouping() {
return isChecked("#Time-Scales");
}
function updateForces(forceSim, forces) {
forceSim.force("x").x(forces.xpos);
forceSim.force("y").y(forces.ypos);
forceSim.alpha(0.5).restart();
}
function addGroupingListeners() {
addListener("#Features", forces.features);
addListener("#Cases", forces.cases);
addListener("#Time-Scales", forces.timeScales);
function addListener(selector, forces) {
d3.select(selector).on("click", function() {
updateForces(forceSimulation_t, forces);
updateForces(forceSimulation_f, forces);
toggleTimeScaleAxis(timeScaleGrouping());
//toggleFeaturesKey();
});
}
function toggleTimeScaleAxis(showAxes) {
var onScreenXOffset = 100,
offScreenXOffset = -40;
var onScreenYOffset = 120,
offScreenYOffset = 100;
if (d3.select(".x-axis").empty()) {
createAxes();
}
var xAxis = d3.select(".x-axis"),
yAxis = d3.select(".y-axis");
if (showAxes) {
translateAxis(xAxis, "translate(20," + (height - onScreenYOffset) + ")");
translateAxis(yAxis, "translate(" + onScreenXOffset + ",-50)");
} else {
translateAxis(xAxis, "translate(0," + (height + offScreenYOffset) + ")");
translateAxis(yAxis, "translate(" + offScreenXOffset + ",0)");
}
function createAxes() {
var numberOfTicks = 10,
tickFormat = ".0s";
var xAxis = d3.axisBottom(timeScaleX)
.ticks(numberOfTicks, tickFormat);
svg.append("g")
.attr("class", "x-axis")
.attr("transform", "translate(0," + (height + offScreenYOffset) + ")")
.call(xAxis)
.selectAll(".tick text")
.attr("font-size", "16px");
var yAxis = d3.axisLeft(timeScaleY)
.ticks(numberOfTicks, tickFormat);
svg.append("g")
.attr("class", "y-axis")
.attr("transform", "translate(" + offScreenXOffset + ",0)")
.call(yAxis);
}
function translateAxis(axis, translation) {
axis
.transition()
.duration(100)
.attr("transform", translation);
}
}
}