要在网页上使用D3.js创建日历可视化,可以通过以下步骤实现一个交互式日历,本教程以2024年9月为例,完整代码可直接嵌入网页使用。
<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
创建周次分组 - 坐标映射:通过(i % 7)计算横向坐标,Math.floor(i/7)计算纵向坐标
- 性能优化:使用虚拟DOM进行批量元素操作
适配调整建议
- 响应式布局:添加viewBox属性实现自适应
.attr("viewBox", `0 0 ${width} ${cellSize*6}`)
- 国际化:使用
d3.timeFormat
适配不同日期格式 - 数据加载:结合
d3.json()
实现动态数据绑定
参考文献:
- D3官方日历示例 (https://observablehq.com/@d3/calendar)
- MDN Date对象文档 (https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Date)
- 《Interactive Data Visualization for the Web》第二版,第7章时间数据处理