D3 v3升级至v5后出现‘Cannot read properties of null (reading 'transition')’错误求助
D3 v3升级v5后"Cannot read properties of null (reading 'transition')"错误修复
问题背景
将D3可视化项目从v3升级到v5,已完成d3.svg.arc→d3.arc、d3.layout.pie→d3.pie等API适配,但运行时抛出错误:
"Cannot read properties of null (reading 'transition')"
错误原因
- 缓动函数写法变更:D3 v5废弃了v3中
.ease('cubic')的字符串参数写法,必须传入d3.easeCubic这类函数引用,否则过渡对象初始化失败返回null,触发transition读取错误。 - 弧生成器调用方式:v3中直接将弧生成器函数传给
.attr("d")的写法,在v5中需显式调用(如arc()而非arc),否则无法生成正确的路径字符串。 - SVG属性兼容性:v3中的
xlink:href属性在v5中需改为标准href,D3 v5会自动处理SVG命名空间。
修复步骤
- 替换缓动函数:将
.ease('cubic')改为.ease(d3.easeCubic) - 修正弧生成器调用:所有
.attr("d", arc)改为.attr("d", arc()) - 更新SVG图片属性:将
xlink:href替换为href - 清理冗余代码:移除未使用的
pie生成器
修复后的完整代码
JavaScript
// set the dimensions and margins of the graph var margin = { top: 50, right: 30, bottom: 10, left: 50 }, width = 900 - margin.left - margin.right, height = 150 - margin.top - margin.bottom; // append the svg object to the body of the page var svg = d3.select("#chart") .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); var outerRadius = 45; var innerRadius = outerRadius - 5; var color = ['#ec1561', '#2a3a46', '#ec1561']; var data = [ { "value": 90, "variance": 6 } ]; var percent = d3.max(data, function(d) { return d.value; }); var arc = d3.arc() .innerRadius(30) .outerRadius(35) .startAngle(0) .endAngle(2 * Math.PI); var arc2 = d3.arc() .innerRadius(30) .outerRadius(35) .startAngle(0) .endAngle(2 * Math.PI); //The circle is following this var arcDummy = d3.arc() .innerRadius((outerRadius - innerRadius) / 2 + innerRadius) .outerRadius((outerRadius - innerRadius) / 2 + innerRadius) .startAngle(0); var arcLine = d3.arc() .innerRadius(innerRadius) .outerRadius(outerRadius) .startAngle(0); //background var path = svg.append('path') .attr("d", arc()) .attr("fill", color[1]) .attr("opacity", 0.2); var path2 = svg.append('path') .attr("d", arc2()) .attr("fill", color[1]) .attr("opacity", 0.2); var pathForeground = svg.append('path') .datum({ endAngle: 0 }) .attr("d", arcLine({ endAngle: 0 })) .attr("fill", "#137A76"); //Dummy Arc for Circle var pathDummy = svg.append('path') .datum({ endAngle: 0 }) .attr("d", arcDummy({ endAngle: 0 })) .attr("fill", color[0]); var endCircle = svg.append('circle') .attr("r", 0) .attr("transform", 'translate(0,' + (-outerRadius + 7) + ')') .attr("stroke", color[0]) .attr("stroke-width", 5) .attr("fill", color[2]); var middleTextCount = svg.append('text') .datum(0) .text(function(d) { return d + '%'; }) .attr("class", 'middleText') .attr("text-anchor", 'middle') .attr("dy", 0) .attr("dx", 75) .attr("fill", '#ec1561') .attr("font-size", '20px'); svg .data(data) .append("svg:image") .attr("y", 20) .attr("x", 50) .attr("width", 8) .attr("href", function(d) { if (d.variance < 0 ) { return "https://upload.wikimedia.org/wikipedia/commons/thumb/0/04/Red_Arrow_Down.svg/2048px-Red_Arrow_Down.svg.png"; } else { return "https://upload.wikimedia.org/wikipedia/commons/thumb/5/50/Green_Arrow_Up.svg/1200px-Green_Arrow_Up.svg.png"; } }); svg .append("svg:image") .attr("y", -25) .attr("x", -25) .attr("width", 50) .attr("href", "https://cdn-icons-png.flaticon.com/512/2879/2879567.png"); svg .append("text") .attr("class", "title") .attr("y", 15) .attr("x", 50) .text("Positive Feedback"); svg .data(data) .append("text") .attr("class", "varweek") .attr("y", 27) .attr("x", 62) .text(function(d) { return d.variance + '% vs last week'; }); var gradient = svg.append("defs") .append("linearGradient") .attr("id", "gradient") .attr("x1", "0%") .attr("y1", "0%") .attr("x2", "0%") .attr("y2", "100%") .attr("spreadMethod", "pad"); gradient.append("stop") .attr("offset", "0%") .attr("stop-color", "#DA0D91") .attr("stop-opacity", 0.05); gradient.append("stop") .attr("offset", "100%") .attr("stop-color", "#DA0D91") .attr("stop-opacity", 1); var arcTweenOld = function(transition, percent, oldValue) { transition.attrTween("d", function(d) { var newAngle = (percent / 100) * (2 * Math.PI); var interpolate = d3.interpolate(d.endAngle, newAngle); var interpolateCount = d3.interpolate(oldValue, percent); return function(t) { d.endAngle = interpolate(t); var pathForegroundCircle = arcLine(d); middleTextCount.text(Math.floor(interpolateCount(t)) + '%'); var pathDummyCircle = arcDummy(d); var coordinate = pathDummyCircle.split("L")[1].split("A")[0]; endCircle.attr('transform', 'translate(' + coordinate + ')'); return pathForegroundCircle; }; }); }; var oldValue = 0; var animate = function() { pathForeground.transition() .duration(750) .ease(d3.easeCubic) .call(arcTweenOld, percent, oldValue); oldValue = percent; }; setTimeout(animate, 0);
CSS
@import url('https://fonts.googleapis.com/css2?family=Georama:ital@1&display=swap'); body { background-color: #1B1F2A; width: 100%; font-family: 'Georama', sans-serif; height: 100%; } .widget { margin: 0 auto; width:350px; margin-top:50px; background-color: #222D3A; border-radius: 5px; box-shadow: 0px 0px 1px 0px #06060d; } .header{ background-color: #29384D; height:40px; color:#929DAF; text-align: center; line-height: 40px; border-top-left-radius: 7px; border-top-right-radius: 7px; font-weight: 400; font-size: 1.5em; text-shadow: 1px 1px #06060d; } .chart-container{ padding:25px; } .shadow { -webkit-filter: drop-shadow( 0px 3px 3px rgba(0,0,0,.1) ); filter: drop-shadow( 0px 3px 3px rgba(0,0,0,.1) ); } .title { font-family: 'Georama', sans-serif; fill: white; font-size: 11px; } .varweek { fill: white; font-size: 9px; opacity: 0.25 }
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script> <div id="chart" class="chart-container"></div>
内容的提问来源于stack exchange,提问作者Gangrel
相关产品推荐
相关产品推荐

