D3.js文本节点旋转后未定位至目标位置问题求助
问题:旋转文本节点后远离目标位置
尝试为节点添加旋转文本,预期文本位于节点侧边,但实际文本远离节点。
问题原因
当前代码中,文本先平移到节点坐标,再进行旋转,但未针对旋转后的坐标系调整文本的偏移位置。SVG的变换是按顺序执行的:先将文本移动到节点位置,旋转后文本的锚点位置没有向节点方向偏移,导致文本远离节点。
解决方案
在旋转后添加一个基于旋转坐标系的平移,让文本向节点方向偏移。同时可调整text-anchor和dy属性,优化文本对齐效果。
修改文本部分的核心代码:
var txt = g.selectAll('.txt') .data(nodes) .join('text') .text(d => d.name) .attr('class','txt') .attr('stroke','black') .attr('font-size',40) .attr('text-anchor','middle') // 改为居中锚点,更易调整位置 .attr('dy', '.3em') // 统一垂直对齐基准 .attr('transform', (d,i) => { let alpha = 30; if (d.y == 0) { alpha = -30; } // 先平移到节点位置→旋转→向旋转后的X轴正方向偏移(节点半径+间距) return `translate(${xScale(d.x)},${yScale(d.y)})rotate(${alpha})translate(${r + 10}, 0)`; })
如果希望保留原有的text-anchor: end设置,可改为向旋转后的X轴负方向偏移:
var txt = g.selectAll('.txt') .data(nodes) .join('text') .text(d => d.name) .attr('class','txt') .attr('stroke','black') .attr('font-size',40) .attr('text-anchor','end') .attr('dy', (d,i) => { if (d.y == 0) return '.3em' else return '-.3em' }) .attr('transform', (d,i) => { let alpha = 30; if (d.y == 0) { alpha = -30; } return `translate(${xScale(d.x)},${yScale(d.y)})rotate(${alpha})translate(-${r + 10}, 0)`; })
完整代码
JavaScript代码
demo() function demo() { var r = 5 var color = 'steelblue' var width = 800 var height = 600 var margin = {top: 20, left: 50,right: 10, bottom: 50} var gwidth = width + margin.left + margin.right var gheight = height + margin.top + margin.bottom; const data = ` AAAA,BBB,CCC,DDDD,EEEEE,X,y,z,u,v,w BBB,FFFF,GGGGG,HHHHH DDDD,IIII,JJJJ,KKKK,LLL ` var nodes = [] var links = [] var dx = 20 var dy = 20 var x = 0 var y = 0 var alpha = Math.PI/180*70 var parent = null data.trim().split("\n").forEach((line,i) => { line.split(",").forEach((elem,j)=> { if (elem == "") return var m = nodes.find(d => d.name == elem) if (m == undefined) { var node = {name:elem,x:x,y:y,parent:parent} nodes.push(node) parent = node x += dx }else { x = m.x + y*Math.cos(alpha)/Math.sin(alpha) parent = m } }) y += dy }) var xmin = d3.min(nodes,d => d.x) var xmax = d3.max(nodes,d => d.x) var ymin = d3.min(nodes,d => d.y) var ymax = d3.max(nodes,d => d.y) var xScale = d3.scaleLinear() .range([0,width]) .domain([xmin-10,xmax+10]) var yScale = d3.scaleLinear() .range([height,0]) .domain([ymin-10,ymax+10]) var svg = d3.select("body") .append("svg") .attr("width", gwidth) .attr("height", gheight) .style('border','1px solid red') var g = svg.append("g") .attr('transform',`translate(${margin.left},${margin.top})`); var xaxis = g.append("g") .attr("transform",`translate(0,${height})`) .call(d3.axisBottom(xScale) .tickFormat((d,i) => null)//i==0 ? null:d) .tickSize(0) ); var yaxis = g.append("g") .attr("transform", `translate(0,0)`) .call(d3.axisLeft(yScale) .tickFormat((d,i) => null)//i==0 ? null:d) .tickSize(0) ); [xaxis,yaxis].forEach((d) => d.call(g => g.select(".domain").remove())) var node = g.selectAll('.point') .data(nodes) .join('circle') .attr('class','point') .attr('cx',d => xScale(d.x)) .attr('cy',d => { return yScale(d.y) }) .attr('r',r) .attr('stroke',color) .attr('fill',color) var txt = g.selectAll('.txt') .data(nodes) .join('text') .text(d => d.name) .attr('class','txt') .attr('stroke','black') .attr('font-size',40) .attr('text-anchor','middle') .attr('dy', '.3em') .attr('transform', (d,i) => { let alpha = 30; if (d.y == 0) { alpha = -30; } return `translate(${xScale(d.x)},${yScale(d.y)})rotate(${alpha})translate(${r + 10}, 0)`; }) function drawlink(d) { if (d.parent == null) return "" var ax = xScale(d.parent.x) var ay = yScale(d.parent.y) var bx = xScale(d.x) var by = yScale(d.y) var path = ['M',ax,ay,'L',bx,by] return path.join(' ') } var links = g.selectAll('.link') .data(nodes) .join('path') .attr('class','link') .attr('d',drawlink) .attr('stroke',color) .attr('stroke-width',r) return }
HTML代码
<script src="https://unpkg.com/d3@7.0.4/dist/d3.min.js"></script>
内容的提问来源于stack exchange,提问作者lucky1928
相关产品推荐
相关产品推荐

