I try to rotate text and background rect to annotate two point. but after rotation, the background position not match the text bbox.
console.clear()
var svg = d3.select('body')
.append('svg')
add_defs(svg)
var g = svg.append('g')
var data = [[0,0],[100,100]]
var nodes = g.selectAll('.circle')
.data(data)
.join('circle')
.attr('cx',d => d[0])
.attr('cy',d => d[1])
.attr('r',2)
.attr('stroke','none')
.attr('fill','red')
ann_line(svg,data[0][0],data[0][1],data[1][0],data[1][1],"hello")
adjust_view(svg)
function ann_line(svg,ax,ay,bx,by,text) {
var w = 10
var dx = bx - ax
var dy = by - ay
var l = Math.sqrt(dx*dx+dy*dy)
dx /= l
dy /= l
var a1x = ax + w*dy
var a1y = ay - w*dx
var a2x = ax + 2*w*dy
var a2y = ay - 2*w*dx
var b1x = bx + w*dy
var b1y = by - w*dx
var b2x = bx + 2*w*dy
var b2y = by - 2*w*dx
var path = ['M',a1x,a1y,'L',b1x,b1y]
var line = svg.append('path')
.attr('d',path.join(' '))
.attr('stroke','black')
.attr('marker-start', 'url(#arrhead)')
.attr('marker-end', 'url(#arrhead)');
var cx = (a1x + b1x)/2
var cy = (a1y + b1y)/2
var alpha = Math.atan(dx/dy)/Math.PI*180
const label = svg.append('text')
.text(text)
.attr('text-anchor', 'middle')
.attr('alignment-baseline', 'middle')
.attr('transform',`translate(${cx},${cy}) rotate(${alpha})`)
const bb = label.node().getBBox();
svg.append('rect').lower()
.attr('stroke','none')
.style('fill', 'steelblue')
.attr('transform',`translate(${cx-bb.width/2},${cy-bb.height/2}) rotate(${alpha})`)
.attr('width', bb.width)
.attr('height', bb.height)
}
function renderTextInCenterOfLine(line, text) {
const from = parseInt(line.attr('x1'));
const to = parseInt(line.attr('x2'));
const y = parseInt(line.attr('y1'));
const svg = d3.select('svg');
const textBackground = svg.append('rect')
const textElement = svg.append('text')
.text(text)
.attr('x', (from + to) / 2)
.attr('y', y)
.attr('text-anchor', 'middle')
.attr('alignment-baseline', 'central');
const box = textElement.node().getBBox();
const width = box.width + 50; //
const height = box.height + 20;
textBackground
.attr('x', (from + to - width) / 2)
.attr('y', y - height / 2)
.attr('width', width)
.attr('height', height)
.style('fill', 'white')
}
function add_defs(svg) {
var lw = 1
var w = 6
var h = 12
var m = 2
var lc = 'black'
var path = ['M',2+w,2,'L',2+w,2+h,'M',2+m,2+h/2,'L',2,2+h/4,2+w-lw,2+h/2,2,2+3*h/4,'z']
svg
.append('defs')
.append('marker')
.attr('id', 'arrhead')
.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('stroke-width',lw)
.attr('fill',lc)
.attr('stroke-miterlimit', 10)
}
function adjust_view(svg) {
var bbox = svg.node().getBBox()//getBoundingClientRect()
svg.attr('width',600)
.attr('height',400)
.attr('viewBox',[bbox.x,bbox.y,bbox.width,bbox.height])
.style('border','2px solid red')
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.0.0/d3.min.js"></script>