如何用D3.js创建日期与小时轴热力图并配置刻度?
D3.js热力图:日期/小时轴刻度与布局修正方案
核心问题梳理
- 原代码中y轴使用
d3.scaleTime()但传入数字类型的domain,导致轴渲染异常(scaleTime仅支持时间对象) - 未对坐标轴应用
tickFormat指定日期/小时的显示格式 - 热力图矩形宽高固定为2px,无法适配日期和小时的区间分布,布局混乱
修正后的完整代码
HTML部分
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js"></script> <div id="viz"></div>
JavaScript部分
const margin = { top: 50, right: 30, bottom: 80, left: 80 }, width = 800 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; const svg = d3.select("#viz") .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})`); const parseTime = d3.utcParse("%m/%d/%Y %I:%M:%S %p"); // 定义格式化规则 const formatDateLabel = d3.timeFormat("%b %d"); // 示例:Jan 01 const formatHourLabel = d3.timeFormat("%I%p"); // 示例:12am、01am // 初始化坐标轴比例尺 const x = d3.scaleTime().range([0, width]); const y = d3.scaleTime().range([0, height]); // 颜色比例尺 const color = d3.scaleOrdinal() .range(["#fff000", "purple", "blue", "green"]); d3.csv("https://raw.githubusercontent.com/sprucegoose1/sample-data/main/subway_delays.csv").then((data) => { // 处理数据:解析时间,提取日期和对应小时的时间对象 data.forEach((d) => { d.fullTime = parseTime(d.Date); d.day = d3.timeDay(d.fullTime); // 提取日期(当天0点) d.hourTime = d3.timeHour(d.fullTime); // 提取当天对应小时的时间对象 }); // 设置坐标轴domain x.domain(d3.extent(data, d => d.day)); y.domain(d3.extent(data, d => d.hourTime)); // 渲染x轴(顶部),应用日期格式化 svg.append("g") .attr("transform", `translate(0, 0)`) .call(d3.axisTop(x).tickFormat(formatDateLabel)) .selectAll("text") .style("text-anchor", "start") .attr("transform", "rotate(45)"); // 旋转日期标签避免重叠 // 渲染y轴(左侧),应用小时格式化 svg.append("g") .call(d3.axisLeft(y).tickFormat(formatHourLabel)); // 渲染热力图矩形 svg.selectAll(".heatmap-rect") .data(data) .enter() .append("rect") .attr("class", "heatmap-rect") .attr("x", d => x(d.day)) .attr("y", d => y(d.hourTime)) // 根据日期区间自动计算宽度 .attr("width", x(d3.timeDay.offset(data[0].day, 1)) - x(data[0].day)) // 根据小时区间自动计算高度 .attr("height", y(d3.timeHour.offset(data[0].hourTime, 1)) - y(data[0].hourTime)) .style("fill", d => color(d.Affected)) .style("stroke", "#fff") // 添加白色边框区分单元格 .style("stroke-width", 0.5); });
关键细节说明
时间数据处理:
使用d3.timeDay和d3.timeHour分别提取日期(当天0点)和小时时间对象,确保坐标轴的scaleTime能正确映射时间值。坐标轴刻度格式化:
- x轴通过
.tickFormat(formatDateLabel)将日期格式化为Jan 01样式,同时旋转标签避免重叠 - y轴通过
.tickFormat(formatHourLabel)将小时格式化为12am/01am样式
- x轴通过
热力图单元格适配:
通过计算相邻日期/小时的比例尺差值,自动设置矩形的宽高,确保每个日期-小时组合对应一个规整的单元格,而非固定尺寸的小方块。
内容的提问来源于stack exchange,提问作者sprucegoose
相关产品推荐
相关产品推荐

