feat(about): 实现动态 GitHub 提交统计与页面样式优化
- 移除冗余的夜间主题 CSS 变量定义 - 新增 softPulse 动画用于按钮微光效果 - 优化主页标题动画与样式,调整颜色与阴影 - 删除重复的 hero-title 和 profile-avatar 样式定义 - 更新时间轴移动端布局逻辑并增强响应式表现 - 为社交卡片、博客、联系人等区块添加注释分隔 - 引入 Commit 统计功能,通过 GitHub API 获取真实数据 - 添加本地缓存机制以提升性能和减少请求频率 - 创建 fetchCommitCounts 函数计算周/月/年提交次数 - 构建 renderCommitStats 函数动态渲染提交统计数据 - 修复 CSS 文件末尾多余空行与格式问题
This commit is contained in:
171
css/about.css
171
css/about.css
@@ -41,14 +41,6 @@
|
||||
--glass-border: rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
|
||||
:root.theme-night {
|
||||
--grad-a: #101216;
|
||||
--grad-b: #171a1f;
|
||||
--text-strong: #F2F3F5;
|
||||
--text-soft: rgba(255, 255, 255, 0.9);
|
||||
--glass-alpha: 0.14;
|
||||
--glass-border: rgba(255, 255, 255, 0.16);
|
||||
}
|
||||
|
||||
.theme-day .nav-logo {
|
||||
color: var(--text-strong);
|
||||
@@ -701,6 +693,11 @@ a:not(.nav-logo):not(.nav-links a):not(.social-link):not(.btn):not(.footer-info
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes softPulse {
|
||||
0%, 100% { transform: scale(1); }
|
||||
50% { transform: scale(1.05); }
|
||||
}
|
||||
|
||||
.profile-info h1 {
|
||||
margin: 0 0 0.5rem 0;
|
||||
font-size: 2rem;
|
||||
@@ -798,31 +795,19 @@ a:not(.nav-logo):not(.nav-links a):not(.social-link):not(.btn):not(.footer-info
|
||||
color: var(--text-strong); /* 使用主题文字颜色 */
|
||||
}
|
||||
|
||||
.profile-avatar {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
font-size: 4rem;
|
||||
font-weight: 800;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 1rem;
|
||||
background: linear-gradient(45deg, #fff, #e0e0e0);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
animation: titleGlow 3s ease-in-out infinite;
|
||||
text-shadow: 0 0 30px rgba(255, 255, 255, 0.3);
|
||||
animation: titleGlow 3s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
.hero-subtitle {
|
||||
font-size: 1.4rem;
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
margin-bottom: 1.5rem;
|
||||
font-weight: 500;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.hero-description {
|
||||
margin-bottom: 2rem;
|
||||
@@ -840,10 +825,6 @@ a:not(.nav-logo):not(.nav-links a):not(.social-link):not(.btn):not(.footer-info
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.avatar-image:hover {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.avatar-ring {
|
||||
position: absolute;
|
||||
@@ -866,18 +847,6 @@ a:not(.nav-logo):not(.nav-links a):not(.social-link):not(.btn):not(.footer-info
|
||||
}
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
font-size: 4rem;
|
||||
font-weight: 800;
|
||||
color: #2c3e50;
|
||||
margin-bottom: 1rem;
|
||||
background: linear-gradient(45deg, #fff, #e0e0e0);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
animation: titleGlow 3s ease-in-out infinite alternate;
|
||||
}
|
||||
|
||||
@keyframes titleGlow {
|
||||
from {
|
||||
filter: drop-shadow(0 0 20px rgba(255, 255, 255, 0.5));
|
||||
@@ -911,7 +880,7 @@ a:not(.nav-logo):not(.nav-links a):not(.social-link):not(.btn):not(.footer-info
|
||||
font-size: 1rem;
|
||||
font-weight: 700;
|
||||
box-shadow: 0 8px 25px rgba(255, 107, 107, 0.4);
|
||||
animation: pulse 2s infinite;
|
||||
animation: softPulse 2s infinite;
|
||||
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||
}
|
||||
|
||||
@@ -972,44 +941,7 @@ a:not(.nav-logo):not(.nav-links a):not(.social-link):not(.btn):not(.footer-info
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
/* 技术栈云图 - INFJ风格设计 */
|
||||
.tech-cloud-section {
|
||||
padding: 8rem 2rem;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
backdrop-filter: blur(10px);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
border-radius: 50px;
|
||||
margin: 2rem;
|
||||
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tech-cloud-section::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: radial-gradient(circle at 20% 80%, rgba(120, 119, 198, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 80% 20%, rgba(255, 119, 198, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 40% 40%, rgba(120, 219, 255, 0.1) 0%, transparent 50%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@keyframes gradientShift {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
}
|
||||
|
||||
/* 标签浮动动画 - 仅在非球体模式下使用 */
|
||||
@keyframes cloudFloat {
|
||||
@@ -1137,11 +1069,6 @@ a:not(.nav-logo):not(.nav-links a):not(.social-link):not(.btn):not(.footer-info
|
||||
}
|
||||
}
|
||||
|
||||
.cloud-tag {
|
||||
flex-shrink: 0;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
|
||||
/* 标签云标签 */
|
||||
.cloud-tag {
|
||||
@@ -1382,39 +1309,10 @@ a:not(.nav-logo):not(.nav-links a):not(.social-link):not(.btn):not(.footer-info
|
||||
.timeline-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 4rem;
|
||||
margin-bottom: 3rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 在移动端保持单列布局 */
|
||||
@media (max-width: 768px) {
|
||||
.personality-timeline::before {
|
||||
left: 30px;
|
||||
}
|
||||
|
||||
.timeline-item {
|
||||
flex-direction: row !important;
|
||||
padding-left: 80px;
|
||||
}
|
||||
|
||||
.timeline-icon {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.timeline-content {
|
||||
margin: 0;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.timeline-item:nth-child(even) {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.timeline-icon {
|
||||
width: 90px;
|
||||
height: 90px;
|
||||
@@ -1477,7 +1375,40 @@ a:not(.nav-logo):not(.nav-links a):not(.social-link):not(.btn):not(.footer-info
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* PC端保持左右交错布局 */
|
||||
@media (min-width: 769px) {
|
||||
.timeline-item:nth-child(even) {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
}
|
||||
|
||||
/* 在移动端保持单列布局 */
|
||||
@media (max-width: 768px) {
|
||||
.personality-timeline::before {
|
||||
left: 30px;
|
||||
}
|
||||
|
||||
.timeline-item {
|
||||
flex-direction: row !important;
|
||||
padding-left: 80px;
|
||||
}
|
||||
|
||||
.timeline-icon {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.timeline-content {
|
||||
margin: 0;
|
||||
margin-left: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* GitHub项目展示 */
|
||||
|
||||
.github-showcase-section {
|
||||
padding: 8rem 2rem;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
@@ -1581,6 +1512,7 @@ a:not(.nav-logo):not(.nav-links a):not(.social-link):not(.btn):not(.footer-info
|
||||
}
|
||||
|
||||
/* 博客瀑布流 */
|
||||
|
||||
.blog-waterfall-section {
|
||||
padding: 8rem 2rem;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
@@ -1680,6 +1612,7 @@ a:not(.nav-logo):not(.nav-links a):not(.social-link):not(.btn):not(.footer-info
|
||||
}
|
||||
|
||||
/* 联系方式轨道 - 循环随机围绕动画 */
|
||||
|
||||
.contact-floating-section {
|
||||
padding: 8rem 2rem;
|
||||
position: relative;
|
||||
@@ -1702,8 +1635,8 @@ a:not(.nav-logo):not(.nav-links a):not(.social-link):not(.btn):not(.footer-info
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: radial-gradient(circle at 20% 30%, rgba(102, 126, 234, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 80% 70%, rgba(118, 75, 162, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 40% 80%, rgba(155, 89, 182, 0.1) 0%, transparent 50%);
|
||||
radial-gradient(circle at 80% 70%, rgba(118, 75, 162, 0.1) 0%, transparent 50%),
|
||||
radial-gradient(circle at 40% 80%, rgba(155, 89, 182, 0.1) 0%, transparent 50%);
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
border-radius: 50px;
|
||||
@@ -1746,6 +1679,7 @@ a:not(.nav-logo):not(.nav-links a):not(.social-link):not(.btn):not(.footer-info
|
||||
}
|
||||
|
||||
/* 社交卡片 */
|
||||
|
||||
.social-card {
|
||||
background: rgba(255, 255, 255, var(--glass-alpha));
|
||||
backdrop-filter: blur(var(--glass-blur));
|
||||
@@ -1835,6 +1769,7 @@ a:not(.nav-logo):not(.nav-links a):not(.social-link):not(.btn):not(.footer-info
|
||||
}
|
||||
|
||||
/* 轨道围绕动画 - 每个卡片不同的轨道路径,优化避免碰撞 */
|
||||
|
||||
.social-card:nth-child(1) {
|
||||
animation: orbit1 20s linear infinite;
|
||||
}
|
||||
@@ -1945,6 +1880,7 @@ a:not(.nav-logo):not(.nav-links a):not(.social-link):not(.btn):not(.footer-info
|
||||
}
|
||||
|
||||
/* 微信弹窗样式 */
|
||||
|
||||
.modal {
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
@@ -2000,6 +1936,7 @@ a:not(.nav-logo):not(.nav-links a):not(.social-link):not(.btn):not(.footer-info
|
||||
}
|
||||
|
||||
/* 评论系统 */
|
||||
|
||||
.comments-section {
|
||||
padding: 8rem 2rem;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
@@ -2037,6 +1974,7 @@ a:not(.nav-logo):not(.nav-links a):not(.social-link):not(.btn):not(.footer-info
|
||||
}
|
||||
|
||||
/* 加载动画 */
|
||||
|
||||
.loading-placeholder {
|
||||
text-align: center;
|
||||
padding: 4rem 2rem;
|
||||
@@ -2223,7 +2161,7 @@ a:not(.nav-logo):not(.nav-links a):not(.social-link):not(.btn):not(.footer-info
|
||||
@keyframes orbit6 {
|
||||
}
|
||||
|
||||
/* 移动端英雄区域重构 */
|
||||
|
||||
.hero-container {
|
||||
grid-template-columns: 1fr;
|
||||
text-align: center;
|
||||
@@ -2439,6 +2377,7 @@ a:not(.nav-logo):not(.nav-links a):not(.social-link):not(.btn):not(.footer-info
|
||||
}
|
||||
|
||||
/* 页脚样式 - 首页风格 */
|
||||
|
||||
.footer {
|
||||
background: rgba(0, 0, 0, 0.2);
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
@@ -2593,4 +2532,6 @@ a:not(.nav-logo):not(.nav-links a):not(.social-link):not(.btn):not(.footer-info
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
}
|
||||
|
||||
/* 文件末尾 */
|
||||
|
||||
117
js/about.js
117
js/about.js
@@ -155,35 +155,7 @@ function displayGitHubProfile(data) {
|
||||
$('#github-profile').html(profileHtml);
|
||||
$('#github-repos').text(data.public_repos);
|
||||
$('#github-followers').text(data.followers);
|
||||
|
||||
// 获取提交统计(使用GitHub API的限制,这里模拟数据)
|
||||
var commitsHtml = '<div class="commits-stats">' +
|
||||
'<h3>提交统计</h3>' +
|
||||
'<div class="commits-chart">' +
|
||||
'<div class="commit-item">' +
|
||||
'<span class="commit-date">本周</span>' +
|
||||
'<div class="commit-bar">' +
|
||||
'<div class="commit-fill" style="width: 80%"></div>' +
|
||||
'</div>' +
|
||||
'<span class="commit-count">24</span>' +
|
||||
'</div>' +
|
||||
'<div class="commit-item">' +
|
||||
'<span class="commit-date">本月</span>' +
|
||||
'<div class="commit-bar">' +
|
||||
'<div class="commit-fill" style="width: 65%"></div>' +
|
||||
'</div>' +
|
||||
'<span class="commit-count">156</span>' +
|
||||
'</div>' +
|
||||
'<div class="commit-item">' +
|
||||
'<span class="commit-date">今年</span>' +
|
||||
'<div class="commit-bar">' +
|
||||
'<div class="commit-fill" style="width: 90%"></div>' +
|
||||
'</div>' +
|
||||
'<span class="commit-count">1,247</span>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
$('#github-commits').html(commitsHtml);
|
||||
initCommitStats(data.login || 'listener-He');
|
||||
}
|
||||
|
||||
// 优质项目展示
|
||||
@@ -1045,3 +1017,90 @@ function updateTechTagColors() {
|
||||
}
|
||||
});
|
||||
}
|
||||
function initCommitStats(username) {
|
||||
var cacheKey = 'github_commits_cache';
|
||||
var cacheTimeKey = 'github_commits_cache_time';
|
||||
var now = new Date().getTime();
|
||||
var oneDay = 24 * 60 * 60 * 1000;
|
||||
var cached = localStorage.getItem(cacheKey);
|
||||
var cachedTime = localStorage.getItem(cacheTimeKey);
|
||||
if (cached && cachedTime && (now - parseInt(cachedTime)) < oneDay) {
|
||||
renderCommitStats(JSON.parse(cached));
|
||||
return;
|
||||
}
|
||||
localStorage.removeItem(cacheKey);
|
||||
localStorage.removeItem(cacheTimeKey);
|
||||
fetchCommitCounts(username).then(function(stats){
|
||||
localStorage.setItem(cacheKey, JSON.stringify(stats));
|
||||
localStorage.setItem(cacheTimeKey, now.toString());
|
||||
renderCommitStats(stats);
|
||||
}).catch(function(){
|
||||
fetch('data/github_commits.json')
|
||||
.then(function(res){ return res.json(); })
|
||||
.then(function(json){ renderCommitStats(json); })
|
||||
.catch(function(){ renderCommitStats({week:0,month:0,year:0}); });
|
||||
});
|
||||
}
|
||||
|
||||
function fetchCommitCounts(username) {
|
||||
function fmt(d){
|
||||
var y=d.getFullYear();
|
||||
var m=('0'+(d.getMonth()+1)).slice(-2);
|
||||
var s=('0'+d.getDate()).slice(-2);
|
||||
return y+'-'+m+'-'+s;
|
||||
}
|
||||
var today = new Date();
|
||||
var weekStart = new Date(today.getFullYear(), today.getMonth(), today.getDate()-6);
|
||||
var monthStart = new Date(today.getFullYear(), today.getMonth(), today.getDate()-29);
|
||||
var yearStart = new Date(today.getFullYear(), 0, 1);
|
||||
var h = { 'Accept': 'application/vnd.github.cloak-preview+json' };
|
||||
function q(start,end){
|
||||
var url = 'https://api.github.com/search/commits?q=author:'+encodeURIComponent(username)+'+author-date:'+fmt(start)+'..'+fmt(end);
|
||||
return fetch(url,{ headers: h, method:'GET' }).then(function(r){ return r.json(); }).then(function(j){ return (j && typeof j.total_count==='number')? j.total_count : 0; });
|
||||
}
|
||||
return Promise.all([
|
||||
q(weekStart,today),
|
||||
q(monthStart,today),
|
||||
q(yearStart,today)
|
||||
]).then(function(arr){
|
||||
return { week: arr[0], month: arr[1], year: arr[2], range: { week:{start:fmt(weekStart),end:fmt(today)}, month:{start:fmt(monthStart),end:fmt(today)}, year:{start:fmt(yearStart),end:fmt(today)} }, generated_at: new Date().toISOString() };
|
||||
});
|
||||
}
|
||||
|
||||
function renderCommitStats(stats) {
|
||||
var w = parseInt(stats.week||0,10);
|
||||
var m = parseInt(stats.month||0,10);
|
||||
var y = parseInt(stats.year||0,10);
|
||||
function pct(v){
|
||||
var p = 0;
|
||||
if (y>0) p = Math.min(100, Math.max(5, Math.round(v*100/Math.max(y,1))));
|
||||
return p;
|
||||
}
|
||||
var commitsHtml = '<div class="commits-stats">'+
|
||||
'<h3>提交统计</h3>'+
|
||||
'<div class="commits-chart">'+
|
||||
'<div class="commit-item">'+
|
||||
'<span class="commit-date">本周</span>'+
|
||||
'<div class="commit-bar">'+
|
||||
'<div class="commit-fill" style="width: '+pct(w)+'%"></div>'+
|
||||
'</div>'+
|
||||
'<span class="commit-count">'+w+'</span>'+
|
||||
'</div>'+
|
||||
'<div class="commit-item">'+
|
||||
'<span class="commit-date">本月</span>'+
|
||||
'<div class="commit-bar">'+
|
||||
'<div class="commit-fill" style="width: '+pct(m)+'%"></div>'+
|
||||
'</div>'+
|
||||
'<span class="commit-count">'+m+'</span>'+
|
||||
'</div>'+
|
||||
'<div class="commit-item">'+
|
||||
'<span class="commit-date">今年</span>'+
|
||||
'<div class="commit-bar">'+
|
||||
'<div class="commit-fill" style="width: 100%"></div>'+
|
||||
'</div>'+
|
||||
'<span class="commit-count">'+y+'</span>'+
|
||||
'</div>'+
|
||||
'</div>'+
|
||||
'</div>';
|
||||
$('#github-commits').html(commitsHtml);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user