Как сделать d3.js прямоугольной формы силы тяжести?

В раскладке силы d3.js значение гравитации делает раскладку круглой.

введите описание изображения здесь Однако я бы хотел сделать расклад сил прямоугольным, в то время как узлы имеют отрицательный заряд и даже расстояние. (как выше)

Есть ли способ сделать расклад сил прямоугольным?

Могу ли я добиться этого, изменив функцию галочки?

Вот мой код:

var background = d3.select('.background');

var width = background.node().getBoundingClientRect().width,
 height = background.node().getBoundingClientRect().height;

var nodes = d3.range(50).map(function(d, i) {
  return {
        text: "Hello"
  };
 });

var messages = background.selectAll('.message')
 .data(nodes)
 .enter()
  .append('div')
  .attr('class', 'message')
  .text(d => d.text)
  .each(function(d, i) {
   d.width = this.getBoundingClientRect().width;
   d.height = this.getBoundingClientRect().height;
  });

var force = d3.layout.force()
 .gravity(1/88)
 .charge(-50)
 .nodes(nodes)
 .size([width, height]);

messages.call(force.drag);

force.on('tick', function(e) {
 messages.each(d => {
  d.x = Math.max(0, Math.min(width - d.width, d.x));
  d.y = Math.max(0, Math.min(height - d.height, d.y));
 });
 
 messages.style('left', d => `${d.x}px`)
  .style('top', d => `${d.y}px`);
});

force.start();
body {
  padding: 0;
  margin: 0;
}
.background {
  width: 100%;
  height: 100vh;
  border: 1px solid #007aff;
}
.message {
  display: inline-block;
  font-family: sans-serif;
  border-radius: 100vh;
  color: white;
  padding: 10px;
  background-color: #007aff;
  position: absolute;
}
.boundary {
  display: inline-block;
  width: 10px;
  height: 10px;
  background-color: red;
  position: absolute;
}
<script src="https://d3js.org/d3.v3.min.js"></script>
<body>
  <div class="background">
  </div>
</body>

1 ответ

Решение

ОК, отредактируйте 3: в d3v4, forceCollide может использоваться для установки минимальных расстояний между узлами, если вы затем используете положительную силу, это объединяет узлы вместе, помогая установить их на равном расстоянии друг от друга (хотя для кругов лучше, чем для прямоугольников)):

var force = d3.forceSimulation(nodes)
.force("charge", d3.forceManyBody().strength(-10))
.force("collide", d3.forceCollide(30).strength(1).iterations(1))
.force('x', d3.forceX(width/2).strength(0.5))
.force('y', d3.forceY(height/2).strength(10));

Предполагая, что узлы находятся в прямоугольной SVG, ограничение их в пределах центра SVG может помочь выровнять края:

position.nodes(nodes).on('tick', function ticks() {
        nodes.attr("cx", function(d) {
            return d.x = Math.max(20, Math.min(width + 20, d.x))
        }).attr("cy", function(d) {
            return d.y = Math.max(20, Math.min(height + 20, d.y));
        })
    });

и игра с силами силы может помочь привлечь их вдоль оси Y:

var position = d3.forceSimulation(nodes).force("charge", d3.forceManyBody())
.force('x', d3.forceX(width/2).strength(1))
.force('y', d3.forceY(height/2).strength(5));

возиться здесь

Кажется, что силы в v4 гораздо более настраиваемы, чем в v3, я думаю, что forceCollide интегрировал некоторые обходные пути в библиотеку. Таким образом, вы можете попробовать найти обходной путь v3 или, возможно, изучить возможность обновления до v4.

В v3 я поиграл с гравитацией, зарядом и ограничением x и y, чтобы немного лучше сохранить узлы в коробке, возьмемся за основу. Но я не знаю достаточно о v3, чтобы улучшить его намного выше этого.

Другие вопросы по тегам