web-dev-qa-db-fra.com

Fonction D3 .merge

J'ai du mal à comprendre la fonction de fusion dans D3, malgré la lecture de l'API D3 un nombre incalculable de fois.

L'API dit: "Cette méthode est couramment utilisée pour fusionner les sélections d'entrée et de mise à jour après une jointure de données. Après avoir modifié les éléments d'entrée et de mise à jour séparément, vous pouvez fusionner les deux sélections et effectuer des opérations sur les deux sans code en double."

Voici un exemple de son utilisation supposée simple, dans un graphique dirigé par la force, où la fonction cochée est appelée à chaque tic:

var simulation = d3.forceSimulation(nodes)
    .force("charge", chargeForce)
    .force("center", centerForce)
    .on("tick", ticked);

    function ticked() {

    var u = d3.select("svg").selectAll("circle").data(nodes)

    u.enter().append("circle").attr("r",5)
        .merge(u) // What is the merge function doing here?
        .attr("cx", d => d.x)
        .attr("cy", d => d.y)

    u.exit().remove() // Why is it necessary to remove excess objects w/ the exit selection?

    }

Je comprends le fonctionnement de la liaison de données et le fonctionnement des sélections enter () et exit (). Cependant, je n'ai jamais eu à utiliser de "fusion" auparavant, et je ne comprends pas que cela se fasse ici. Si quelqu'un pouvait expliquer brièvement ce qui se passe dans cette fonction étape par étape, ce serait extrêmement utile. Je suis sûr que d'autres ont des questions similaires.

12
Harry Cramer

La documentation explique très bien ce que fait cette fonction, donc ce qu'elle fait est au lieu que vous ayez à le faire

u.attr("cx", d => d.x)
 .attr("cy", d => d.y);

u.enter().append("circle").attr("r",5)
        .attr("cx", d => d.x)
        .attr("cy", d => d.y);

Vous pouvez simplement appeler attr une fois comme

u.enter().append("circle").attr("r",5)
        .merge(u) // after this point, any updates will apply to both u and u.enter() selections
        .attr("cx", d => d.x)
        .attr("cy", d => d.y)

Il définira les attributs cx et cy sur les deux u- la sélection de mise à jour et u.enter()- la sélection d'entrée

Pourquoi est-il nécessaire de supprimer les objets en excès avec la sélection de sortie?

Étant donné que la sélection de sortie contient des éléments DOM supplémentaires qui n'étaient pas liés aux éléments du tableau que vous avez transmis à data(), vous pouvez faire tout ce dont vous avez besoin sur la collection de sortie, par exemple, définir les styles en appelant u.exit().style(...), etc. au lieu d'appeler remove pour les supprimer du DOM

8
Teedeez

Vous avez en fait deux problèmes ici:

  1. Comprendre la méthode merge();
  2. Comprendre ce morceau de code que vous avez partagé;

Concernant # 1, vous avez déjà reçu une réponse. Concernant # 2, ce sont mes deux cents: ce code n'a pas un sens.

C'est simple à comprendre: la fonction ticked s'exécute des dizaines de fois par seconde. Pourquoi voudriez-vous relier les données et réaffecter la mise à jour, entrer et quitter des sélections des dizaines de fois par seconde, si les données ne changent pas ? (il convient de mentionner que l'auteur de ce code est un bon programmeur, quelque chose d'étrange s'est produit ici ... après tout, nous faisons tous des erreurs)

La fonction ticked a juste besoin de calculer les positions des éléments, c'est tout.

Voici le même code que vous avez lié avec la fonction ticked simplifiée pour cela:

function ticked() {
    u.attr('cx', function(d) {
            return d.x;
        })
        .attr('cy', function(d) {
            return d.y;
        })
}

Et voici le code en cours d'exécution:

var width = 600,
  height = 400;

var colorScale = ['orange', 'lightblue', '#B19CD9'];
var xCenter = [100, 300, 500]

var numNodes = 100;
var nodes = d3.range(numNodes).map(function(d, i) {
  return {
    radius: Math.random() * 25,
    category: i % 3
  }
});

var u = d3.select('svg g')
  .selectAll('circle')
  .data(nodes);

var enter = u.enter()
  .append('circle')
  .attr('r', function(d) {
    return d.radius;
  })
  .style('fill', function(d) {
    return colorScale[d.category];
  });

u = enter.merge(u);

u.exit().remove();

var simulation = d3.forceSimulation(nodes)
  .force('charge', d3.forceManyBody().strength(5))
  .force('x', d3.forceX().x(function(d) {
    return xCenter[d.category];
  }))
  .force('collision', d3.forceCollide().radius(function(d) {
    return d.radius;
  }))
  .on('tick', ticked);

function ticked() {
  u.attr('cx', function(d) {
      return d.x;
    })
    .attr('cy', function(d) {
      return d.y;
    })
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="content">
  <svg width="700" height="400">
    <g transform="translate(50, 200)"></g>
  </svg>
</div>
4
Gerardo Furtado

TL; DR - la fusion fait de deux collections de nœuds une seule

var x = d3.selectAll(".node");
var y = d3.selectAll(".link");
var z = x.merge(y);

z contient maintenant tous les éléments de x ET tous les éléments de y.

1
user2728841