D3.js退出过渡动画失效:如何实现文本左移淡出效果?
问题根源分析
你的退出动画没触发的核心原因是D3无法识别新旧数据的差异,导致exit()集合始终是空的。具体来说:
- 每次调用
displayData时,你把单个数据包装成数组[data]传入,然后用.data([data])绑定数据,但没有指定key函数。D3默认用索引匹配数据,新旧数组都是长度1的数组,索引0的位置始终有数据,所以旧元素不会被标记为"需要退出"。 - 另外,你在处理enter/update/exit的顺序上也没有配合动画逻辑设计,进一步导致动画无法触发。
修正方案(微调现有代码即可实现)
只需要两个关键改动就能解决问题:
- 给
data()方法添加key函数,让D3通过Date字段区分不同的数据项 - 调整enter/update/exit的处理逻辑,确保退出动画能正确触发
下面是修正后的完整代码:
修正后的JS代码
var width = 200; var height = 100; var margins = {left:20, right:20, top:0, bottom:0 }; var svg = d3.select('body') .append('svg') .attr('width', width+margins.left+margins.right) .attr('height', height+margins.top+margins.bottom); var data = [ {'Date':'May-24', 'Event':'Event 1'}, {'Date':'Jun-30', 'Event':'Event 2'} ]; var dateG = svg.append('g') .attr('transform', 'translate('+margins.left+','+(margins.top+40)+')') .attr('class', 'uiText'); function displayData(newData) { // 关键改动1:添加key函数,用Date字段作为数据项的唯一标识 var dateText = dateG .selectAll('text') .data([newData], function(d) { return d.Date; }); // 处理enter的新元素:设置初始不可见状态,再执行入场动画 dateText.enter() .append('text') .attr('x', -50) // 初始在左侧不可见位置 .attr('opacity', 0) .transition() // 新元素淡入+右移到目标位置 .duration(750) .attr('x', 10) .attr('opacity', 1) .text(function(d) { return d.Date; }); // 处理update的元素(此场景下不会触发,因为每次都是全新数据) dateText .text(function(d) { return d.Date; }); // 关键改动2:正确触发exit元素的退出动画 dateText.exit() .transition() .duration(750) .attr('x', -50) .attr('opacity', 0) .remove(); // 过渡结束后移除元素 } displayData(data[0]); d3.select('#button').on('click', function() { displayData(data[1]); });
修正后的HTML代码(补全闭合标签+添加基础样式)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <script src="//d3js.org/d3.v3.min.js"></script> <style> .button { cursor: pointer; padding: 8px; background: #eee; display: inline-block; margin: 10px; } </style> </head> <body> <div class='button' id='button'>Next Event</div> </body> </html>
改动细节解释
- key函数的作用:
data([newData], function(d) { return d.Date; })告诉D3,每个数据项的唯一标识是Date字段。当你切换到Jun-30的数据时,D3会发现旧的May-24数据没有匹配项,就会把旧的text元素放入exit()集合,动画才有了执行对象。 - enter元素的入场动画:新增的text元素先放在左侧不可见位置,然后通过过渡动画移动到目标位置并淡入,和退出动画形成对称的视觉效果。
- exit元素的过渡逻辑:旧元素先左移并淡出,过渡完成后再被移除,这样就能看到完整的退出动画流程了。
额外说明
你之前尝试的delay()、单独调用remove()等操作无效,本质是因为exit()集合一直是空的,根本没有元素能执行这些过渡。只要解决了数据匹配的核心问题,退出动画就能正常触发。
内容的提问来源于stack exchange,提问作者Arash Howaida
相关产品推荐
相关产品推荐

