I would like move the g element include multiple tspan text to enter.
demo()
function demo() {
//subtitle("iptable filter")
// 2. 描画用のデータ準備
var width = 800
var height = 400
var data = {name:"AAAnBBB",children: [
{name: "CCCnDDD",children:[
{name:"EEEnFFF"}
]},
{name: "GGGnHHH",children:[
{name:"IIInJJJ"}
]},
{name: "KKKnLLL",children: [
{name: "MMMnNNN"}
]},
{name: "OOOnPPP",children:[
{name: "QQQnRRR"}
]}
]}
var root = d3.hierarchy(data);
var tree = d3.tree()
.size([height, width])
tree(root);
var margin = {left:80,top:20,right:20,bottom:20}
var svg = d3.select('body').append("svg")
.attr('width',width + margin.left + margin.right)
.attr('height',height + margin.top + margin.bottom)
var g = svg.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`)
var link = g.selectAll(".link")
.data(root.descendants().slice(1))
.enter()
.append("path")
.attr("class", "link")
.attr("d", function(d) {
return "M" + d.y + "," + d.x +
"C" + (d.parent.y + 100) + "," + d.x +
" " + (d.parent.y + 100) + "," + d.parent.x +
" " + d.parent.y + "," + d.parent.x;
});
var node = g.selectAll(".node")
.data(root.descendants())
.enter()
.append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })
var txtnode = node.append("text")
.attr("text-anchor", 'start')
.attr("dominant-baseline","text-before-edge")//"middle"
.attr("font-size", "200%")
.selectAll('tspan')
.data(d => d.data.name.split('n'))
.join('tspan')
.attr('class','tspan')
.attr('x',0)
.attr('y',(d,i) => i*25)
.text(d => d)
node.each((d,i,n) =>{
var bbox = d3.select(n[i]).node().getBBox()
var margin = 4
bbox.x -= margin
bbox.y -= margin
bbox.width += 2*margin
bbox.height += 2*margin
d.bbox = bbox
})
node.insert("rect",'text')
.attr('fill','#FEFECE')
.attr('fill','none')
.attr('rx',5.5)
.attr('ry',5.5)
.attr('stroke','#A80036')
.attr('stroke-width',2)
.attr('x',d => d.bbox.x)
.attr('y',d => d.bbox.y)
.attr('width',d => d.bbox.width)
.attr('height',d => d.bbox.height)
node.attr('dx',(d,i,n) => {
var x = d.bbox.width/2
return -x
})
.attr('dy',(d,i,n) => {
var x = d.bbox.width/2
var y = d.bbox.height/2
return -y
})
g.selectAll('.link')
.attr('fill','none')
.attr('stroke','#555')
.attr('stroke-opacity',1)
.attr('stroke-width',4)
}<script src="https://d3js.org/d3.v6.min.js"></script>
The attribute dx and dy doesn’t work in this example. what’s the proper way to move the g element to make it move to center?
Advertisement
Answer
For repositioning them dynamically, an easy approach is getting the size of the element and translating it up/left by half its height/width:
node.each(function(d) {
const thisSize = this.getBoundingClientRect();
d3.select(this).attr("transform", `translate(${d.y - thisSize.width/2},${d.x - thisSize.height/2})`)
});
Here is your code with that change:
demo()
function demo() {
//subtitle("iptable filter")
// 2. 描画用のデータ準備
var width = 800
var height = 400
var data = {
name: "AAAnBBB",
children: [{
name: "CCCnDDD",
children: [{
name: "EEEnFFF"
}]
},
{
name: "GGGnHHH",
children: [{
name: "IIInJJJ"
}]
},
{
name: "KKKnLLL",
children: [{
name: "MMMnNNN"
}]
},
{
name: "OOOnPPP",
children: [{
name: "QQQnRRR"
}]
}
]
}
var root = d3.hierarchy(data);
var tree = d3.tree()
.size([height, width])
tree(root);
var margin = {
left: 80,
top: 20,
right: 20,
bottom: 20
}
var svg = d3.select('body').append("svg")
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
var g = svg.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`)
var link = g.selectAll(".link")
.data(root.descendants().slice(1))
.enter()
.append("path")
.attr("class", "link")
.attr("d", function(d) {
return "M" + d.y + "," + d.x +
"C" + (d.parent.y + 100) + "," + d.x +
" " + (d.parent.y + 100) + "," + d.parent.x +
" " + d.parent.y + "," + d.parent.x;
});
var node = g.selectAll(".node")
.data(root.descendants())
.enter()
.append("g")
.attr("class", "node");
var txtnode = node.append("text")
.attr("text-anchor", 'start')
.attr("dominant-baseline", "text-before-edge") //"middle"
.attr("font-size", "200%")
.selectAll('tspan')
.data(d => d.data.name.split('n'))
.join('tspan')
.attr('class', 'tspan')
.attr('x', 0)
.attr('y', (d, i) => i * 25)
.text(d => d)
node.each((d, i, n) => {
var bbox = d3.select(n[i]).node().getBBox()
var margin = 4
bbox.x -= margin
bbox.y -= margin
bbox.width += 2 * margin
bbox.height += 2 * margin
d.bbox = bbox
})
node.insert("rect", 'text')
.attr('fill', '#FEFECE')
.attr('fill', 'none')
.attr('rx', 5.5)
.attr('ry', 5.5)
.attr('stroke', '#A80036')
.attr('stroke-width', 2)
.attr('x', d => d.bbox.x)
.attr('y', d => d.bbox.y)
.attr('width', d => d.bbox.width)
.attr('height', d => d.bbox.height)
node.attr('dx', (d, i, n) => {
var x = d.bbox.width / 2
return -x
})
.attr('dy', (d, i, n) => {
var x = d.bbox.width / 2
var y = d.bbox.height / 2
return -y
})
g.selectAll('.link')
.attr('fill', 'none')
.attr('stroke', '#555')
.attr('stroke-opacity', 1)
.attr('stroke-width', 4)
node.each(function(d) {
const thisSize = this.getBoundingClientRect();
d3.select(this).attr("transform", `translate(${d.y - thisSize.width/2},${d.x - thisSize.height/2})`)
});
}<script src="https://d3js.org/d3.v6.min.js"></script>