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>