在 components 目录下新增 Heatmap.astro 组件
---
import { format, eachDayOfInterval, startOfYear, endOfYear } from 'date-fns';
import { getColorScale } from '../utils/colorscale';
import wordCountData from '../data/word-counts.json'
// 格式化数据为字典
const wordCountMap = new Map(wordCountData.map(({ date, charCount }) => [date, charCount]));
// 计算所有日期的字数总和
const getTotalWordCount = wordCountData.reduce((total, { charCount }) => {
return total + charCount; // 累加每个日期的字数
}, 0);
console.log(getTotalWordCount)
const today = new Date();
const startDate = startOfYear(today);
const endDate = endOfYear(today);
const days = eachDayOfInterval({ start: startDate, end: endDate });
---
<div class="bg-white dark:bg-zinc-900 dark:text-zinc-200 p-4 rounded-lg shadow-md">
<h3 class="text-center mb-2">码字热力图</h3>
<div class="relative">
<div class="grid grid-cols-52 gap-[6px] h-[100px]">
{days.map(day => {
const date = format(day, 'yyyy-MM-dd');
const wordCount = wordCountMap.get(date) || 0;
const bgColor = getColorScale(wordCount);
return (
<div
class="w-[6px] h-[6px] rounded-[1px] tooltip"
style={`background-color: ${bgColor};`}
data-tip={`${format(day, 'yyyy-MM-dd')}: ${wordCount} 字符`}
/>
);
})}
</div>
</div>
<div class="flex items-center justify-end gap-2 mt-2 text-xs text-gray-600">
<span>总字符:{getTotalWordCount}</span>
</div>
</div>
<style>
.grid-cols-52 {
grid-template-columns: repeat(52, minmax(0, 1fr));
}
.tooltip {
position: relative;
}
.tooltip:hover::after {
content: attr(data-tip);
position: absolute;
bottom: 100%;
left: 50%;
transform: translateX(-50%);
padding: 2px 4px;
background-color: #333;
color: white;
border-radius: 2px;
font-size: 10px;
white-space: nowrap;
z-index: 10;
}
</style>
在 utils 目录下新增 `colorscale.ts
export function getColorScale(count: number): string {
if (count === 0) return '#ebedf0';
if (count < 600) return '#A5D6A7';
if (count < 900) return '#66BB6A';
if (count < 1200) return '#43A047';
if (count < 1500) return '#2E7D32';
return '#1B5E20';
}
在 types 目录下新增 Article.ts
Article.ts
定义类型
export interface Article {
id: string;
title: string;
content: string;
date: Date;
wordCount: number;
}
在 index.astro 文件中引用
---
import { SITE_DESCRIPTION, SITE_TITLE } from "../consts";
import Heatmap from '../components/Heatmap.astro';
import Layout from "../layouts/Layout.astro";
---
<Layout
title={SITE_TITLE}
description={SITE_DESCRIPTION}
className="flex h-svh flex-col justify-center"
>
<main class="space-y-4">
<p>
偶尔想到的一些内容会做记录,题材不限于技术文章,更多的是一些个人的只言片语。博主平时喜欢运动,以糊代码为乐趣,偶尔也靠读些哲学书籍来打发无聊。
</p>
<Heatmap />
</main>
</Layout>
注意组件之间的相互引用关系