fixed 友圈掉帧问题以及评论区边框样式bug

This commit is contained in:
Vaica committed 2026-01-22 12:54:52 +08:00
1 parent c70835138f
commit 40e1e60bc5
2 files changed
+124 -83

No files matched your search

+118 -79
View File
@@ -43,10 +43,20 @@
-webkit-backdrop-filter: blur(16px) saturate(180%); -webkit-backdrop-filter: blur(16px) saturate(180%);
border: 1px solid #111827; border: 1px solid #111827;
box-shadow: none; box-shadow: none;
transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); transition:
transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1),
box-shadow 0.3s ease,
background-color 0.3s ease,
border-color 0.3s ease;
will-change: transform;
overflow: hidden; overflow: hidden;
opacity: 0; opacity: 0;
animation: ukFadeInUp 0.8s cubic-bezier(0.165, 0.84, 0.44, 1) forwards; animation: ukFadeInUp 0.6s cubic-bezier(0.165, 0.84, 0.44, 1) forwards;
text-decoration: none;
transform: translateZ(0); /* Enable GPU acceleration */
}
.friend-circle-list a {
text-decoration: none; text-decoration: none;
} }
@@ -75,12 +85,29 @@
*/ */
/* UK Style Animation & Hover */ /* UK Style Animation & Hover */
/* 优化后的 hover 效果 */
.fc-item:hover { .fc-item:hover {
transform: translateY(-8px) scale(1.02); /* 位移+缩放:微调数值,更克制不夸张 */
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.12); transform: translateY(-6px) scale(1.015);
background: rgba(255, 255, 255, 0.6); /* 阴影:分层阴影,更有立体感(主阴影+柔和光晕) */
border-color: #000; box-shadow:
0 12px 28px rgba(0, 0, 0, 0.1),
/* 主阴影:缩小范围,降低不透明度 */ 0 2px 8px rgba(0, 0, 0, 0.06); /* 次级光晕:增加柔和感 */
/* 背景:轻微提亮,保留通透感 */
background: rgba(255, 255, 255, 0.7);
/* 边框:轻微加亮,避免纯黑太突兀 */
border-color: #1a1a1a;
/* 层级:保持原有值 */
z-index: 10; z-index: 10;
/* 光标:交互提示 */
cursor: pointer;
}
/* 可选:点击回弹效果,增强交互反馈 */
.fc-item:active {
transform: translateY(-3px) scale(1.005);
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.08);
transition: all 0.15s ease;
} }
@keyframes ukFadeInUp { @keyframes ukFadeInUp {
@@ -110,7 +137,7 @@
} }
.fc-item:hover .fc-title { .fc-item:hover .fc-title {
color: #3498db; color: #000000;
} }
/* Background Image (Decorative) */ /* Background Image (Decorative) */
@@ -131,7 +158,7 @@
.fc-item:hover .fc-bg-image { .fc-item:hover .fc-bg-image {
opacity: 0.3; opacity: 0.3;
transform: scale(1.1) rotate(5deg); transform: scale(1.1) rotate(deg);
filter: grayscale(0%); filter: grayscale(0%);
} }
@@ -189,7 +216,7 @@
/* FontAwesome fallback if not available, simple calendar icon via CSS or Unicode */ /* FontAwesome fallback if not available, simple calendar icon via CSS or Unicode */
.fc-icon-calendar::before { .fc-icon-calendar::before {
content: '\f133'; content: "\f133";
font-family: "Font Awesome 5 Free", "FontAwesome"; font-family: "Font Awesome 5 Free", "FontAwesome";
font-weight: 900; font-weight: 900;
} }
@@ -216,7 +243,12 @@
/* Dark Mode Specific Adjustments to match the reference exactly */ /* Dark Mode Specific Adjustments to match the reference exactly */
[data-theme="dark"] .fc-item { [data-theme="dark"] .fc-item {
background: rgba(35, 35, 35, 0.65); /* More transparent for glass effect */ background: rgba(
35,
35,
35,
0.65
); /* More transparent for glass effect */
box-shadow: none; box-shadow: none;
border: 1px solid rgba(255, 255, 255, 0.1); border: 1px solid rgba(255, 255, 255, 0.1);
} }
@@ -233,7 +265,9 @@
/* Removed unused fadeInUp to keep clean, utilizing ukFadeInUp instead */ /* Removed unused fadeInUp to keep clean, utilizing ukFadeInUp instead */
@keyframes spin { @keyframes spin {
to { transform: rotate(360deg); } to {
transform: rotate(360deg);
}
} }
/* Load More Button */ /* Load More Button */
.fc-load-more-btn { .fc-load-more-btn {
@@ -274,41 +308,38 @@
</style> </style>
<script> <script>
function timeAgo(dateStr) { // 使用全局对象管理友圈逻辑
window.FriendCircle = {
allPosts: [],
currentIndex: 0,
BATCH_SIZE: 10,
timeAgo: function (dateStr) {
const date = new Date(dateStr); const date = new Date(dateStr);
const year = date.getFullYear(); const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0'); const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, '0'); const day = String(date.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`; return `${year}-${month}-${day}`;
} },
let allPosts = []; renderPosts: function (posts) {
let currentIndex = 0; const container = document.getElementById("friend-circle-container");
const BATCH_SIZE = 10; if (!container) return;
function renderPosts(posts) { const existingBtn = document.getElementById("fc-load-more-btn");
const container = document.getElementById('friend-circle-container');
// Check if button exists, if so, remove it temporarily to append items before it?
// Or just insertAdjacentHTML 'beforeend' and then append button.
// Remove existing button if any
const existingBtn = document.getElementById('fc-load-more-btn');
if (existingBtn) existingBtn.remove(); if (existingBtn) existingBtn.remove();
let html = ''; let html = "";
posts.forEach((post, index) => { posts.forEach((post, index) => {
const dateStr = timeAgo(post.date); const dateStr = this.timeAgo(post.date);
// Stagger delay based on index in this batch // 减少延迟时间,避免动画积压
const delay = index * 0.05; const delay = index * 0.05;
const bgImageSrc = post.image || post.avatar; const bgImageSrc = post.image || post.avatar;
html += ` html += `
<a href="${post.link}" target="_blank" class="fc-item" style="animation-delay: ${delay}s"> <a href="${post.link}" target="_blank" class="fc-item" style="animation-delay: ${delay}s">
<div class="fc-title" title="${post.title}">${post.title}</div> <div class="fc-title" title="${post.title}">${post.title}</div>
<img src="${bgImageSrc}" class="fc-bg-image" loading="lazy" onerror="this.style.opacity=0"> <img src="${bgImageSrc}" class="fc-bg-image" loading="lazy" onerror="this.style.opacity=0">
<div class="fc-bottom"> <div class="fc-bottom">
<div class="fc-author-pill"> <div class="fc-author-pill">
<img src="${post.avatar}" class="fc-avatar" loading="lazy" onerror="this.src='https://cravatar.cn/avatar/default?s=50'"> <img src="${post.avatar}" class="fc-avatar" loading="lazy" onerror="this.src='https://cravatar.cn/avatar/default?s=50'">
@@ -322,77 +353,85 @@
`; `;
}); });
container.insertAdjacentHTML('beforeend', html); container.insertAdjacentHTML("beforeend", html);
// Check if we need a load more button if (this.currentIndex < this.allPosts.length) {
if (currentIndex < allPosts.length) { const btn = document.createElement("button");
const btn = document.createElement('button'); btn.id = "fc-load-more-btn";
btn.id = 'fc-load-more-btn'; btn.className = "fc-load-more-btn";
btn.className = 'fc-load-more-btn'; btn.innerText = "加载更多";
btn.innerText = '加载更多'; btn.onclick = () => this.loadMore();
btn.onclick = loadMore;
container.appendChild(btn); container.appendChild(btn);
} else { } else {
// Add update time footer if all loaded
// We need to retrieve updateTime from somewhere, or just don't show it repeatedly.
// Let's store updateTime globally or just append it at the very end.
if (window.fcUpdateTime) { if (window.fcUpdateTime) {
const footer = document.createElement('div'); const footer = document.createElement("div");
footer.style.cssText = "grid-column: 1 / -1; text-align: center; margin-top: 20px; font-size: 12px; color: #999; opacity: 0.6;"; footer.style.cssText =
"grid-column: 1 / -1; text-align: center; margin-top: 20px; font-size: 12px; color: #999; opacity: 0.6;";
footer.innerText = `最近更新: ${window.fcUpdateTime}`; footer.innerText = `最近更新: ${window.fcUpdateTime}`;
container.appendChild(footer); container.appendChild(footer);
} }
} }
} },
function loadMore() { loadMore: function () {
const nextBatch = allPosts.slice(currentIndex, currentIndex + BATCH_SIZE); const nextBatch = this.allPosts.slice(
currentIndex += BATCH_SIZE; this.currentIndex,
renderPosts(nextBatch); this.currentIndex + this.BATCH_SIZE,
} );
this.currentIndex += this.BATCH_SIZE;
// 使用 requestAnimationFrame 优化渲染性能
requestAnimationFrame(() => {
this.renderPosts(nextBatch);
});
},
function loadFriendCircle() { init: function () {
const container = document.getElementById('friend-circle-container'); const container = document.getElementById("friend-circle-container");
if (!container) return; // Prevent errors if container not found if (!container) return;
// Reset state if needed, though loadMore will handle rendering // 防止重复初始化导致掉帧
// But we need to make sure we don't re-fetch if we don't have to? if (this.isFetching) return;
// Actually PJAX reload means we are on a "new" page state, so re-fetching is safer to ensure data is fresh and DOM is rebuilt. this.isFetching = true;
fetch('/json/friend_circle_data.json?t=' + new Date().getTime()) fetch("/json/friend_circle_data.json?t=" + new Date().getTime())
.then(res => { .then((res) => {
if (!res.ok) throw new Error('Network response was not ok'); if (!res.ok) throw new Error("Network response was not ok");
return res.json(); return res.json();
}) })
.then(data => { .then((data) => {
if (!data.posts || data.posts.length === 0) { if (!data.posts || data.posts.length === 0) {
container.innerHTML = '<div class="empty" style="grid-column: 1/-1; text-align:center; padding:20px;">暂无动态</div>'; container.innerHTML =
'<div class="empty" style="grid-column: 1/-1; text-align:center; padding:20px;">暂无动态</div>';
return; return;
} }
allPosts = data.posts; this.allPosts = data.posts;
window.fcUpdateTime = new Date(data.updated).toLocaleString(); window.fcUpdateTime = new Date(data.updated).toLocaleString();
currentIndex = 0; this.currentIndex = 0;
container.innerHTML = "";
// Clear loading spinner this.loadMore();
container.innerHTML = '';
loadMore(); // Load first batch
}) })
.catch(err => { .catch((err) => {
console.error('Friend Circle Error:', err); console.error("Friend Circle Error:", err);
container.innerHTML = '<div class="error" style="grid-column: 1/-1; text-align:center; padding:20px; color:#d9534f;">加载失败,请稍后刷新重试</div>'; container.innerHTML =
'<div class="error" style="grid-column: 1/-1; text-align:center; padding:20px; color:#d9534f;">加载失败,请稍后刷新重试</div>';
})
.finally(() => {
this.isFetching = false;
}); });
} },
};
// Initial load // 移除多余的 pjax:complete 监听,因为脚本在 PJAX 容器内,每次导航都会重新执行
document.addEventListener('DOMContentLoaded', loadFriendCircle); // 避免了重复触发 init 导致的性能问题
// PJAX support - listen for pjax:complete event // 立即执行初始化
document.addEventListener('pjax:complete', loadFriendCircle); if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", () =>
if (document.readyState === 'interactive' || document.readyState === 'complete') { window.FriendCircle.init(),
loadFriendCircle(); );
} else {
window.FriendCircle.init();
} }
</script> </script>
</div> </div>
+6 -4
View File
@@ -2304,15 +2304,17 @@ input[type=submit] {
stroke: var(--text-highlight-color); stroke: var(--text-highlight-color);
} }
/* 通用样式 */
.artalk .atk-main-editor input.atk-nick, .artalk .atk-main-editor input.atk-nick,
.artalk .atk-main-editor input.atk-email, .artalk .atk-main-editor input.atk-email,
.artalk .atk-main-editor input.atk-link { .artalk .atk-main-editor input.atk-link {
color: var(--text-content-color)!important; color: var(--text-content-color)!important;
} }
[data-theme="dark"] .artalk .atk-main-editor input.atk-nick, /* 暗黑模式样式:重复类选择器,最大化权重 */
[data-theme="dark"] .artalk .atk-main-editor input.atk-email, [data-theme="dark"] .artalk.artalk .atk-main-editor input.atk-nick,
[data-theme="dark"] .artalk .atk-main-editor input.atk-link { [data-theme="dark"] .artalk.artalk .atk-main-editor input.atk-email,
[data-theme="dark"] .artalk.artalk .atk-main-editor input.atk-link {
color: var(--text-content-dark-color)!important; color: var(--text-content-dark-color)!important;
} }
@@ -2429,7 +2431,7 @@ img[atk-emoticon] {
color: var(--text-content-dark-color) color: var(--text-content-dark-color)
} }
.atk-main-editor { .artalk .atk-main-editor {
background: 0 0!important; background: 0 0!important;
border: 1px solid var(--frame-dark-color)!important border: 1px solid var(--frame-dark-color)!important
} }