求助:解决D3.v5水平堆叠柱状图JSON数据加载报错问题
解决D3.v5水平堆叠柱状图的报错问题
咱们先逐个拆解你遇到的核心错误,然后给出修复后的完整代码,再聊聊后续优化的小建议:
错误1:<rect> width属性解析失败
你设置柱子宽度的时候写了return d;,但这里的d是数组里的完整对象(比如{ position: 1, label: '70k', value: 1708026 }),D3没办法把一个对象转换成SVG能识别的长度值,所以才会报Expected length, "[object Object]"的错误。
修复方法:把宽度的回调改成返回对象的value属性,也就是return d.value;,这样传给width的就是纯数值了。
错误2:sum函数的参数与内部逻辑错误
你调用sum函数的时候传了bar1Data.value,但bar1Data是一个数组,根本没有value属性,这直接导致了Cannot read property '0' of undefined的报错;另外就算传对了数组,sum函数里直接累加array[i]也不对,因为数组里的元素是对象,不是纯数值。
修复方法:
- 调用sum时传入完整的
bar1Data数组:sum(bar1Data, 0, i) - 改造sum函数,累加每个元素的
value属性:
function sum(array, start, end) { var total = 0; for (var i = start; i < end; i++) { total += array[i].value; // 这里要明确取对象的value值 } return total; }
错误3:DOM选择器不匹配
你的HTML里是<div id="bar"></div>,但代码里用了d3.select('#bars')(多了个s),这会导致D3找不到要挂载SVG的容器,等于白忙活一场。
修复方法:把选择器改成d3.select('#bar'),和HTML里的id对应上。
修复后的完整代码
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script> <div id="bar"></div> <script> var bar1Data = [{ position: 1, label: '70k', value: 1708026 }, { position: 2, label: '71K - 149K', value: 1915059 }]; // 按position升序排序 bar1Data = bar1Data.sort(function(a, b) { return d3.ascending(a.position, b.position); }); var colors = ['green', 'blue']; var bars = d3.select('#bar') .append('svg') .attr('width', 800) .attr('height', 200); bars.selectAll('rect') .data(bar1Data) .enter() .append('rect') .attr('width', function(d) { return d.value; // 取对象的value作为宽度 }) .attr('x', function(d, i) { return sum(bar1Data, 0, i); // 传入完整数组计算堆叠偏移 }) .attr('fill', function(d, i) { return colors[i]; }) .attr('y', 0) .attr('height', 100); // 修正后的sum函数,累加每个元素的value function sum(array, start, end) { var total = 0; for (var i = start; i < end; i++) { total += array[i].value; } return total; } </script>
后续优化小建议
现在的代码直接用原始数值作为宽度,你的value都是百万级,而SVG宽度只有800,柱子肯定会超出画布。后续可以用D3的线性比例尺来映射数值到合适的范围,比如:
// 定义线性比例尺,把总数值映射到SVG的可用宽度(留50px边距) var xScale = d3.scaleLinear() .domain([0, d3.sum(bar1Data, d => d.value)]) // 定义域:0到所有value的总和 .range([0, 750]); // 值域:0到750px(SVG总宽度800,留50px边距) // 然后把width和x的属性改成用比例尺转换: .attr('width', function(d) { return xScale(d.value); }) .attr('x', function(d, i) { return xScale(sum(bar1Data, 0, i)); })
这样柱子就会自适应SVG的宽度,不会超出范围啦。
内容的提问来源于stack exchange,提问作者AliShahVis
相关产品推荐
相关产品推荐

