Как отключить анимацию в силовом графе?

Есть ли способ отключить анимацию в графе, ориентированном на силу D3?

Я работаю с этим примером: https://bl.ocks.org/mbostock/4062045

Я хочу отобразить график без начальной анимации, то есть показать все узлы и связи в их конечных позициях.


person sosloucr    schedule 27.11.2017    source источник
comment
См. этот пример.   -  person Mark    schedule 27.11.2017
comment
@Mark OP даже не нужны веб-работники ... это тривиальная вещь.   -  person Gerardo Furtado    schedule 28.11.2017
comment
@GerardoFurtado, с тривиальным графиком, да, веб-воркер - это излишество. Но ваш код ниже выполняет замкнутый цикл в основном потоке браузера. Если бы вам потребовалось больше итераций для урегулирования вашей симуляции, браузер перестал бы отвечать.   -  person Mark    schedule 28.11.2017
comment
Но даже в этом случае нагрузка на браузер меньше, чем при обычной силе.   -  person Gerardo Furtado    schedule 28.11.2017
comment
Мне интересно, почему это было закрыто, поскольку почему этот код не работает?. Здесь явно не тот случай. В лучшем случае это будет слишком широкое. Тем не менее, два пользователя с золотым значком для D3 прокомментировали здесь, не видели причин закрывать это. Я только что проголосовал за открытие.   -  person Gerardo Furtado    schedule 29.11.2017
comment
Я голосую за повторное открытие этого вопроса, снова... как я уже говорил в комментарии выше, неясно, почему мод закрыл его как не по теме.   -  person Gerardo Furtado    schedule 19.06.2019
comment
@GerardoFurtado Учитывая стандарты SO, вопрос, вероятно, следует закрыть. Тем не менее, это поместит сообщение в длинный список популярных вопросов, получивших большое количество голосов, ответы на которые получили большое количество голосов, которые закрыты или даже удалены из-за того, что не вписываются в узкий набор правил этого сайта, в результате чего теряется ценная информация. Вот что происходит, если вы предпочитаете форму содержанию. На Meta есть множество дискуссий по этому поводу. В любом случае, я поддерживаю вашу апелляцию, а также проголосовал за возобновление работы. Хотя я не очень уверен.   -  person altocumulus    schedule 19.06.2019
comment
@altocumulus Я не согласен в основном с причиной: почему этот код не работает?. Тут явно не тот случай. Слишком широкий может быть немного лучше, но даже в этом случае вы лучше меня знаете, что такого рода вопросы довольно распространены в сообществе D3: OP делится кодом и запрашивает модификацию в каком-то аспекте D3. этого. Я не верю, что это так низко в отношении С.О. стандарты.   -  person Gerardo Furtado    schedule 19.06.2019
comment
@GerardoFurtado Согласен, поэтому я проголосовал за повторное открытие. И я поддерживаю вашу оценку того, что близкая причина, если таковая имеется, должна быть слишком широкой. Есть так много вопросов самого низкого качества, которые, безусловно, заслуживают того, чтобы их удалили, и я являюсь сторонником строгого отрицательного, закрытого голосования и удаления этих сообщений для поддержания высокого уровня качества. Я думаю, что основная проблема с этим вопросом заключается в том, что вы должны быть экспертом в области D3, чтобы понять, что он на самом деле по существу, лаконичен и заслуживает тщательного ответа.   -  person altocumulus    schedule 19.06.2019
comment
@meagar Это работает? Могу ли я на самом деле пропинговать близкого избирателя? Не могли бы вы пересмотреть свое решение закрыть голосование по этому вопросу, прочитав комментарии и учитывая голоса за вопрос и ответы? Поскольку d3 является довольно узким тегом, я сомневаюсь, что у нас когда-либо будет достаточно избирателей, чтобы снова открыть его обычным способом.   -  person altocumulus    schedule 19.06.2019


Ответы (2)


ИЗМЕНИТЬ

Этот метод просто скрывает анимационную часть симуляции. См. ответ Херардо Фуртадо, который выполняет моделирование без рисования промежуточных результатов, что означает, что пользователь не нужно ждать, пока решение медленно развивается.

========

«Анимация» на самом деле является симуляцией. Можно поиграть со временем запуска симуляции, но это может означать, что узлы застревают на локальном минимуме — подробнее см. в документации.

У вас есть возможность добавить прослушиватель к событию 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>

person John    schedule 27.11.2017

Несмотря на то, что на этот вопрос уже есть принятый ответ, предлагаемое решение не является правильным способом отключения анимации в силовой диаграмме D3. . Браузер по-прежнему перемещает узлы и ссылки с каждым тиком! Вы просто не видите, как они перемещаются, но браузер перемещает их, выполняя много вычислений и тратя много времени/ресурсов. Кроме того, вам не нужна серверная часть для этого.

Мой ответ предлагает другое решение, которое на самом деле не рисует анимацию. Вы можете увидеть это, например, в этом коде Майка Бостока (создателя D3).

Этому решению легко следовать, если вы понимаете, что такое функция tick: это просто функция, которая вычисляет все позиции в симуляции и продвигается на один шаг вперед. Несмотря на то, что подавляющее большинство принудительно ориентированных графов D3 рисуют узлы и связи на каждом тике, вам не нужно этого делать.

Вот что вы можете сделать:

  1. Остановите симуляцию, используя 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
    
  2. Запустите симуляцию, ничего не рисуя. Это самый важный шаг: вам не нужно перемещать элементы на каждом такте. Здесь я запускаю 300 тиков, что примерно соответствует числу по умолчанию:

    for (var i = 0; i < 300; ++i) simulation.tick();
    
  3. Затем просто используйте свойства, созданные симуляцией (x, y, source, target), чтобы нарисовать круги и линии только один раз:

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

Вот связанные блоки только с этими изменениями: /85ced3ea82a4bed20a2010530562b655d8f6e464

Сравните время этого решения со временем решения «скрытия узлов» (принятый ответ). Вот этот намного быстрее. В моих тестах я получил следующие результаты:

  • решение «скрытие узлов»: около 5000 мс
  • Это решение: около 200 мс

То есть в 25 раз быстрее.

PS: для простоты я убрал функцию ticked в разветвленных блоках. Если вы хотите перетащить узлы, просто добавьте их обратно.


ИЗМЕНИТЬ для D3 v5.8

Теперь, когда D3 v5.8 позволяет передавать количество взаимодействий в simulation.tick(), вам больше не нужен цикл for. Итак, вместо:

for (var i = 0; i < 300; ++i) simulation.tick();

Вы можете просто сделать:

simulation.tick(300);
person Gerardo Furtado    schedule 28.11.2017
comment
Я был бы рад удалить свой ответ, но я не уверен, что смогу. Может быть, я могу отметить это? - person John; 28.11.2017
comment
@ Джон, я думаю, вам не следует удалять свой ответ: в нем есть информация, и мы никогда не должны удалять информацию. Тем не менее, у вас есть пара голосов против (вы этого не видите, но у вас есть 1 голос за и 2 голоса против)... Итак, вы можете просто отредактировать его, объяснив, что вы просто скрываете симуляцию. Таким образом, вы даете понять OP, что пользователь не видит анимацию, но анимация работает. - person Gerardo Furtado; 28.11.2017
comment
Спасибо за предложение! Надеюсь, это должно направить людей к лучшему ответу. - person John; 28.11.2017
comment
@GerardoFurtado Вы уверены, что ссылка на пример Майка Бостока (все еще) верна? В его коде я не вижу сходства с кодом в вашем ответе. - person borisdiakur; 06.03.2020
comment
@GerardoFurtado, как мы можем создать тот же график с разрешенным щелчком и перетаскиванием? Просто без гравитации/физики? - person Tamjid; 18.06.2020
comment
@Tamjid Вам просто нужно создать пользовательскую функцию перетаскивания, а не ту, что в коде (это просто остаток от исходного кода). - person Gerardo Furtado; 18.06.2020
comment
@borisdiakur Bostock переместил все свои примеры с bl.ocks.org в Observable, что действительно сильно отличается. - person Gerardo Furtado; 18.06.2020