Подтвердить что ты не робот

Манипулировать элементами путем связывания новых данных

Я пытаюсь решить, как обновить некоторые элементы D3.js, просто привязывая новые данные. Я не уверен, возможно ли это или нет, но похоже, что это должно быть.

Итак, сначала я создал четыре круга SVG и установил смещение cx как функцию данных:

 <body><div id="container"></div></body>

 var svg = d3.select("div.container").append("svg")
     .attr("class", "chart")
     .attr("width", 1000)
     .attr("height", 500);

  // Create initial data and display
  var data = [0, 10, 20, 30];
  var circle = svg.selectAll("circle")
      .data(data)
      .enter()
      .append('circle')
      .attr("cx", function(d) {
        return d*10;
      })
      .attr("cy", 100)
      .attr("r", 10)
      .style("fill", "steelblue");

Далее я добавляю новые данные и переход. Я бы ожидал увидеть, что круги медленно перемещаются к новой позиции (чего я пытаюсь достичь), но они этого не делают:

  var data1 = [40, 50, 60, 70];
  circle.data(data1).transition().duration(2500);

Я делаю базовую ошибку? Возможно, у меня неправильный выбор. Или просто невозможно обновить элементы только путем манипулирования данными?

UPDATE: если я делаю console.log(circle), тогда я вижу массив элементов окружения SVG, чего я ожидаю.

4b9b3361

Ответ 1

Мощно (но раздражающе) D3.js иногда заставляет вас повторять себя для простых визуализаций. Если вы сообщите ему, что хотите создать элемент с атрибутами, полученными из данных определенным образом, а затем вы хотите перевести эти атрибуты на новые данные, вы должны снова рассказать о том, как получить значения (в случае, если вы захотите делать что-то другое, например, различную визуальную компоновку). Как говорит @Andrew, вы должны сказать, что делать, когда он переходит.

Вы можете обойти эту "проблему", следуя этой схеме:

var foo = d3.select('foo');
function redraw(someArray){
  var items = foo.selectAll('bar').data(someArray);
  items.enter().append('bar');
  items.exit().remove();
  items
    .attr('foo',function(d){ return d });
}

В 2.0, когда вы append() новые элементы в enter(), они автоматически добавляются к исходному привязке с привязкой к данным, поэтому вызовы на attr() и то, что позже будут применяться к ним. Это позволяет использовать один и тот же код для установки начальных значений и обновления значений.

Поскольку вы не хотите повторно создавать оболочку SVG для каждого обновления, вы должны создать это за пределами функции redraw.

Если вы хотите выполнить переходы:

function redraw(someArray){
  var items = foo.selectAll('bar').data(someArray);
  items.enter().append('bar')
    .attr('opacity',0)
    .attr('foo',initialPreAnimationValue);
  items.exit().transition().duration(500)
    .attr('opacity',0)
    .remove();
  items.transition.duration(500)
    .attr('opacity',1)
    .attr('foo',function(d){ return d });
}

Обратите внимание, что указанное выше связывает объекты с данными по индексу. Если вы хотите, чтобы промежуточная точка данных удаляла эту точку данных перед ее удалением (вместо удаления последнего элемента и преобразования каждого другого элемента, чтобы он выглядел как этот), вы должны указать уникальное (неиндексное) строковое значение для связывания каждый элемент:

var data = [
  {id:1, c:'red',   x:150},
  {id:3, c:'#3cf',  x:127},
  {id:2, c:'green', x:240},
  {id:4, c:'red',   x:340}
];
myItems.data(data,function(d){ return d.id; });

Вы можете увидеть пример этого и сыграть с ним в прямом эфире на моей игровой площадке D3.js.

Во-первых, посмотрите, что произойдет, когда вы закомментируете одну из строк данных, а затем верните ее снова. Затем удалите параметр ƒ('id') из вызова data() в строке 4 и снова попробуйте комментировать и в строках данных.

Изменить. В качестве альтернативы, прокомментированный прославленным mbostock, вы можете использовать selection.call() вместе с функцией многократного использования в качестве способа DRY для вашего кода:

var foo = d3.select('foo');
function redraw(someArray){
  var items = foo.selectAll('bar').data(someArray);
  items.enter().append('bar').call(setEmAll);
  items.exit().remove();
  items.call(setEmAll);
}

function setEmAll(myItems){
  myItems
    .attr('foo',function(d){ return d*2 })
    .attr('bar',function(d){ return Math.sqrt(d)+17 });
}

Как показано выше, .call() вызывает функцию и проходит по выбору в качестве аргумента, так что вы можете выполнить ту же настройку в своем выборе в нескольких местах.

Ответ 2

Хорошо, теперь у меня это получилось! Вам нужно сказать, ЧТО переход:

circle.data(data1).transition().duration(2500).attr("cx", function(d) {
      return d*10;
  });

Ответ 3

Очень хороший ресурс для добавления новых данных к существующей визуализации - это официальный учебник по обновлению данных.

http://bl.ocks.org/mbostock/3808221

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