【问题标题】:D3.js v5 modular swarm clusters (variable radius?)D3.js v5 模块化集群(可变半径?)
【发布时间】:2020-09-10 16:10:30
【问题描述】:

我想创建一个视觉效果,其中一个群体包含一个大圆圈和一堆围绕它的卫星圆圈。为了简单演示,我准备了一个小版本的数据集;数组中的每个项目都应该有一个大圆圈,然后有许多小圆圈紧贴它:

var data = [
  {'wfoe':'wfoe1','products':d3.range(20)},
  {'wfoe':'wfoe2','products':d3.range(40)},
  {'wfoe':'wfoe3','products':d3.range(10)}
];

这是我的进度记录:

var margins = {
  top: 100,
  bottom: 300,
  left: 100,
  right: 100
};

var height = 250;
var width = 900;

var totalWidth = width + margins.left + margins.right;
var totalHeight = height + margins.top + margins.bottom;

var svg = d3.select('body')
  .append('svg')
  .attr('width', totalWidth)
  .attr('height', totalHeight);

var graphGroup = svg.append('g')
  .attr('transform', "translate(" + margins.left + "," + margins.top + ")");



var data = [
  {'wfoe':'wfoe1','products':d3.range(20)},
  {'wfoe':'wfoe2','products':d3.range(40)},
  {'wfoe':'wfoe3','products':d3.range(10)}
];

var columns = 4;
var spacing = 250;
var vSpacing = 250;

var fmcG = graphGroup.selectAll('.fmc')
  .data(data)
  .enter()
  .append('g')
  .attr('class', 'fmc')
  .attr('id', (d, i) => 'fmc' + i)
  .attr('transform', (d, k) => {
var horSpace = (k % columns) * spacing;
var vertSpace = ~~((k / columns)) * vSpacing;
return "translate(" + horSpace + "," + vertSpace + ")";
  });

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

var rScale = d3.scaleThreshold()
  .range([50,5])
  .domain([0,1]);

data.forEach(function(d, i) {
  d.x = (i % columns) * spacing;
  d.y = ~~((i / columns)) * vSpacing;
});


var simulation = d3.forceSimulation(data)
  .force("x", d3.forceX(function(d,i) {
return (i % columns) * spacing;
  }).strength(0.1))
  .force("y", d3.forceY(function(d,i) {
return ~~((i / columns)) * vSpacing;
  }).strength(0.01))
  .force("collide", d3.forceCollide(function(d,i) { return rScale(i)}))
  .stop();

simulation.tick(75);

fmcG.selectAll(null)
  .data(data)
  .enter()
  .append("circle")
  .attr("r", function(d,i) {
return rScale(i)
  })
  .attr("cx", function(d) {
return d.x;
  })
  .attr("cy", function(d) {
return d.y;
  })
  .style('fill',"#003366");
<script src="https://d3js.org/d3.v5.min.js"></script>

我想快速指出,大圆圈不代表任何数据点(它们只是用来存放名称/徽标)。我只是认为将它包含在模拟数据中将是为群体圈引入所需的力逻辑的最简单方法。我认为一个优雅的解决方案是使用阈值比例并让第一个 (i=0) 基准始终是最大的圆。这就是我的意思:

var rScale = d3.scaleThreshold()
  .range([0, 1])
  .domain([50, 5]);

fmcG.selectAll(null)
  .data(data)
  .enter()
  .append("circle")
  .attr("r", function(d,i) {
    return rScale(i)
  })
  .attr("cx", function(d) {
    return d.x;
  })
  .attr("cy", function(d) {
    return d.y;
  })
  .style('fill',"#003366");

我上面提到的结果(三个大圆圈,周围都是小圆圈)没有实现,实际上附加的圆圈很少,可变半径组件似乎没有像我想象的那样工作。 (日志中也没有显示错误)。

问题

如果适用于样本数据集,我如何迭代地创建从一个大圆圈开始并在初始大圆圈周围附加后续较小圆圈的群?

【问题讨论】:

    标签: javascript d3.js


    【解决方案1】:

    您可以使用力模拟,如下所示,只有这样会给出不确定的结果。但是,当您想逐渐添加更多节点时,它确实很好。在下面的解决方案中,我给了所有相关节点一个到中心节点的链接,但没有绘制它。这使得链接节点可以大量吸引。

    另一方面,如果您希望 D3 为您找到最佳包装解决方案,您也可以使用bubble chart,而无需对其施加压力。唯一的缺点是您每次都必须调用所有节点的打包函数,并且其他节点可能会因为新节点而移动。

    var margins = {
      top: 100,
      bottom: 300,
      left: 100,
      right: 100
    };
    
    var height = 250;
    var width = 900;
    
    var totalWidth = width + margins.left + margins.right;
    var totalHeight = height + margins.top + margins.bottom;
    
    var svg = d3.select('body')
      .append('svg')
      .attr('width', totalWidth)
      .attr('height', totalHeight);
    
    var graphGroup = svg.append('g')
      .attr('transform', "translate(" + margins.left + "," + margins.top + ")");
    
    var data = [{
        'wfoe': 'wfoe1',
        'products': d3.range(20).map(function(v) {
          return v.toString() + '_wfoe1';
        })
      },
      {
        'wfoe': 'wfoe2',
        'products': d3.range(40).map(function(v) {
          return v.toString() + '_wfoe2';
        })
      },
      {
        'wfoe': 'wfoe3',
        'products': d3.range(10).map(function(v) {
          return v.toString() + '_wfoe3';
        })
      }
    ];
    
    var columns = 4;
    var spacing = 250;
    var vSpacing = 250;
    
    function dataToNodesAndLinks(d) {
      // Create one giant array of points and
      // one link between each wfoe and each product
      var nodes = [{
        id: d.wfoe,
        center: true
      }];
      var links = [];
    
      d.products.forEach(function(p) {
        nodes.push({
          id: p,
          center: false
        });
        links.push({
          source: d.wfoe,
          target: p
        });
      });
      return {
        nodes: nodes,
        links: links
      };
    }
    
    var fmcG = graphGroup.selectAll('.fmc')
      .data(data.map(function(d, i) {
        return dataToNodesAndLinks(d, i);
      }))
      .enter()
      .append('g')
      .attr('class', 'fmc')
      .attr('id', (d, i) => 'fmc' + i)
      .attr('transform', (d, k) => {
        var horSpace = (k % columns) * spacing;
        var vertSpace = ~~((k / columns)) * vSpacing;
        return "translate(" + horSpace + "," + vertSpace + ")";
      });
    
    var xScale = d3.scalePoint()
      .range([0, width])
      .domain([0, 100]);
    
    var rScale = d3.scaleThreshold()
      .range([50, 5])
      .domain([0, 1]);
    
    fmcG.selectAll("circle")
      .data(function(d) {
        return d.nodes;
      })
      .enter()
      .append("circle")
      .attr("id", function(d) {
        return d.id;
      })
      .attr("r", function(d, i) {
        return d.center ? rScale(i) * 5 : rScale(i);
      })
      .style('fill', function(d) { return d.center ? "darkred" : "#003366"; })
    
    fmcG
      .each(function(d, i) {
        d3.forceSimulation(d.nodes)
          .force("collision", d3.forceCollide(function(d) {
            return d.center ? rScale(i) * 5 : rScale(i);
          }))
          .force("center", d3.forceCenter(0, 0))
          .force("link", d3
            .forceLink(d.links)
            .id(function(d) {
              return d.id;
            })
            .distance(0)
            .strength(2))
          .on('tick', ticked);
      });
    
    function ticked() {
      fmcG.selectAll("circle")
        .attr("transform", function(d) {
          return "translate(" + d.x + "," + d.y + ")";
        });
    }
    <script src="https://d3js.org/d3.v5.js"></script>

    【讨论】:

    • 非常酷,谢谢!只是想知道如何禁用模拟的输入动画 - 有没有简单的方法?
    猜你喜欢
    • 1970-01-01
    • 2020-05-04
    • 1970-01-01
    • 1970-01-01
    • 2018-09-07
    • 2020-04-19
    • 1970-01-01
    • 2018-09-14
    • 2019-07-10
    相关资源
    最近更新 更多