d3.js使用join方法更新图表时旧内容未清理导致元素重叠问题
问题根因
- 数据绑定的退出逻辑缺失:D3的
join方法默认仅处理新增和更新的元素,当新数据集长度小于旧数据集时,多余的旧图形元素(path、circle)不会被自动删除,会残留在画布上 - 坐标轴未清空重绘:每次调用坐标轴渲染方法时,只会在现有容器内追加新的刻度、轴线,旧的坐标轴内容不会被自动清除,导致多层刻度重叠
修复代码
只需修改refresh_graph函数内的对应逻辑即可,完整修改后的代码如下:
console.clear() click_to_update() function click_to_update() { var svg = d3.select('body').append('svg') .attr('width',600) .attr('height',400) .style('border','5px solid red') var frame = svg.append('g').attr('class','frame') frame.append('g').attr('class','xaxis') frame.append('g').attr('class','yaxis') d3.select('svg').on('click', function(){ var data = fetch_data() refresh_graph(data) }) var data = fetch_data() refresh_graph(data) function refresh_graph(data){ var svg = d3.select('svg') var colors = d3.scaleOrdinal(d3.schemeSet3) var margin = {left: 40,top: 10, right:10,bottom: 60}, width = +svg.attr('width') - margin.left - margin.right, height = +svg.attr('height') - margin.top - margin.bottom; var g = d3.select('.frame') .attr('transform',`translate(${margin.left},${margin.top})`) var xrange = data.map(function(d,i) { return i; }) var x = d3.scalePoint() .domain(xrange) .range([0, width]); var ymax = d3.max(data,d => d.value) var y = d3.scaleLinear() .domain([0,ymax]) .range([height, 0]); var drawpath = function(d,i) { var ax = x(i) var ay = y(d.value) var bx = x(i) var by = y(0) var path = ['M',ax,ay,'L',bx,by] return path.join(' ') } // 修复1:给join显式配置退出删除逻辑 var g1 = g.selectAll('path') .data(data) .join( enter => enter.append('path') .attr('stroke','gray') .attr('stroke-width',1) .style('fill', (d, i) => colors(i)) .attr('d', drawpath), update => update .style('fill', (d, i) => colors(i)) .transition() .attr('d', drawpath), exit => exit.remove() ) // 修复1:circle同理加退出删除 g.selectAll('circle') .data(data) .join( enter => enter.append('circle') .attr('fill','red') .attr('r',5) .attr('cx',(d,i) => x(i)) .attr('cy',(d,i) => y(d.value)), update => update .attr('cx',(d,i) => x(i)) .attr('cy',(d,i) => y(d.value)), exit => exit.remove() ) // 修复2:渲染坐标轴前先清空容器内旧内容 var xaxis = d3.select('.xaxis') .attr("transform", "translate(0," + height + ")") .selectAll("*").remove() .call(d3.axisBottom(x)); var yaxis = d3.select('.yaxis') .attr('transform','translate(-5,0)') .selectAll("*").remove() .call(d3.axisLeft(y)); } function fetch_data(){ var num = parseInt(Math.random()*20) + 5 var data = d3.range(num).map(d => { return {value:Math.random()} }) return data } }
HTML代码无需修改:
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.0.0/d3.min.js"></script>
内容的提问来源于stack exchange,提问作者lucky1928
相关产品推荐
相关产品推荐

