Gli archi disegnati sono tali che la loro tangente nel mezzo è esattamente la direzione della linea di base del testo, ed è anche colineare con il vettore che separa i due nodi dell'albero.
Possiamo usarlo per risolvere il problema.
È necessario un po 'di matematica. In primo luogo, definiamo una funzione che restituisce l'angolo di un vettore v
rispetto all'asse orizzontale:
function xAngle(v) {
return Math.atan(v.y/v.x) + (v.x < 0 ? Math.PI : 0);
}
Quindi, ad ogni tick, facciamo ruotare il testo in posizione da meno l'angolo di linea di base.In primo luogo, alcune funzioni di utilità:
function isFiniteNumber(x) {
return typeof x === 'number' && (Math.abs(x) < Infinity);
}
function isVector(v) {
return isFiniteNumber(v.x) && isFiniteNumber(v.y);
}
e poi, nella funzione tick
, aggiungere
linkText.attr('transform', function (d) {
// Checks just in case, especially useful at the start of the sim
if (!(isVector(d.source) && isVector(d.target))) {
return '';
}
// Get the geometric center of the text element
var box = this.getBBox();
var center = {
x: box.x + box.width/2,
y: box.y + box.height/2
};
// Get the tangent vector
var delta = {
x: d.target.x - d.source.x,
y: d.target.y - d.source.y
};
// Rotate about the center
return 'rotate('
+ (-180/Math.PI*xAngle(delta))
+ ' ' + center.x
+ ' ' + center.y
+ ')';
});
});
edit: pic aggiunto:
modificare 2 Con linee rette anziché archi curvi (semplicemente <text>
invece di <textPath>
all'interno di <text>
), è possibile sostituire la parte della funzione tick
che riguarda linkText
con questo:
linkText.attr('transform', function(d) {
if (!(isVector(d.source) && isVector(d.target))) {
return '';
}
// Get the geometric center of this element
var box = this.getBBox();
var center = {
x: box.x + box.width/2,
y: box.y + box.height/2
};
// Get the direction of the link along the X axis
var dx = d.target.x - d.source.x;
// Flip the text if the link goes towards the left
return dx < 0
? ('rotate(180 '
+ center.x
+ ' ' + center.y
+ ')')
: '';
});
e questo è quello che si ottiene:
Notate come il il testo viene capovolto mentre il collegamento passa dal puntare più a destra a puntare più a sinistra.
Il problema con questo è che il testo finisce sotto il collegamento. Che può essere fissato come segue:
linkText.attr('transform', function(d) {
if (!(isVector(d.source) && isVector(d.target))) {
return '';
}
// Get the geometric center of this element
var box = this.getBBox();
var center = {
x: box.x + box.width/2,
y: box.y + box.height/2
};
// Get the vector of the link
var delta = {
x: d.target.x - d.source.x,
y: d.target.y - d.source.y
};
// Get a unitary vector orthogonal to delta
var norm = Math.sqrt(delta.x * delta.x + delta.y * delta.y);
var orth = {
x: delta.y/norm,
y: -delta.x/norm
};
// Replace this with your ACTUAL font size
var fontSize = 14;
// Flip the text and translate it beyond the link line
// if the link goes towards the left
return delta.x < 0
? ('rotate(180 '
+ center.x
+ ' ' + center.y
+ ') translate('
+ (orth.x * fontSize) + ' '
+ (orth.y * fontSize) + ')')
: '';
});
e ora il risultato è simile al seguente:
Come si può vedere, il testo sta piacevolmente sulla parte superiore della linea, anche quando il collegamento punta verso sinistra.
Infine, per risolvere il problema mantenendo gli archi e avendo il testo rivolto verso l'alto curvo lungo l'arco, ritengo che sia necessario creare due elementi <textPath>
. Uno per andare da source
a target
e uno per andare nella direzione opposta. Dovresti usare il primo quando il link va verso destra (delta.x >= 0
) e il secondo quando il link va verso sinistra (delta.x < 0
) e penso che il risultato sarebbe più bello e il codice non sarebbe necessariamente più complicato dell'originale , solo con un po 'più di logica aggiunta.
Vedere https://stackoverflow.com/questions/8663844/add-text-label-onto-links-in-d3-force-directed-graph –