本教程将指导你如何使用 Obsidian 的 DataviewJS 插件创建一个热力图视图,用于展示日记文件中的任务完成情况。以下是完整的实现步骤。
功能概述
- 日历式布局:每个月的小方块按照周排列,空白占位符正确显示。
- 2行6列布局:每个月的小方块组按照2行6列排列。
- 交互功能:鼠标悬停时显示工具提示(日期和任务数量)。
- 动态边框:代表“今天”的小方块有黑色边框,方便快速定位。
- 简洁美观:无边框设计,整体布局紧凑且现代化。
前置条件
-
安装 Obsidian 和必要插件:
-
创建日记文件夹:
-
在你的 Vault 中创建一个名为
日记
的文件夹。 -
每个日记文件的命名格式为
YYYY-MM-DD.md
(例如2023-10-05.md
)。 -
文件内容中使用
- [x]
标记已完成的任务。例如:- [x] 完成任务 1 - [ ] 待办任务 2
-
实现步骤
1. 创建 DataviewJS 代码块
在任意笔记中插入以下 DataviewJS 代码块:
// 获取当前年份
const currentYear = new Date().getFullYear();
// 定义颜色映射
const colors = {
0: "#f0f0f0", // 更浅的灰色(替代白色)
1: "#FFD1FF", // 稍微加深的浅薰衣草紫
2: "#E6B3FF", // 柔和薰衣草紫
3: "#9933FF", // 基础薰衣草紫
4: "#6600CC", // 深薰衣草紫
5: "#330099" // 浓薰衣草紫
};
// 获取今天的日期,格式化为 YYYY-MM-DD
const today = new Date();
const todayFormatted = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, "0")}-${String(today.getDate()).padStart(2, "0")}`;
// 获取“日记”文件夹中的所有文件
const diaryFolder = "日记"; // 日记文件夹路径
const files = app.vault.getMarkdownFiles().filter(file => file.path.startsWith(diaryFolder));
// 创建一个日期到任务数量的映射
const taskCounts = {};
for (let file of files) {
const fileName = file.name.replace(".md", ""); // 去掉 .md 后缀
if (!/^\d{4}-\d{2}-\d{2}$/.test(fileName)) continue; // 确保文件名是 YYYY-MM-DD 格式
const date = fileName;
const content = await app.vault.read(file);
const completedTasks = (content.match(/- \[x\]/gi) || []).length; // 统计已完成任务数量
taskCounts[date] = completedTasks;
}
// 辅助函数:判断某年是否为闰年
function isLeapYear(year) {
return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);
}
// 生成热力图
console.log("开始生成热力图...");
const heatmapContainer = document.createElement("div");
heatmapContainer.className = "heatmap-container";
const months = ["January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"];
const monthDays = [31, isLeapYear(currentYear) ? 29 : 28, 31, 30, 31, 30,
31, 31, 30, 31, 30, 31];
months.forEach((month, monthIndex) => {
console.log(`正在处理月份: ${month}`);
// 创建每个月的容器
const monthDiv = document.createElement("div");
monthDiv.className = "month-container";
// 添加月份标题
const monthTitle = document.createElement("div");
monthTitle.className = "month-title";
monthTitle.textContent = month; // 设置月份名称
monthDiv.appendChild(monthTitle);
// 创建每个月的日历布局
const calendarDiv = document.createElement("div");
calendarDiv.className = "calendar";
// 获取该月的第一天是星期几(0 表示周日,1 表示周一,依此类推)
let firstDayOfWeek = new Date(currentYear, monthIndex, 1).getDay(); // 默认 0=周日, 1=周一...
firstDayOfWeek = (firstDayOfWeek === 0) ? 6 : firstDayOfWeek - 1; // 调整为周一作为第一天
const daysInMonth = monthDays[monthIndex];
// 添加空白占位符(如果该月的第一天不是周一)
for (let i = 0; i < firstDayOfWeek; i++) {
const placeholderDiv = document.createElement("div");
placeholderDiv.className = "day placeholder";
calendarDiv.appendChild(placeholderDiv);
}
// 添加每一天的小方块
for (let day = 1; day <= daysInMonth; day++) {
const date = `${currentYear}-${String(monthIndex + 1).padStart(2, "0")}-${String(day).padStart(2, "0")}`;
const count = taskCounts[date] || 0;
const color = colors[Math.min(count, 5)]; // 最大为 5 个任务
const dayDiv = document.createElement("div");
dayDiv.className = "day";
dayDiv.style.backgroundColor = color;
// 如果是今天的日期,添加黑色边框
if (date === todayFormatted) {
dayDiv.style.border = "1px solid #000"; // 黑色边框
} else {
dayDiv.style.border = "none"; // 其他日期无边框
}
// 添加自定义工具提示
const tooltip = document.createElement("div");
tooltip.className = "tooltip";
tooltip.textContent = `${date}: ${count} tasks`;
tooltip.style.display = "none"; // 默认隐藏
dayDiv.appendChild(tooltip);
// 鼠标悬停时显示工具提示
dayDiv.addEventListener("mouseenter", () => {
tooltip.style.display = "block";
});
// 鼠标移出时隐藏工具提示
dayDiv.addEventListener("mouseleave", () => {
tooltip.style.display = "none";
});
calendarDiv.appendChild(dayDiv);
}
// 将日历布局添加到月份容器中
monthDiv.appendChild(calendarDiv);
heatmapContainer.appendChild(monthDiv);
});
dv.container.appendChild(heatmapContainer); // 将热力图插入到 DataviewJS 容器中
console.log("热力图生成完成!");
2. 添加 CSS 样式
为了使热力图更加美观,我们需要添加一些 CSS 样式。以下是具体操作步骤:
(a) 打开 Obsidian 的 CSS 片段设置
-
打开设置:
- 在 Obsidian 中,点击左下角的齿轮图标(设置)。
-
进入外观设置:
- 在设置页面中,找到并点击“外观”选项。
-
打开 CSS 片段文件夹:
- 向下滚动到“CSS 片段”部分,点击“打开片段文件夹”按钮。
- 这将打开一个文件夹,存储所有自定义的 CSS 文件。
(b) 创建新的 CSS 文件
-
新建文件:
- 在打开的 CSS 片段文件夹中,右键单击空白区域,选择“新建文件”。
- 将文件命名为
heatmap.css
(或其他你喜欢的名字,但必须以.css
结尾)。
-
粘贴样式代码:
- 打开刚刚创建的
heatmap.css
文件,将以下样式代码粘贴进去:
- 打开刚刚创建的
/* 热力图容器样式 */
.heatmap-container {
display: grid;
grid-template-columns: repeat(6, 1fr); /* 四列布局 */
grid-template-rows: repeat(2, auto); /* 三行布局 */
gap: 5px; /* 缩小每个月之间的间距 */
padding: 5px;
width: 750px; /* 容器宽度 */
}
/* 每个月的容器样式 */
.month-container {
display: flex;
flex-direction: column;
gap: 5px; /* 缩小标题和日历之间的间距 */
background-color: transparent; /* 移除背景色 */
border: none; /* 移除边框 */
padding: 0; /* 移除内边距 */
}
/* 每个月的标题样式 */
.month-title {
font-size: 14px; /* 缩小字体大小 */
font-weight: bold; /* 加粗 */
text-align: center; /* 居中对齐 */
color: #333; /* 文字颜色 */
margin-bottom: 5px; /* 缩小标题与日历的间距 */
}
/* 日历布局样式 */
.calendar {
display: grid;
grid-template-columns: repeat(7, 15px); /* 每周 7 天,每列固定宽度 15px */
row-gap: 1px; /* 纵向间距 */
column-gap: 1px; /* 横向间距 */
justify-content: center; /* 居中对齐 */
}
/* 每个小方块的样式 */
.day {
width: 15px; /* 固定宽度 */
height: 15px; /* 固定高度 */
border: none; /* 移除边框 */
display: flex;
align-items: center;
justify-content: center;
position: relative; /* 用于定位工具提示 */
}
/* 空白占位符样式 */
.placeholder {
background-color: transparent; /* 空白占位符 */
border: none; /* 隐藏边框 */
}
/* 自定义工具提示样式 */
.tooltip {
position: absolute;
top: 100%; /* 显示在小方块下方 */
left: 50%;
transform: translateX(-50%); /* 水平居中 */
background-color: #333; /* 背景颜色 */
color: #fff; /* 文字颜色 */
padding: 5px;
border-radius: 4px; /* 圆角 */
white-space: nowrap; /* 防止文字换行 */
font-size: 12px; /* 字体大小 */
z-index: 10; /* 确保工具提示在最上层 */
display: none; /* 默认隐藏 */
}
/* 鼠标悬停时显示工具提示 */
.day:hover .tooltip {
display: block; /* 显示工具提示 */
}
(c) 启用 CSS 片段
-
返回 Obsidian 设置:
- 回到 Obsidian 的设置页面,刷新“CSS 片段”部分。
-
启用片段:
- 你会看到刚刚创建的
heatmap.css
文件出现在列表中。 - 点击右侧的开关,启用该片段。
- 你会看到刚刚创建的
-
验证样式:
- 刷新 Obsidian 页面(或重新打开笔记),确认热力图的样式是否生效。
3. 测试和验证
-
运行代码:
- 打开包含 DataviewJS 代码块的笔记,确认热力图正确生成。
-
检查功能:
- 确认每个月的小方块是否按周排列。
- 确认鼠标悬停提示是否正常工作。
- 确认“今天”的小方块是否有黑色边框。
-
调整细节:
- 如果需要进一步优化,可以根据需求调整 CSS 样式或 JavaScript 逻辑。
常见问题
-
热力图未显示:
- 确保日记文件夹路径正确(默认为
日记
)。 - 确保日记文件的命名格式为
YYYY-MM-DD.md
。
- 确保日记文件夹路径正确(默认为
-
CSS 样式未生效:
- 确保 CSS 文件名正确(例如
heatmap.css
)。 - 确保 CSS 片段已启用。
- 确保 CSS 文件名正确(例如
-
任务统计不准确:
- 确保日记文件中使用了
- [x]
标记已完成任务。
- 确保日记文件中使用了
总结
通过以上步骤,你可以轻松实现一个功能强大的热力图视图,用于展示日记文件中的任务完成情况。希望这个教程对你有所帮助!如果你有任何问题或建议,请随时提出。
祝你使用愉快!