基于D3.js在24小时时间轴上计算条形的起始位置与宽度
解决D3.js 24小时时间轴条形图的位置与宽度计算问题
看起来你在实现时间段条形图时,核心问题出在X轴的定义域设置和条形宽度的计算逻辑上,咱们一步步来修正:
1. 修正X轴的时间范围
你当前的X轴定义域是[new Date(), new Date()],这相当于只取了当前时刻,完全覆盖不了24小时的范围。应该把X轴的定义域设为从今天0点到明天0点,也就是完整的24小时区间:
var today = new Date(); today.setHours(0, 0, 0, 0); var tomorrow = new Date(today); tomorrow.setDate(today.getDate() + 1); // 明天0点 var xScale = d3.scaleTime() .domain([today, tomorrow]) // 完整的24小时区间 .nice(d3.timeHour, 1) // 按小时刻度优化 .range([0, width]); // 这里不需要再减margin.left,因为svg已经做了translate
2. 正确计算条形的起始位置与宽度
- 起始位置:直接用
xScale(d.fromHours)就可以,因为d.fromHours已经是转换后的Date对象,X轴比例尺会自动映射到正确的像素位置。 - 宽度:不能直接对时间差用
xScale,应该用结束时间的X坐标减去开始时间的X坐标,也就是xScale(d.toHours) - xScale(d.fromHours)。之前的代码里用了xScale(Math.abs(d.toHours - d.fromHours / 36e5)),这是完全错误的,因为d.toHours - d.fromHours得到的是毫秒数,而xScale只接受Date对象。
修正后的矩形属性设置代码:
var rect = layer.selectAll("rect") .data(function(d, i) { d.map(function(b) { b.colorIndex = i; return b; }); return d; }) .enter() .append("rect") .transition() .duration(500) .delay(function(d, i) { return i * 10; }) .attr("y", function(d, i, j) { var k = Array.prototype.indexOf.call(j[i].parentNode.parentNode.childNodes, j[i].parentNode); return yScale(d.gate) + yScale.bandwidth() / n * k; }) .attr("height", yScale.bandwidth() / n) .attr("x", function(d) { return xScale(d.fromHours); // 直接用转换后的起始时间 }) .attr("width", function(d) { return xScale(d.toHours) - xScale(d.fromHours); // 结束位置减开始位置得到宽度 }) .attr("class", "bar") .style("fill", function(d) { return dataset["colors"][d.colorIndex]; });
3. 其他小调整
- 你的X轴
tickSize(-height)会绘制贯穿整个图表的网格线,这个没问题,但要确保X轴的transform是正确的,应该是translate(0, height),因为svg的g已经做了margin的translate。 - 修正y轴的选择器,原来的
svg.select("g")会选中第一个g元素(也就是我们的主绘图g),应该用svg.append("g")来创建y轴的容器:
svg.append("g") .attr("class", "y axis") .call(yAxis);
测试验证
修改后,你可以在控制台打印xScale(d.fromHours)和xScale(d.toHours)的值,确认它们对应24小时轴上的正确位置,宽度也会是两个位置的差值,这样条形就能准确展示时间段了。
内容的提问来源于stack exchange,提问作者Rajkishan Swami
相关产品推荐
相关产品推荐

