要在网页上使用D3.js创建日历可视化,可以通过以下步骤实现一个交互式日历,本教程以2024年9月为例,完整代码可直接嵌入网页使用。
日历本质上是二维日期矩阵的可视化映射,通过D3的布局算法,我们将日期数据转换为:
<div id="calendar"></div> <script src="https://d3js.org/d3.v7.min.js"></script> <script> // 基础配置 const width = 800; const cellSize = 80; const weekDays = ["日", "一", "二", "三", "四", "五", "六"]; // 创建SVG画布 const svg = d3.select("#calendar") .append("svg") .attr("width", width) .attr("height", cellSize * 7); // 生成日期数据集 const startDate = new Date(2024, 8, 1); // 2024年9月1日 const endDate = new Date(2024, 9, 0); // 获取9月最后一天 const dates = d3.timeDays(startDate, endDate); // 创建日历布局 const calendarLayout = d3.timeWeek .range(startDate, endDate) .map(week => d3.timeDay.range(week, d3.timeDay.offset(week, 7))); // 绘制日历格子 svg.selectAll(".week") .data(calendarLayout) .enter().append("g") .attr("transform", (d,i) => `translate(0,${i * cellSize})`) .selectAll(".day") .data(d => d) .enter().append("rect") .attr("class", "day-cell") .attr("x", (d,i) => i * cellSize) .attr("width", cellSize - 2) .attr("height", cellSize - 2) .style("fill", d => d.getMonth() === 8 ? "#fff" : "#f5f5f5") // 非本月日期 .style("stroke", "#eee"); // 添加日期文字 svg.selectAll(".day-text") .data(dates) .enter().append("text") .attr("x", (d,i) => { const week = Math.floor(i / 7); return (i % 7) * cellSize + 10; }) .attr("y", (d,i) => Math.floor(i / 7) * cellSize + 30) .text(d => d.getDate()) .style("font-size", "24px") .style("fill", d => d.getDay() === 0 ? "red" : "#333"); // 添加周次标识 svg.append("g") .selectAll("text") .data(weekDays) .enter().append("text") .attr("x", (d,i) => i * cellSize + 30) .attr("y", 20) .text(d => d) .style("font-weight", "bold"); </script> <style> .day-cell:hover { fill: #e3f2fd !important; cursor: pointer; } text { font-family: 'Arial', sans-serif; user-select: none; } </style>
数据绑定:通过.datum()
方法关联每日数据
.day-cell.datum(function(d) { return { date: d, value: Math.random() * 100 // 示例数据 }; })
交互功能:添加点击事件
.on("click", function(event, d) { d3.select(this).style("fill", "#b3e5fc"); console.log("选中日期:", d.toLocaleDateString()); })
热力效果:根据数据值染色
.style("fill", d => { const value = d.value || 0; return d3.interpolateBlues(value/100); })
d3.timeDays
生成完整日期序列d3.timeWeek
创建周次分组.attr("viewBox", `0 0 ${width} ${cellSize*6}`)
d3.timeFormat
适配不同日期格式d3.json()
实现动态数据绑定参考文献: