Blog Function Update 2025 (1)
由于郑州最近的雨夹雪天气,已经一周没有骑行了,实在憋得不行,给自己找点事做,今天中午下班时更新了一下博客
Update details
- 修复了柱形图显示错位
- 移除了骑行页面的活动天数
- 新增了全年骑行总时长、全年骑行总公里数
- 柱形图的宽度不再由骑行时长来计算,而是由骑行公里数来计算显示
- 新增春节快乐红灯笼(移动端不支持)
- 移除
node-sass
包,由sass
代替
Fix Bugs:柱形图显示错位
当前的柱形图仅为有骑行数据的周生成柱形图,导致柱形图与日历中的周对齐错位,所以即某周没有骑行数据时,柱形图也要生成一根柱子
function generateBarChart() {
const barChartElement = document.getElementById('barChart');
// 清空柱形图内容
barChartElement.innerHTML = '';
const today = getChinaTime();
const startDate = getStartDate(today, 21);
// 创建所有周的时间范围
const weeklyData = {};
let currentWeekStart = new Date(startDate);
currentWeekStart.setUTCHours(0, 0, 0, 0);
// 按周计算未来 4 周的日期范围
for (let i = 0; i < 4; i++) {
const weekStart = new Date(currentWeekStart);
const weekEnd = new Date(weekStart);
// 一周结束日期为开始日期 +6 天
weekEnd.setUTCDate(weekStart.getUTCDate() + 6);
const weekKey = `${weekStart.toISOString().split('T')[0]} - ${weekEnd.toISOString().split('T')[0]}`;
// 初始化每周骑行数据为 0
weeklyData[weekKey] = 0;
// 移动到下一周
currentWeekStart.setUTCDate(currentWeekStart.getUTCDate() + 7);
}
// 累加每周的骑行距离
processedActivities.forEach(activity => {
const activityDate = new Date(activity.activity_time);
// 活动所在周的开始日期
const weekStart = getWeekStartDate(activityDate);
const weekEnd = new Date(weekStart);
weekEnd.setUTCDate(weekStart.getUTCDate() + 6);
const weekKey = `${weekStart.toISOString().split('T')[0]} - ${weekEnd.toISOString().split('T')[0]}`;
if (weeklyData[weekKey] !== undefined) {
weeklyData[weekKey] += parseFloat(activity.riding_distance);
}
});
// 获取最大骑行距离(用于柱形图比例)
const maxDistance = Math.max(...Object.values(weeklyData), 0);
// 创建并显示每周的柱形图
Object.keys(weeklyData).forEach(week => {
// 当前周的骑行距离
const distance = weeklyData[week];
const barContainer = document.createElement('div');
barContainer.className = 'bar-container';
const bar = document.createElement('div');
bar.className = 'bar';
// 计算柱形图的宽度
const width = maxDistance > 0 ? (distance / maxDistance) * 190 : 0;
bar.style.setProperty('--bar-width', `${width}px`);
const distanceText = document.createElement('div');
distanceText.className = 'cycling-kilometer';
distanceText.innerText = '0 km';
const messageBox = createMessageBox();
const clickMessageBox = createMessageBox();
barContainer.style.position = 'relative';
bar.appendChild(distanceText);
barContainer.appendChild(bar);
barContainer.appendChild(messageBox);
barContainer.appendChild(clickMessageBox);
barChartElement.appendChild(barContainer);
// 动画效果:逐渐显示柱形图宽度
bar.style.width = '0';
bar.offsetHeight;
bar.style.transition = 'width 1s ease-out';
bar.style.width = `${width}px`;
distanceText.style.opacity = '1';
// 动态更新柱形图的数值
animateText(distanceText, 0, distance, 1000, true);
setupBarInteractions(bar, messageBox, clickMessageBox, distance);
});
}
// 动态文本显示
function animateText(element, startValue, endValue, duration, isDistance = false) {
const startTime = performance.now();
function update() {
const elapsed = performance.now() - startTime;
const progress = Math.min(elapsed / duration, 1);
const currentValue = (progress * endValue).toFixed(2);
element.innerText = isDistance ? `${currentValue} km` : `${currentValue}h`;
if (progress < 1) {
requestAnimationFrame(update);
} else {
element.innerText = isDistance ? `${endValue.toFixed(2)} km` : `${endValue.toFixed(2)}h`;
}
}
update();
}
New:全年骑行总时长、全年骑行总公里数
// 显示总活动数和总公里数
function displayTotalActivities(activities) {
// 全年骑行时长
const ridingTimeThisYear = document.getElementById('totalCount');
// 全年骑行公里数
const milesRiddenThisYear = document.getElementById('milesRiddenThisYear');
// 动态年标题《2025 骑行总时长》
const totalTitleElement = document.getElementById('totalTitle');
if (!ridingTimeThisYear || !milesRiddenThisYear || !totalTitleElement) return;
const ridingTimeThisYearValue = ridingTimeThisYear.querySelector('#ridingTimeThisYearValue');
const milesRiddenThisYearValue = milesRiddenThisYear.querySelector('#milesRiddenThisYearValue');
const totalCountSpinner = ridingTimeThisYear.querySelector('.loading-spinner');
const milesRiddenThisYearSpinner = milesRiddenThisYear.querySelector('.loading-spinner');
totalCountSpinner.classList.add('active');
milesRiddenThisYearSpinner.classList.add('active');
const currentYear = new Date().getFullYear();
totalTitleElement.textContent = `${currentYear} 骑行总时长`;
// 筛选全年活动数据
const filteredActivities = activities.filter(activity => {
const activityYear = new Date(activity.activity_time).getFullYear();
return activityYear === currentYear;
});
// 计算全年活动时间的总和(单位:小时)
const totalMovingTime = filteredActivities.reduce((total, activity) => {
return total + parseFloat(activity.moving_time) || 0;
}, 0);
// 计算全年总公里数
const totalKilometers = calculateTotalKilometers(filteredActivities);
// 动画效果
animateCount(ridingTimeThisYearValue, totalMovingTime, 1000, 50, false);
animateCount(milesRiddenThisYearValue, totalKilometers, 1000, 50, true);
setTimeout(() => {
console.log(totalKilometers.toFixed(2));
ridingTimeThisYearValue.textContent = `${totalMovingTime.toFixed(2)} h`;
milesRiddenThisYearValue.textContent = `${totalKilometers.toFixed(2)} km`;
totalCountSpinner.classList.remove('active');
milesRiddenThisYearSpinner.classList.remove('active');
}, 1000);
}
// 加载数据并生成日历
(async function() {
const today = getChinaTime();
const startDate = getStartDate(today, 21);
const activities = await loadActivityData();
// 显示4周的日历
generateCalendar(activities, startDate, 4);
// 显示全年骑行时长和公里数
displayTotalActivities(activities);
})();
New:春节快乐红灯笼
两年前在冲浪时下载的,已经是第二次用了:
// default.html
include lantern.html
// main.scss
@use 'lantern'
Fix Bugs:移除 node-sass 包
node-sass 是基于 LibSass 库构建的,而 LibSass 从 2019 年就停止了更新。所以,Sass 团队放弃了这个项目,重构了 sass(Dart 编写)
sass 相对 node-sass 的优点
-
原生支持 Dart
sass 是由 Dart 编写,它不再依赖 C++ 编译器,安装和构建速度更快
-
不再依赖编译
node-sass 需要本地编译,会遇到编译问题,尤其是 Windows 系统上。而 sass 是纯 JavaScript 实现,跨平台时不会有编译问题
{
"devDependencies": {
"sass": "^1.83.4",
}
}