d3.js forceSimulation () с записями объекта

1


У меня проблема с моей диаграммой пузырей.
Ранее я использовал forceSimulation() с массивом объектов, и он работал. Теперь я изменил источник данных, и это не так, даже если консоль не отображает ошибок.
Мои данные - это объект под названием "lightWeight" со следующей структурой: Изображение 174551
Я использую его для добавления кругов:

// draw circles
var node = bubbleSvg.selectAll("circle")
   .data(d3.entries(lightWeight))
   .enter()
   .append("circle")
   .attr('r', function(d) { return scaleRadius(d.value.length)})
   .attr("fill", function(d) { return colorCircles(d.key)})
   .attr('transform', 'translate(' + [w/2, 150] + ')');

Затем я создаю симуляцию:

// simulate physics
  var simulation = d3.forceSimulation()
    .nodes(lightWeight)
    .force("charge", d3.forceCollide(function(d) { return d.r + 10; }))
    .force("x", d3.forceX())
    .force("y", d3.forceY())
  .on("tick", ticked); // updates the position of each circle (from function to DOM)

  // call to check the position of each circle
   function ticked(e) {
      node.attr("cx", function(d) { return d.x; })
          .attr("cy", function(d) { return d.y; });
  }

Но круги остаются на вершине друг друга и не становятся пузырьковой диаграммой, как раньше.
Я прошу прощения, если это, вероятно, немой вопрос, я новичок в d3 и мало что понимаю о том, как работает forceSimulation().
Например, если я назову его несколько раз с разными данными, получившееся имитирование повлияет только на указанные данные?
Заранее спасибо!

Теги:
d3.js
force-layout

1 ответ

1
Лучший ответ

Здесь есть пара проблем:

  1. Вы используете разные наборы данных для рендеринга и силовое моделирование, то есть: .data(d3.entries(lightWeight)) создает новый массив объектов, которые вы используете для привязки к DOM, тогда как .nodes(lightWeight) пытается запустить lightWeight моделирование на исходном объекте lightWeight (он ожидает массив, поэтому это не сработает).

Попробуйте сделать что-то вроде var lightWeightList = d3.entries(lightWeight); до того, как начнется какой-либо из этого кода, и используйте этот массив для привязки к DOM и как параметр симуляции. Конечно, это должно дать понять, что вы можете столкнуться с другими проблемами, когда дело доходит до обновления узлов, на которые вы смотрите - переписывание lightWeightList будет lightWeightList любые предыдущие позиции узла (поскольку мы не можем видеть больше вашего кода, особенно, как вы бы назвали это во второй раз, у меня нет полезных идей).

  1. Особенно, если вы планируете повторно вызвать этот код, есть еще одна проблема: способ, которым вы .enter() вызов .enter() означает, что node будет ссылаться только на ввод ввода, то есть, если вы снова .enter() этот код, сила симуляция будет только обновлять новые узлы внутри ticked.

С D3 я обнаружил, что хорошая привычка состоит в том, чтобы сохранить ваш выбор в отдельных переменных, например:

var lightWeightList = d3.entries(lightWeight);

// ...

var nodes = bubbleSvg.selectAll('circle')
  .data(lightWeightList);
var nodesEnter = nodes.enter()
  .append('circle');
// If you're using D3 v4 and above, you'll need to merge the selections:
nodes = nodes.merge(nodesEnter);
nodes.select('circle')
     .attr('r', function(d) { return scaleRadius(d.value.length)})
     .attr('fill', function(d) { return colorCircles(d.key)})
     .attr('transform', 'translate(' + [w/2, 150] + ')');

// ...

var simulation = d3.forceSimulation()
  .nodes(lightWeightList)
  .force("charge", d3.forceCollide(function(d) { return d.r + 10; }))
  .force("x", d3.forceX())
  .force("y", d3.forceY())
  .on("tick", ticked);

function ticked(e) {
  nodes.attr("cx", function(d) { return d.x; })
       .attr("cy", function(d) { return d.y; });
}

Ещё вопросы

Сообщество Overcoder
Наверх
Меню