D3.js D3 wForceSimulation的多个实例

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}

希望有人能帮助我的第一个D3可视化。我有两个数据集,作为单独的组元素选择加载。我试图模拟气泡上的力,但是我只能让其中一组同时起作用。我觉得这与多次调用d3.forceSimulation有关,但我想不出解决办法。感谢您的帮助!(为草率的编码表示歉意,游戏时间不长!Github repo:)


您必须更新强制勾选中封闭的
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);
      }
    }
  }