之前根据蜗牛哥的教程弄了一版使用Cal-heatmap库生成的热力图 博客更新热力图,不过这个方式需要加载四五个js文件还有一些css文件,比较拖慢整个网页的加载速度,所以蜗牛哥后来又出了一个CSS 和 JS 实现博客热力图 方案,不过当时我闲麻烦一直没跟着做。
不过我也一直在思考该怎么用纯CSS和JS更为简便的实现这个功能,今天灵光一闪,有了些思路,所以趁着这股热乎劲把这个功能实现了,撰文分享一下我的思路。
我的思路其实很简单,因为博客上显示的热力图其实就是一个很多小方块组成的方阵,所以其实不用管什么年份、月份之类的,我们只需生成固定个数的小方块,再将匹配的数据填充进去不就好了吗?
我的构思如下:
grid-colum
,每列7个 grid-tem
grid-item
中有一个 item-info
用于展示信息item-info
中根据需要设置选项这里通过 grid
布局实现了Heatmap的效果,我为了省事直接通过 after
搭配 content
在最后几个方块添加了星期标注。
这边可以根据你的需求生成这部分数据
几乎所有代码都是通过ChatGPT生成,相关说明我直接写在代码中。
// 字符串转换为时间格式
function parseDate(str) {
return new Date(str);
}
// 获取本周的星期天作为开始时间
function getThisSunday(date) {
const dayOfWeek = date.getDay();
const daysToSunday = dayOfWeek === 0 ? 0 : 7 - dayOfWeek;
const thisSunday = new Date(date);
thisSunday.setDate(thisSunday.getDate() + daysToSunday);
return thisSunday;
}
const today = new Date();
const sunday = getThisSunday(today);
let startDate = new Date(sunday);
// 往后推63天
startDate.setDate(sunday.getDate() - 62);
// 构建基础数据
function dateBuild(data) {
const dateCounts = {};
// 标准化json中的时间格式
data.forEach((item) => {
const dateStr = parseDate(item.date).toISOString().split("T")[0];
dateCounts[dateStr] = (dateCounts[dateStr] || 0) + 1;
});
const result = [];
// 生成63天的数据数组
for (
let currentDate = sunday;
currentDate >= startDate;
currentDate.setDate(currentDate.getDate() - 1)
) {
const dateStr = currentDate.toISOString().split("T")[0];
const count = dateCounts[dateStr] || 0;
// 通过时间去json数据获取当天的文章
const dataContent = data.filter(
(item) =>
parseDate(item.date).toISOString().split("T")[0] === dateStr
);
// 放进数组中
result.push({
date: dateStr,
count: count,
data: dataContent,
});
}
// 统计文章字符总数(好像可以整合进上面的循环中,下次再优化把。
result.forEach((item) => {
var sumOfWordcounts = item.data.reduce((accumulator, currentItem) => {
return (accumulator = currentItem.word_count || 0);
}, 0);
item.wordcount = sumOfWordcounts;
});
return result;
}
// 填充数据
export default function fillGrid(data) {
// 先将构建用于渲染的数据
let articles = dateBuild(data);
// 获取热力图元素
const gridContainer = document.getElementById("relitu-container");
// 构建grid-item元素
const gridItemTemplate = document.createElement("div");
gridItemTemplate.className = "grid-item";
// 倒序遍历文章数据
articles
.slice()
.reverse()
.forEach((article, index) => {
const gridItem = gridItemTemplate.cloneNode(false);
// 构建提示字符串
const tooltipStr = article.data
.map(
(item, i) =>
`- <a href='${item.href}'>${item.title}</a></br>`
)
.join(" ");
// 构建grid-info中的信息
// 关于小方框颜色部分,我这里直接用的百分比透明度,起步0.2,5000字100%
gridItem.innerHTML = `<div style="${ article.wordcount != 0 ? `background-color:rgba(77, 208, 90,${article.wordcount / 5000 + 0.2})`:""}"
class="item-info ${ article.count != 0 ? `item-tippy" data-tippy-content="共 ${article.count} 篇,共 ${article.wordcount} 字<br />${tooltipStr}"`:'"' } data-date="${article.date}"></div>`;
// 计算排列顺序
const colIndex = Math.floor(index / 7);
const rowIndex = index % 7;
if (rowIndex === 0) {
const gridColumn = document.createElement("div");
gridColumn.className = "grid-column";
gridContainer.appendChild(gridColumn);
}
// 根据顺序构建
const gridColumns = document.getElementsByClassName("grid-column");
if (gridColumns[colIndex]) {
gridColumns[colIndex].append(gridItem);
} else {
// 如果列索引超出了当前已有的列,需要创建新的列
const newColumn = document.createElement("div");
newColumn.className = "grid-column";
gridContainer.appendChild(newColumn);
newColumn.append(gridItem);
}
});
}
我这里用的fetch加载的json文件,刚刚测试了一下速度还是没有直接将json生成在html中速度快,下一版做一下改动。
加入评论