У меня проблема с моей диаграммой пузырей.
Ранее я использовал forceSimulation() с массивом объектов, и он работал. Теперь я изменил источник данных, и это не так, даже если консоль не отображает ошибок.
Мои данные - это объект под названием "lightWeight" со следующей структурой:
Я использую его для добавления кругов:
// 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().
Например, если я назову его несколько раз с разными данными, получившееся имитирование повлияет только на указанные данные?
Заранее спасибо!
Здесь есть пара проблем:
.data(d3.entries(lightWeight))
создает новый массив объектов, которые вы используете для привязки к DOM, тогда как .nodes(lightWeight)
пытается запустить lightWeight
моделирование на исходном объекте lightWeight
(он ожидает массив, поэтому это не сработает). Попробуйте сделать что-то вроде var lightWeightList = d3.entries(lightWeight);
до того, как начнется какой-либо из этого кода, и используйте этот массив для привязки к DOM и как параметр симуляции. Конечно, это должно дать понять, что вы можете столкнуться с другими проблемами, когда дело доходит до обновления узлов, на которые вы смотрите - переписывание lightWeightList
будет lightWeightList
любые предыдущие позиции узла (поскольку мы не можем видеть больше вашего кода, особенно, как вы бы назвали это во второй раз, у меня нет полезных идей).
.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; });
}