d3.js - 将背景矩形添加到元素组

d3.js - add background rect to elements group

下面的代码绘制了 4 个组元素,我想根据组的 bbox 大小在每个组上绘制背景矩形。我尝试了下面的代码但没有用!

var margin = {top:40,left:10,bottom:10,right:10}
var svg = d3.select('body').append('svg')
.attr('width',600)
.attr('height',600)
.attr('viewBox',[0,0,300,300])
.attr('transform',`translate(${margin.left},${margin.top})`)

add_defs(svg)

var color = d3.scaleOrdinal(d3.schemeCategory10);

var data = [
  {name:'data1',
   x:20,
   y:40,
   contents:[
     {name:'Group 1'},
     {name:'AAAA'},
     {name:'BBBB'},
     {name:'CCCC'},
     {name:'DDDD'},
     {name:'EEEE'},
     {name:'FFFF'}
   ]
  },
  {name:'data2',
   x:110,
   y:10,
   contents:[
     {name:'Group2'},
     {name:'GGGG'},
     {name:'HHHH'}
   ]
  },
  {name:'data3',
   x:110,
   y:50,
   contents:[
     {name:'Group3'},
     {name:'IIII'},
     {name:'JJJJ'}
   ]
  },
  {name:'data4',
   x:110,
   y:90,
   contents:[
     {name:'Group4'},
     {name:'KKKK'},
     {name:'LLLL'},
     {name:'MMMM'},
     {name:'NNNN'},
   ]
  },   
]

add_box(data)

function add_box(data) {
  var groups = svg.selectAll('.groups')
  .data(data).enter()
  .append('g')
  
  var g1 = groups.selectAll('nodes')
  .data(d => d.contents)
  .enter().append('g')  
  var w = 70
  var fontsize = 5
  var text = g1.append('text')
  .attr('class','modname')
  .attr('text-anchor','start')
  .attr('font-size',fontsize)
  .attr("dominant-baseline", "central")
  .attr('x',(d,i,n) => {
    var p = d3.select(n[i].parentNode.parentNode).datum()
    return p.x
  })
  .attr('y',(d,i,n) => {
    var p = d3.select(n[i].parentNode.parentNode).datum()
    return i*10 + p.y
  })
  .text(d => d.name)
  .each( (d,i,n) => {
    if (n[i] instanceof SVGGraphicsElement) { 
      var bbox = n[i].getBBox();
      bbox.x -= 5
      bbox.width += 12
      bbox.height += 2
      if (bbox.width < w) bbox.width = w
      d.bbox = bbox
      d.xs = d.bbox.x
      d.ys = d.bbox.y + d.bbox.height/2
      d.xe = d.xs + d.bbox.width;
      d.ye = d.ys
    }
  })

  var rects = g1.insert('rect','text')
  .attr('id','modrect')
  .attr('x',(d,i) => {
    return d.bbox.x
  })
  .attr('y',(d,i) => {
    return d.bbox.y
  })
  .attr('width',(d,i) => {
    return d.bbox.width
  })
  .attr('height',(d,i) => {
    return d.bbox.height
  })
  .attr('fill',(d,i) => {
    if (i == 0) return 'none'
    return color(i)})
  .attr('fill-opacity',.3)

  var e = 2
  var g1bbox = g1.node().getBBox()
  g1bbox.x -= e
  g1bbox.y -= e
  g1bbox.width += 2*e
  g1bbox.height += 2*e

  g1.insert('rect','rect')
    .attr('x',g1bbox.x)
    .attr('y',g1bbox.y)
    .attr('width',g1bbox.width)
    .attr('height',g1bbox.height)
    .attr('fill',color(0))
    .attr('fill-opacity',.3)
    .attr('stroke','grey')
  
  return
}

function add_defs(svg) {
  var w = 3
  var h = 2
  var m = 0
  var lc = 'black'
  var path = ['M',2+m,2+h/2,'L',2,2,2+w,2+h/2,2,2+h,'z']   
  svg
    .append('defs')
    .append('marker')
    .attr('id', 'arr1')
    .attr('viewBox', [0, 0, w+4, h+4])
    .attr('refX', w+1)
    .attr('refY', 2+h/2)
    .attr('markerWidth', w+4)
    .attr('markerHeight', h+4)
    .attr('orient', 'auto-start-reverse')
    .append('path')
    .attr('d',path.join(' '))
    .attr('stroke', lc)
    .attr('fill',lc) 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>

将矩形添加到组中!

  groups.each(function(d) {
    var bbox = d3.select(this).node().getBBox()
    var m = 4
    bbox.x -= m
    bbox.y -= m
    bbox.width += 2*m
    bbox.height += 2*m
    d.bbox = bbox
  })
  .append('rect').lower()
  .attr('stroke','grey')
  .attr('fill',color(0))
  .attr('fill-opacity',0.3)
  .attr('x',d => d.bbox.x)
  .attr('y',d => d.bbox.y)
  .attr('width',d => d.bbox.width)
  .attr('height',d => d.bbox.height)

console.clear()

var margin = {top:40,left:10,bottom:10,right:10}
var svg = d3.select('body').append('svg')
.attr('width',600)
.attr('height',600)
.attr('viewBox',[0,0,300,300])
.attr('transform',`translate(${margin.left},${margin.top})`)

add_defs(svg)

var color = d3.scaleOrdinal(d3.schemeCategory10);

var data = [
  {name:'data1',
   x:20,
   y:40,
   contents:[
     {name:'Group 1',group:1},
     {name:'AAAA'},
     {name:'BBBB'},
     {name:'CCCC'},
     {name:'DDDD'},
     {name:'EEEE'},
     {name:'FFFF'}
   ]
  },
  {name:'data2',
   x:110,
   y:10,
   contents:[
     {name:'Group2'},
     {name:'GGGG'},
     {name:'HHHH'}
   ]
  },
  {name:'data3',
   x:110,
   y:50,
   contents:[
     {name:'Group3'},
     {name:'IIII'},
     {name:'JJJJ'}
   ]
  },
  {name:'data4',
   x:110,
   y:90,
   contents:[
     {name:'Group4'},
     {name:'KKKK'},
     {name:'LLLL'},
     {name:'MMMM'},
     {name:'NNNN'},
   ]
  },   
]

add_box(data)

function add_box(data) {
  var groups = svg.selectAll('.groups')
  .data(data).enter()
  .append('g')
  
  var g1 = groups.selectAll('nodes')
  .data(d => d.contents)
  .enter().append('g')

  var w = 70
  var fontsize = 5
  var text = g1.append('text')
  .attr('class','modname')
  .attr('text-anchor','start')
  .attr('font-size',fontsize)
  .attr("dominant-baseline", "central")
  .attr('x',(d,i,n) => {
    var p = d3.select(n[i].parentNode.parentNode).datum()
    return p.x
  })
  .attr('y',(d,i,n) => {
    var p = d3.select(n[i].parentNode.parentNode).datum()
    return i*10 + p.y
  })
  .text(d => d.name)
  .each( (d,i,n) => {
    if (n[i] instanceof SVGGraphicsElement) { 
      var bbox = n[i].getBBox();
      bbox.x -= 5
      bbox.width += 12
      bbox.height += 2
      if (bbox.width < w) bbox.width = w
      d.bbox = bbox
      d.xs = d.bbox.x
      d.ys = d.bbox.y + d.bbox.height/2
      d.xe = d.xs + d.bbox.width;
      d.ye = d.ys
    }
  })

  var rects = g1.insert('rect','text')
  .attr('id','modrect')
  .attr('x',(d,i) => {
    return d.bbox.x
  })
  .attr('y',(d,i) => {
    return d.bbox.y
  })
  .attr('width',(d,i) => {
    return d.bbox.width
  })
  .attr('height',(d,i) => {
    return d.bbox.height
  })
  .attr('fill',(d,i) => {
    if (i == 0) return 'none'
    return color(i)})
  .attr('fill-opacity',.3)

  groups.each(function(d) {
    var bbox = d3.select(this).node().getBBox()
    var m = 4
    bbox.x -= m
    bbox.y -= m
    bbox.width += 2*m
    bbox.height += 2*m
    d.bbox = bbox
  })
  .append('rect').lower()
  .attr('stroke','grey')
  .attr('fill',color(0))
  .attr('fill-opacity',0.3)
  .attr('x',d => d.bbox.x)
  .attr('y',d => d.bbox.y)
  .attr('width',d => d.bbox.width)
  .attr('height',d => d.bbox.height)

  return
}

function add_defs(svg) {
  var w = 3
  var h = 2
  var m = 0
  var lc = 'black'
  var path = ['M',2+m,2+h/2,'L',2,2,2+w,2+h/2,2,2+h,'z']   
  svg
    .append('defs')
    .append('marker')
    .attr('id', 'arr1')
    .attr('viewBox', [0, 0, w+4, h+4])
    .attr('refX', w+1)
    .attr('refY', 2+h/2)
    .attr('markerWidth', w+4)
    .attr('markerHeight', h+4)
    .attr('orient', 'auto-start-reverse')
    .append('path')
    .attr('d',path.join(' '))
    .attr('stroke', lc)
    .attr('fill',lc) 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>