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.
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
Vous avez en fait deux problèmes ici:
merge()
;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>
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.