d3.join () введите вызывается вместо обновления? - PullRequest
1 голос
/ 10 октября 2019

Я пытаюсь заставить D3 обновлять элемент SVG раз в секунду, передавая свежий объект Date в функцию data(), а затем join() для обновления страницы. Я на самом деле делаю это в Observable записной книжке с использованием Promises.tick(), но я думаю, что приведенный ниже код ( полный исходный код ) является приблизительным приближением соответствующих битов. Моя конечная цель - сделать часы, но код здесь - минимальная демонстрация проблемы, с которой я столкнулся.

Проблема с этим кодом заключается в том, что функция update в join() никогда не вызывается - каждыйвремя вокруг цикла вместо этого вызывается функция enter, в результате чего каждый раз добавляется новый text. Поскольку у меня есть один элемент данных и один элемент, я ожидаю, что будет вызван join update, а не enter. Я знаю, что ключевая функция в data() ( пример ) важна для определения того, что изменилось, но я понимаю, что ключом по умолчанию является индекс массива, который здесь всегда должен быть 0. В любом случае, определение другой ключевой функции (от функции идентификации до возврата константы) не помогло.

while (true) {
  var data = [new Date()];

  svg
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
    .selectAll("text")
    .data(data)
    .join(
      enter => enter.append("text").text(d => "enter: " + d),
      update => update.text(d => "update: " + d)
    );

  await new Promise(resolve => setTimeout(resolve, 1000));
}

1 Ответ

2 голосов
/ 11 октября 2019

Посмотрите на это:

svg
  .append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
  //etc...

Вы добавляете новый элемент <g> каждый раз, когда запускаете цикл, и все выбранные элементы (текстовые или любые другие) послеэто будет ссылаться на эту недавно добавленную группу, то есть selectAll выделяет все тексты внутри этой новой добавленной группы. Очевидно, нет ни одного. Вот почему у вас всегда есть только выбор ввода, но не выбор обновления.

Посмотрите на эту демонстрацию, воспроизводящую вашу проблему:

var body = d3.select("body");

function foo(data) {
  var div = body.append("div")
    .selectAll("p")
    .data(data)
    .join(
      enter => enter.append("p").html(d => "enter: " + d),
      update => update.html(d => "update: " + d)
    );
};

foo([Math.random()]);
d3.interval(function() {
  foo([Math.random()])
}, 1000)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.8.0/d3.min.js"></script>

Предположим, что вы хотите добавить группу только один раз , вы можете сделать что-то вроде этого:

var g = svg.selectAll(".myGroup")
  .data([true]);

g = g.append("g")
  .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
  .merge(g);

g.selectAll("text")
  .data(data)
  .join(
    enter => enter.append("text").text(d => "enter: " + d),
    update => update.text(d => "update: " + d)
  );

Здесь data([true]) означает, что сами данные не имеют значения, это просто одно истинное значение для добавления одного элемента группы.

Вот демонстрация, показывающая, как это работает:

var body = d3.select("body");

function foo(data) {
  var div = body.selectAll(".myDiv")
    .data([true]);

  div = div.enter()
    .append("div")
    .attr("class", "myDiv")
    .merge(div);

  div.selectAll("p")
    .data(data)
    .join(
      enter => enter.append("p").html(d => "enter: " + d),
      update => update.html(d => "update: " + d)
    );
};

foo([Math.random()])
d3.interval(function() {
  foo([Math.random()])
}, 1000)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.8.0/d3.min.js"></script>
...