Изменить порядок отображения данных в d3 - PullRequest
0 голосов
/ 08 февраля 2019

У меня есть некоторые данные (Node s), которые мне нужно нарисовать.Эти узлы могут перекрываться, и поэтому важен порядок , в котором они нарисованы (те, которые должны отображаться сверху, должны быть нарисованы в последнюю очередь).

Позиция и, следовательно, z-ось этих узлов может быть изменена, поэтому я попытался смоделировать это поведение с помощью ключа , который включает текущий индекс списка, в котором хранятся узлы.

case class Node(id: Int)

def addNodesToSVG = {
   val sortedData: List[Node] = ??? 

   val nodesSelection = d3.select("#nodes").selectAll(".node")
      .data(sortedData.toJSArray, (n: Node) => {
         n.id.toString +
         // the position of those nodes may change over time
         // that's why we need to include the position in the identifier
         sortedData.indexOf(n)
   }

   nodesSelection.enter().append("g").attr("class", "node") // ...

   nodesSelection
       .attr("transform", transform) // ...

   nodesSelection.exit().remove()
}

К сожалению, это не работает должным образом.

Теоретически, именно так я и думал, что это будет работать, если у меня всего два узла (n1 и * 1016).*), которые сохраняются в List(n1, n2)

node   key
-----  ---
n1      10 // node 1 at position 0
n2      21 // node 2 at position 1

Теперь, если я изменю Список на List(n2, n1) и снова вызову addNodesToSVG, я думаю, это произойдет:

node   key
-----  ---
n2      20 // node 1 at position 0
n1      12 // node 2 at position 1

Так как они неизвестны, я думал, что это удалит (nodesSelection.exit().remove()) старые узлы и нарисует «новые» в правильном порядке.Это - однако - не происходит.Почему?

Редактировать после дополнительной отладки я обнаружил, что мой exit Выбор всегда имеет размер 0.

1 Ответ

0 голосов
/ 08 февраля 2019

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

<!DOCTYPE html>
<head>
  <meta charset="utf-8">
  <script src="https://d3js.org/d3.v4.min.js"></script>
  <style>
    body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
  </style>
</head>

<body>
  <button onclick="sw()">Switch</button>
  <script>
    var d1 = [{
      id: 'a',
      z: 1,
      fill: 'red', 
      y: 0
    }, {
      id: 'b',
      z: 2,
      fill: 'green', 
      y: 5
    }];
    
    var d2 = [{
      id: 'a',
      z: 2, 
      fill: 'red',
      y: 5
    }, {
      id: 'b',
      z: 1, 
      fill: 'green',
      y: 0
    }]
    
    var current = 0;
    
    var svg = d3.select("body").append("svg")
      .attr("width", 100)
      .attr("height", 100)
    	.attr('viewBox', '0 0 10 20');

    function render(d) {
      var r = svg.selectAll('rect')
      	.data(d, function(d) { return d.id; });
      r.enter()
      	.append('rect')
      	.attr('width', 10)
      	.attr('height', 10)
      	.attr('fill', function(d) { return d.fill; })
      .merge(r)
      	.sort(function(r1, r2) {
        	if (r1.z > r2.z) return 1;
        	if (r1.z < r2.z) return -1;
        	return 0;
      	})
      	.transition()
      	.attr('y', function(d) { return d.y; });
      
      r.exit().remove();
    };
    
    function sw() {
        if (current == 0) {
          current = 1;
          render(d2);
        } else {
          current = 0;
          render(d1);
        }
    }
    
    render(d1);

  </script>
</body>
...