Есть ли способ отключить анимацию в графе, ориентированном на силу D3?
Я работаю с этим примером: https://bl.ocks.org/mbostock/4062045
Я хочу отобразить график без начальной анимации, то есть показать все узлы и связи в их конечных позициях.
Есть ли способ отключить анимацию в графе, ориентированном на силу D3?
Я работаю с этим примером: https://bl.ocks.org/mbostock/4062045
Я хочу отобразить график без начальной анимации, то есть показать все узлы и связи в их конечных позициях.
ИЗМЕНИТЬ
Этот метод просто скрывает анимационную часть симуляции. См. ответ Херардо Фуртадо, который выполняет моделирование без рисования промежуточных результатов, что означает, что пользователь не нужно ждать, пока решение медленно развивается.
========
«Анимация» на самом деле является симуляцией. Можно поиграть со временем запуска симуляции, но это может означать, что узлы застревают на локальном минимуме — подробнее см. в документации.
У вас есть возможность добавить прослушиватель к событию end
, которое срабатывает после завершения моделирования. Я создал фрагмент, в котором график изначально скрыт, а затем появляется после завершения моделирования.
Альтернативой может быть рендеринг диаграммы на стороне сервера (если это возможно), а затем предоставление готового SVG, которым можно в дальнейшем манипулировать с помощью d3.
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var color = d3.scaleOrdinal(d3.schemeCategory20);
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) {
return d.id;
}))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2))
.on('end', function() {
svg.classed('hidden', false)
d3.select('#loading').remove()
});
// I wasn't able to get the snippet to load the original data from https://bl.ocks.org/mbostock/raw/4062045/miserables.json so this is a copy hosted on glitch
d3.json("https://cdn.glitch.com/8e57a936-9a34-4e95-a03d-598e5738f44d%2Fmiserables.json", function(error, graph) {
if (error) {
console.log(error)
};
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("stroke-width", function(d) {
return Math.sqrt(d.value);
});
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("r", 5)
.attr("fill", function(d) {
return color(d.group);
})
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
node.append("title")
.text(function(d) {
return d.id;
});
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
function ticked() {
link
.attr("x1", function(d) {
return d.source.x;
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
return d.target.x;
})
.attr("y2", function(d) {
return d.target.y;
});
node
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
}
});
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
.links line {
stroke: #999;
stroke-opacity: 0.6;
}
.nodes circle {
stroke: #fff;
stroke-width: 1.5px;
}
.hidden {
visibility: hidden
}
img {
display: block;
margin-left: auto;
margin-right: auto;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<img id ="loading" src="http://thinkfuture.com/wp-content/uploads/2013/10/loading_spinner.gif" />
<svg width="960" height="600" class="hidden"></svg>
Несмотря на то, что на этот вопрос уже есть принятый ответ, предлагаемое решение не является правильным способом отключения анимации в силовой диаграмме D3. . Браузер по-прежнему перемещает узлы и ссылки с каждым тиком! Вы просто не видите, как они перемещаются, но браузер перемещает их, выполняя много вычислений и тратя много времени/ресурсов. Кроме того, вам не нужна серверная часть для этого.
Мой ответ предлагает другое решение, которое на самом деле не рисует анимацию. Вы можете увидеть это, например, в этом коде Майка Бостока (создателя D3).
Этому решению легко следовать, если вы понимаете, что такое функция tick
: это просто функция, которая вычисляет все позиции в симуляции и продвигается на один шаг вперед. Несмотря на то, что подавляющее большинство принудительно ориентированных графов D3 рисуют узлы и связи на каждом тике, вам не нужно этого делать.
Вот что вы можете сделать:
Остановите симуляцию, используя stop()
, сразу после ее определения:
var simulation = d3.forceSimulation(graph.nodes)
.force("link", d3.forceLink().id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2))
.stop();//stop the simulation here
Запустите симуляцию, ничего не рисуя. Это самый важный шаг: вам не нужно перемещать элементы на каждом такте. Здесь я запускаю 300 тиков, что примерно соответствует числу по умолчанию:
for (var i = 0; i < 300; ++i) simulation.tick();
Затем просто используйте свойства, созданные симуляцией (x
, y
, source
, target
), чтобы нарисовать круги и линии только один раз:
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
Вот связанные блоки только с этими изменениями: /85ced3ea82a4bed20a2010530562b655d8f6e464
Сравните время этого решения со временем решения «скрытия узлов» (принятый ответ). Вот этот намного быстрее. В моих тестах я получил следующие результаты:
То есть в 25 раз быстрее.
PS: для простоты я убрал функцию ticked
в разветвленных блоках. Если вы хотите перетащить узлы, просто добавьте их обратно.
ИЗМЕНИТЬ для D3 v5.8
Теперь, когда D3 v5.8 позволяет передавать количество взаимодействий в simulation.tick()
, вам больше не нужен цикл for
. Итак, вместо:
for (var i = 0; i < 300; ++i) simulation.tick();
Вы можете просто сделать:
simulation.tick(300);