2015-05-05 2 views
1

У меня возникли трудности с добавлением и удалением узлов на основе пользовательского ввода, который был решен путем обновления всего набора объектов, вставляемых в force.nodes() каждый раз, когда которые они хотели просмотреть из списка флажков.d3: Добавление и удаление force.nodes на основе значений ползунка

Обновление с ползунка, однако, я думаю, требуется более тонкое прикосновение - я не хочу обновлять весь набор при каждом перемещении ползунка. Я хочу нажимать и вставлять узлы в и из Force.nodes().

С моим текущим кодом узлы выходят просто отлично - они просто не вернуться в jsfiddle здесь - https://jsfiddle.net/hiwilson1/ancmtxux/3/

Это часть вызывает проблемы.

function brushed() { 
    var exists; 

    //var newd = new Date(2013, 05, 01) 

    data.forEach(function(d, i) { 
     //if data point in range (between extent 0 and 1) 
     if (d.date >= brush.extent()[0] && d.date <= brush.extent()[1]) { 
      exists = force.nodes().some(function(node, i) { 
       //check if data point already exists in force.nodes() 
       return (node.mIndex == d.mIndex)     
       }) 
      console.log(exists) 
      if (!exists) { 
       force.nodes().push(d) 
      } 
     } 
     else { 
      force.nodes().splice(i, 1) 
     } 
    }) 



    d3.select("#nodeCount").text(force.nodes().length) 
} 

Для каждой точки данных, я проверка, является ли или нет лежит точка между степенью() [0] и [1]. Если да, то проверьте force.nodes(), чтобы увидеть, есть ли в данный момент член. Если это не так, то нажмите ее в force.nodes().

Если точка данных не лежит между экстентами, тогда соедините ее с force.nodes(). Этот последний бит работает отлично.

ОБНОВЛЕНИЕ: Исправлено. Я также разработал, как фильтровать ссылки, прикрепленные к узлам. jsfiddle here - https://jsfiddle.net/hiwilson1/7oumeat5/2/. Никогда не пытайтесь делать это с индексами/жестко закодированными индексами, сравнивайте узлы/ссылки как объекты.

Кстати, я вижу ссылки на вершине узлов. Если есть способ исправить это, я был бы рад услышать это.

ДАЛЕЕ UPDATE: Для того, чтобы обеспечить ссылки за узлы используют .Применять («строку», «: первый ребенок») вместо .append («линия»)

ответ

2

Есть несколько проблем с текущим кодом , Основная проблема заключается в том, что вы даете data в force.nodes(), что означает, что две структуры данных на самом деле одинаковы. То есть, когда вы удаляете элементы из force.nodes(), вы также изменяете базовый data. Следовательно, вы не можете добавить их обратно - они исчезли.

Это легко исправляется путем передачи копии data в force.nodes():

var force = d3.layout.force() 
    .nodes(JSON.parse(JSON.stringify(data))) 

Тогда вы удаляете неправильные узлы из force.nodes() - индекс вы используете для data, не force.nodes(). Вы можете вычислить индекс data элемента в force.nodes() и использовать его как это:

data.forEach(function(d, i) { 
     var idx = -1; 
     force.nodes().forEach(function(node, j) { 
      if(node.mIndex == d.mIndex) { 
       idx = j; 
      } 
     }); 
     //if data point in range (between extent 0 and 1) 
     if (d.date >= brush.extent()[0] && d.date <= brush.extent()[1]) { 
      if (idx == -1) { 
       force.nodes().push(d) 
      } 
     } 
     else if(idx > -1) { 
      force.nodes().splice(idx, 1) 
     } 

Наконец, вам нужно вызвать force.start() в конце brushed для того, чтобы изменения стали видны после того, как макет расположился.

Полный пример here.

+0

Комментарии не для широкого обсуждения; этот разговор был [перемещен в чат] (http://chat.stackoverflow.com/rooms/77339/discussion-on-answer-by-lars-kotthoff-d3-adding-and-removing-force-nodes-based) , – Taryn

+0

Я старался не использовать их. Я использую компьютерные индексы, чтобы избавиться от ссылок, но только после поиска индексов путем сопоставления источника и ссылок с источником связанных ссылок и ссылками. Я даже повторно вставляю узлы и ссылки на их. исходные позиции через splice(), поэтому я попытался отрицать это на каждом шагу. Коды, которые были свернуты, так что трудно следовать, где я ошибаюсь. Попробует другую стратегию. – hwilson1

1

Основываясь на ответе от могущественного @Lars Kotthoff, который исправил вашу техническую проблему, я сосредоточусь на архитектуре. Вот архитектура, которая проще и более соответствует идиоме d3.

Основными принципами являются:

  1. Использование Array.prototype.filter() для управления данными
  2. Используйте стандартные, управляемые данными модели.
    Проведите визуализацию, управляя данными, а затем используйте стандартный общий шаблон обновления, чтобы преобразовать изменения в vis.
  3. Отдельные события данных и события анимации.
    Ответьте на изменения данных только тогда, когда это необходимо ... сделайте это в махающем событии, а не на каждом тике. Событием brushed является событие данных, а процедура tick - это событие анимации . Не оптимально управлять данными о событиях анимации, если это необходимо.
  4. Сделайте запись и выход узлов более гладкими.

Преимущество пункта 1 в том, что отфильтрованный массив фактически является массив ссылок на оригинальные data элементов, так что, когда дополнительное состояние добавляется на скопированном массиве, он фактически добавляется на оригинальном data массиве. Таким образом, предыдущее состояние доступно, когда оно отфильтровывается обратно, следовательно, происходит плавный выход и поведение входа. Между тем ни один элемент не удаляется в исходном data, когда кисть фильтрует вниз: в клонированный массив удаляются только ссылки на них. Я должен признать, что я этого не ожидал, но это хорошее открытие, даже если это случайно!
Конечно, это работает только потому, что элементы массива являются объектами.

рабочий пример здесь ...

var width = 700, 
 
    height = 600, 
 
    padding = 20; 
 

 
    var start = new Date(2013, 0, 1), 
 
     end = new Date(2013, 11, 31) 
 

 
    var data = [] 
 

 
    for (i = 0; i < 100; i++) { 
 
     var point = {} 
 

 
     var year = 2013; 
 
     var month = Math.floor(Math.random() * 12) 
 
     var day = Math.floor(Math.random() * 28) 
 

 
     point.date = new Date(year, month, day) 
 
     point.mIndex = i 
 
     data.push(point) 
 
    } 
 

 
    var force = d3.layout.force() 
 
      .size([width - padding, height - 100]) 
 
      .on("tick", tick) 
 
      .start() 
 

 
    var svg = d3.select("body").append("svg") 
 
     .attr({ 
 
     "width": width, 
 
     "height": height 
 
     }) 
 

 
    //build stuff 
 
    var x = d3.time.scale() 
 
     .domain([start, end]) 
 
     .range([padding, width - 6 * padding]) 
 
     .clamp(true) 
 

 
    var xAxis = d3.svg.axis() 
 
     .scale(x) 
 
     .tickSize(0) 
 
     .tickPadding(20) 
 
    //.tickFormat(d3.time.format("%x")) 
 

 
    var brush = d3.svg.brush() 
 
     .x(x) 
 
     .extent([start, end]) 
 
     .on("brush", brushed1) 
 

 
    //append stuff 
 
    var slidercontainer = svg.append("g") 
 
     .attr("transform", "translate(100, 500)") 
 

 
    var axis = slidercontainer.append("g") 
 
     .call(xAxis) 
 

 
    var slider = slidercontainer.append("g") 
 
     .call(brush) 
 
     .classed("slider", true) 
 

 
    //manipulate stuff 
 
    d3.selectAll(".resize").append("circle") 
 
     .attr("cx", 0) 
 
     .attr("cy", 0) 
 
     .attr("r", 10) 
 
     .attr("fill", "Red") 
 
     .classed("handle", true) 
 

 
    d3.select(".domain") 
 
     .select(function() { return this.parentNode.appendChild(this.cloneNode(true)) }) 
 
     .classed("halo", true) 
 

 
    function brushed1(e) { 
 

 
     var nodes = includedNodes(data, brush); 
 

 
     nodes.enter().append("circle") 
 
      .attr("r", 10) 
 
      .attr("fill", "red") 
 
      .call(force.drag) 
 
      .attr("class", "node") 
 
      .attr("cx", function (d) { return d.x }) 
 
      .attr("cy", function (d) { return d.y }) 
 

 
     nodes 
 
     .exit() 
 
     .remove() 
 

 
     force 
 
     .nodes(includedData(data, brush)) 
 
     .start() 
 

 
    } 
 

 
    function includedData(data, brush) { 
 

 
     return data.filter(function (d, i, a) { 
 
     return d.date >= brush.extent()[0] && d.date <= brush.extent()[1] 
 
     }) 
 

 
    } 
 
    function includedNodes(data, brush) { 
 
     return svg.selectAll(".node") 
 
       .data(includedData(data, brush), function (d, i) { 
 
       return d.mIndex 
 
       }) 
 
    } 
 

 
    function tick() { 
 

 
     includedNodes(data, brush) 
 
     .attr("cx", function (d) { return d.x }) 
 
     .attr("cy", function (d) { return d.y }) 
 

 
    } 
 
    brushed1()
.domain { 
 
     fill: none; 
 
     stroke: #000; 
 
     stroke-opacity: .3; 
 
     stroke-width: 10px; 
 
     stroke-linecap: round; 
 
    } 
 

 
    .halo { 
 
     fill: none; 
 
     stroke: #ddd; 
 
     stroke-width: 8px; 
 
     stroke-linecap: round; 
 
    } 
 

 
    .tick { 
 
     font-size: 10px; 
 
    } 
 

 
    .selecting circle { 
 
     fill-opacity: .2; 
 
    } 
 

 
     .selecting circle.selected { 
 
     stroke: #f00; 
 
     } 
 

 
    .handle { 
 
     fill: #fff; 
 
     stroke: #000; 
 
     stroke-opacity: .5; 
 
     stroke-width: 1.25px; 
 
     cursor: crosshair; 
 
    }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script> 
 
<p id="nodeCount"></p>

+0

@ hwilson1, учитывая проблемы с вашей первоначальной структурой, которые были спасены Ларсом, мне интересно, если бы вы взглянули на это. Просто запустите фрагмент. –

+0

Мое намерение состоит в том, чтобы застрять в вашем примере, как только я завоевал добавление/удаление узлов. Ваш пример добавляет в себя несколько элегантных расцветок, которые были бы более полезными для меня, чтобы понять на этом этапе. Я привел еще один пример, который избавляется от слайдера и заменяет его флажками, чтобы я мог четко видеть, что происходит с индексами узлов (каждый бит, присвоенный каждому индексу и т. Д.). jsfiddle здесь - https://jsfiddle.net/hiwilson1/296g1vt2/. Я очень близок к тому, что все это сработало, но проблема связана с узлами. Без причины я вижу. Любой совет? – hwilson1

+0

Я вычислил добавление/удаление узлов. Теперь борется с добавлением/удалением связанных ссылок. Я поставил jsfiddle здесь - jsfiddle.net/hiwilson1/r9135qmp/1 - Я выводил некоторые данные о том, что происходит с четырьмя узлами или ссылками. Первый и третий столбцы - force.nodes()/force.links(), 2-й и 4-й столбцы - это nodedata и linkdata. Использование слайдера должно включать и выключать заметки, но оно регулярно ломается, я думаю, потому что индексы не совпадают правильно. Любая помощь оценивается. – hwilson1

Смежные вопросы