fixed 友圈掉帧问题以及评论区边框样式bug
This commit is contained in:
1 parent
c70835138f
commit
40e1e60bc5
2 files changed
+403
-362
No files matched your search
@@ -1,399 +1,438 @@
|
|||||||
{{ define "main" }}
|
{{ define "main" }}
|
||||||
<div id="pjax-container">
|
<div id="pjax-container">
|
||||||
<div class="post-content" itemprop="articleBody">
|
<div class="post-content" itemprop="articleBody">
|
||||||
{{ .Content }}
|
{{ .Content }}
|
||||||
<div id="friend-circle-container" class="friend-circle-list">
|
<div id="friend-circle-container" class="friend-circle-list">
|
||||||
<div class="loading-container">
|
<div class="loading-container">
|
||||||
<div class="loading-spinner"></div>
|
<div class="loading-spinner"></div>
|
||||||
<p>正在探索朋友们的新动态...</p>
|
<p>正在探索朋友们的新动态...</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<style>
|
|
||||||
/* Container Layout */
|
|
||||||
.friend-circle-list {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(2, 1fr);
|
|
||||||
gap: 12px; /* Tighter gap to make cards wider */
|
|
||||||
margin-top: 20px;
|
|
||||||
padding: 0; /* Remove padding to maximize width */
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mobile Layout */
|
<style>
|
||||||
@media (max-width: 768px) {
|
/* Container Layout */
|
||||||
.friend-circle-list {
|
.friend-circle-list {
|
||||||
grid-template-columns: 1fr;
|
display: grid;
|
||||||
gap: 10px;
|
grid-template-columns: repeat(2, 1fr);
|
||||||
}
|
gap: 12px; /* Tighter gap to make cards wider */
|
||||||
}
|
margin-top: 20px;
|
||||||
|
padding: 0; /* Remove padding to maximize width */
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
/* Card Style - Refined based on User Request */
|
/* Mobile Layout */
|
||||||
.fc-item {
|
@media (max-width: 768px) {
|
||||||
display: flex;
|
.friend-circle-list {
|
||||||
flex-direction: column;
|
grid-template-columns: 1fr;
|
||||||
padding: 20px;
|
gap: 10px;
|
||||||
border-radius: 10px;
|
}
|
||||||
position: relative;
|
}
|
||||||
height: 160px;
|
|
||||||
justify-content: space-between;
|
|
||||||
background: rgba(255, 255, 255, 0.4);
|
|
||||||
backdrop-filter: blur(16px) saturate(180%);
|
|
||||||
-webkit-backdrop-filter: blur(16px) saturate(180%);
|
|
||||||
border: 1px solid #111827;
|
|
||||||
box-shadow: none;
|
|
||||||
transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
|
|
||||||
overflow: hidden;
|
|
||||||
opacity: 0;
|
|
||||||
animation: ukFadeInUp 0.8s cubic-bezier(0.165, 0.84, 0.44, 1) forwards;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Light Mode Override - if theme is light, keep it readable but consistent layout */
|
/* Card Style - Refined based on User Request */
|
||||||
/* Assuming the user wants the dark look as shown in the reference, we prioritize that feel
|
.fc-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
position: relative;
|
||||||
|
height: 160px;
|
||||||
|
justify-content: space-between;
|
||||||
|
background: rgba(255, 255, 255, 0.4);
|
||||||
|
backdrop-filter: blur(16px) saturate(180%);
|
||||||
|
-webkit-backdrop-filter: blur(16px) saturate(180%);
|
||||||
|
border: 1px solid #111827;
|
||||||
|
box-shadow: none;
|
||||||
|
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;
|
||||||
|
opacity: 0;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Light Mode Override - if theme is light, keep it readable but consistent layout */
|
||||||
|
/* Assuming the user wants the dark look as shown in the reference, we prioritize that feel
|
||||||
or make it adapt. Let's make it adapt but keep the structure. */
|
or make it adapt. Let's make it adapt but keep the structure. */
|
||||||
:root {
|
:root {
|
||||||
/* --fc-bg: rgba(255, 255, 255, 0.8); */
|
/* --fc-bg: rgba(255, 255, 255, 0.8); */
|
||||||
--fc-text: #333;
|
--fc-text: #333;
|
||||||
--fc-meta-bg: rgba(0, 0, 0, 0.05);
|
--fc-meta-bg: rgba(0, 0, 0, 0.05);
|
||||||
/* --fc-border: rgba(0, 0, 0, 0.05); */
|
/* --fc-border: rgba(0, 0, 0, 0.05); */
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="dark"] {
|
|
||||||
/* --fc-bg: rgba(35, 35, 35, 0.8); */
|
|
||||||
--fc-text: #fff;
|
|
||||||
--fc-meta-bg: rgba(255, 255, 255, 0.1);
|
|
||||||
/* --fc-border: rgba(255, 255, 255, 0.1); */
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
[data-theme="dark"] {
|
||||||
|
/* --fc-bg: rgba(35, 35, 35, 0.8); */
|
||||||
|
--fc-text: #fff;
|
||||||
|
--fc-meta-bg: rgba(255, 255, 255, 0.1);
|
||||||
|
/* --fc-border: rgba(255, 255, 255, 0.1); */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
.fc-item {
|
.fc-item {
|
||||||
background: var(--fc-bg);
|
background: var(--fc-bg);
|
||||||
border-color: var(--fc-border);
|
border-color: var(--fc-border);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* UK Style Animation & Hover */
|
/* UK Style Animation & Hover */
|
||||||
.fc-item:hover {
|
/* 优化后的 hover 效果 */
|
||||||
transform: translateY(-8px) scale(1.02);
|
.fc-item:hover {
|
||||||
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.12);
|
/* 位移+缩放:微调数值,更克制不夸张 */
|
||||||
background: rgba(255, 255, 255, 0.6);
|
transform: translateY(-6px) scale(1.015);
|
||||||
border-color: #000;
|
/* 阴影:分层阴影,更有立体感(主阴影+柔和光晕) */
|
||||||
z-index: 10;
|
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;
|
||||||
|
/* 光标:交互提示 */
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes ukFadeInUp {
|
/* 可选:点击回弹效果,增强交互反馈 */
|
||||||
0% {
|
.fc-item:active {
|
||||||
opacity: 0;
|
transform: translateY(-3px) scale(1.005);
|
||||||
transform: translateY(40px) scale(0.95);
|
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.08);
|
||||||
}
|
transition: all 0.15s ease;
|
||||||
100% {
|
}
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0) scale(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Top: Title */
|
@keyframes ukFadeInUp {
|
||||||
.fc-title {
|
0% {
|
||||||
font-size: 18px;
|
opacity: 0;
|
||||||
font-weight: 700;
|
transform: translateY(40px) scale(0.95);
|
||||||
color: var(--fc-text);
|
}
|
||||||
text-decoration: none;
|
100% {
|
||||||
line-height: 1.4;
|
opacity: 1;
|
||||||
z-index: 2;
|
transform: translateY(0) scale(1);
|
||||||
display: -webkit-box;
|
}
|
||||||
-webkit-line-clamp: 2;
|
}
|
||||||
-webkit-box-orient: vertical;
|
|
||||||
overflow: hidden;
|
|
||||||
margin-right: 60px; /* Space for the image */
|
|
||||||
}
|
|
||||||
|
|
||||||
.fc-item:hover .fc-title {
|
|
||||||
color: #3498db;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Background Image (Decorative) */
|
/* Top: Title */
|
||||||
.fc-bg-image {
|
.fc-title {
|
||||||
position: absolute;
|
font-size: 18px;
|
||||||
right: -20px;
|
font-weight: 700;
|
||||||
bottom: -20px;
|
color: var(--fc-text);
|
||||||
width: 140px;
|
text-decoration: none;
|
||||||
height: 140px;
|
line-height: 1.4;
|
||||||
border-radius: 50%;
|
z-index: 2;
|
||||||
object-fit: cover;
|
display: -webkit-box;
|
||||||
opacity: 0.15;
|
-webkit-line-clamp: 2;
|
||||||
transition: all 0.5s ease;
|
-webkit-box-orient: vertical;
|
||||||
z-index: 0;
|
overflow: hidden;
|
||||||
pointer-events: none;
|
margin-right: 60px; /* Space for the image */
|
||||||
filter: grayscale(20%);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.fc-item:hover .fc-bg-image {
|
.fc-item:hover .fc-title {
|
||||||
opacity: 0.3;
|
color: #000000;
|
||||||
transform: scale(1.1) rotate(5deg);
|
}
|
||||||
filter: grayscale(0%);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Bottom Row: Author Pill & Date */
|
/* Background Image (Decorative) */
|
||||||
.fc-bottom {
|
.fc-bg-image {
|
||||||
display: flex;
|
position: absolute;
|
||||||
justify-content: space-between;
|
right: -20px;
|
||||||
align-items: flex-end;
|
bottom: -20px;
|
||||||
z-index: 2;
|
width: 140px;
|
||||||
}
|
height: 140px;
|
||||||
|
border-radius: 50%;
|
||||||
|
object-fit: cover;
|
||||||
|
opacity: 0.15;
|
||||||
|
transition: all 0.5s ease;
|
||||||
|
z-index: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
filter: grayscale(20%);
|
||||||
|
}
|
||||||
|
|
||||||
/* Author Pill */
|
.fc-item:hover .fc-bg-image {
|
||||||
.fc-author-pill {
|
opacity: 0.3;
|
||||||
display: flex;
|
transform: scale(1.1) rotate(deg);
|
||||||
align-items: center;
|
filter: grayscale(0%);
|
||||||
background: var(--fc-meta-bg);
|
}
|
||||||
padding: 4px 10px 4px 4px;
|
|
||||||
border-radius: 20px;
|
|
||||||
transition: background 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fc-item:hover .fc-author-pill {
|
|
||||||
background: rgba(52, 152, 219, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.fc-avatar {
|
/* Bottom Row: Author Pill & Date */
|
||||||
width: 28px;
|
.fc-bottom {
|
||||||
height: 28px;
|
display: flex;
|
||||||
border-radius: 50%;
|
justify-content: space-between;
|
||||||
object-fit: cover;
|
align-items: flex-end;
|
||||||
margin-right: 8px;
|
z-index: 2;
|
||||||
border: 1px solid rgba(255,255,255,0.2);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.fc-author-name {
|
/* Author Pill */
|
||||||
font-size: 13px;
|
.fc-author-pill {
|
||||||
font-weight: 600;
|
display: flex;
|
||||||
color: var(--fc-text);
|
align-items: center;
|
||||||
opacity: 0.9;
|
background: var(--fc-meta-bg);
|
||||||
}
|
padding: 4px 10px 4px 4px;
|
||||||
|
border-radius: 20px;
|
||||||
|
transition: background 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
/* Date */
|
.fc-item:hover .fc-author-pill {
|
||||||
.fc-date {
|
background: rgba(52, 152, 219, 0.1);
|
||||||
font-size: 12px;
|
}
|
||||||
color: var(--fc-text);
|
|
||||||
opacity: 0.6;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fc-date i {
|
|
||||||
margin-right: 4px;
|
|
||||||
font-size: 11px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FontAwesome fallback if not available, simple calendar icon via CSS or Unicode */
|
|
||||||
.fc-icon-calendar::before {
|
|
||||||
content: '\f133';
|
|
||||||
font-family: "Font Awesome 5 Free", "FontAwesome";
|
|
||||||
font-weight: 900;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Loading Animation */
|
.fc-avatar {
|
||||||
.loading-container {
|
width: 28px;
|
||||||
grid-column: 1 / -1;
|
height: 28px;
|
||||||
display: flex;
|
border-radius: 50%;
|
||||||
flex-direction: column;
|
object-fit: cover;
|
||||||
align-items: center;
|
margin-right: 8px;
|
||||||
padding: 60px;
|
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||||
color: #888;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.loading-spinner {
|
|
||||||
width: 40px;
|
|
||||||
height: 40px;
|
|
||||||
border: 3px solid rgba(127,127,127,0.2);
|
|
||||||
border-radius: 50%;
|
|
||||||
border-top-color: #3498db;
|
|
||||||
animation: spin 1s ease-in-out infinite;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dark Mode Specific Adjustments to match the reference exactly */
|
|
||||||
[data-theme="dark"] .fc-item {
|
|
||||||
background: rgba(35, 35, 35, 0.65); /* More transparent for glass effect */
|
|
||||||
box-shadow: none;
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="dark"] .fc-author-pill {
|
|
||||||
background: #1a1a1a;
|
|
||||||
border: 1px solid #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="dark"] .fc-bg-image {
|
.fc-author-name {
|
||||||
opacity: 0.2;
|
font-size: 13px;
|
||||||
}
|
font-weight: 600;
|
||||||
|
color: var(--fc-text);
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
|
||||||
/* Removed unused fadeInUp to keep clean, utilizing ukFadeInUp instead */
|
/* Date */
|
||||||
|
.fc-date {
|
||||||
@keyframes spin {
|
font-size: 12px;
|
||||||
to { transform: rotate(360deg); }
|
color: var(--fc-text);
|
||||||
}
|
opacity: 0.6;
|
||||||
/* Load More Button */
|
display: flex;
|
||||||
.fc-load-more-btn {
|
align-items: center;
|
||||||
grid-column: 1 / -1;
|
}
|
||||||
padding: 12px 30px;
|
|
||||||
margin: 30px auto 10px;
|
|
||||||
background: #000; /* Black background */
|
|
||||||
border: 1px solid rgba(255,255,255,0.1);
|
|
||||||
border-radius: 30px;
|
|
||||||
color: #fff;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 14px;
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
-webkit-backdrop-filter: blur(10px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.fc-load-more-btn:hover {
|
.fc-date i {
|
||||||
background: #333;
|
margin-right: 4px;
|
||||||
color: #fff;
|
font-size: 11px;
|
||||||
transform: translateY(-2px);
|
}
|
||||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-theme="dark"] .fc-load-more-btn {
|
/* FontAwesome fallback if not available, simple calendar icon via CSS or Unicode */
|
||||||
background: #000;
|
.fc-icon-calendar::before {
|
||||||
border-color: rgba(255, 255, 255, 0.2);
|
content: "\f133";
|
||||||
}
|
font-family: "Font Awesome 5 Free", "FontAwesome";
|
||||||
|
font-weight: 900;
|
||||||
[data-theme="dark"] .fc-load-more-btn:hover {
|
}
|
||||||
background: #222;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Artalk Dark Mode Adjustment */
|
/* Loading Animation */
|
||||||
[data-theme="dark"] .artalk .atk-main-editor {
|
.loading-container {
|
||||||
border: 1px solid rgba(255, 255, 255, 0.1) !important;
|
grid-column: 1 / -1;
|
||||||
}
|
display: flex;
|
||||||
</style>
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
padding: 60px;
|
||||||
|
color: #888;
|
||||||
|
}
|
||||||
|
|
||||||
<script>
|
.loading-spinner {
|
||||||
function timeAgo(dateStr) {
|
width: 40px;
|
||||||
const date = new Date(dateStr);
|
height: 40px;
|
||||||
const year = date.getFullYear();
|
border: 3px solid rgba(127, 127, 127, 0.2);
|
||||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
border-radius: 50%;
|
||||||
const day = String(date.getDate()).padStart(2, '0');
|
border-top-color: #3498db;
|
||||||
return `${year}-${month}-${day}`;
|
animation: spin 1s ease-in-out infinite;
|
||||||
}
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
let allPosts = [];
|
/* Dark Mode Specific Adjustments to match the reference exactly */
|
||||||
let currentIndex = 0;
|
[data-theme="dark"] .fc-item {
|
||||||
const BATCH_SIZE = 10;
|
background: rgba(
|
||||||
|
35,
|
||||||
|
35,
|
||||||
|
35,
|
||||||
|
0.65
|
||||||
|
); /* More transparent for glass effect */
|
||||||
|
box-shadow: none;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
function renderPosts(posts) {
|
[data-theme="dark"] .fc-author-pill {
|
||||||
const container = document.getElementById('friend-circle-container');
|
background: #1a1a1a;
|
||||||
// Check if button exists, if so, remove it temporarily to append items before it?
|
border: 1px solid #333;
|
||||||
// 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();
|
|
||||||
|
|
||||||
let html = '';
|
[data-theme="dark"] .fc-bg-image {
|
||||||
posts.forEach((post, index) => {
|
opacity: 0.2;
|
||||||
const dateStr = timeAgo(post.date);
|
}
|
||||||
// Stagger delay based on index in this batch
|
|
||||||
const delay = index * 0.05;
|
/* Removed unused fadeInUp to keep clean, utilizing ukFadeInUp instead */
|
||||||
|
|
||||||
const bgImageSrc = post.image || post.avatar;
|
@keyframes spin {
|
||||||
|
to {
|
||||||
html += `
|
transform: rotate(360deg);
|
||||||
<a href="${post.link}" target="_blank" class="fc-item" style="animation-delay: ${delay}s">
|
}
|
||||||
<div class="fc-title" title="${post.title}">${post.title}</div>
|
}
|
||||||
|
/* Load More Button */
|
||||||
<img src="${bgImageSrc}" class="fc-bg-image" loading="lazy" onerror="this.style.opacity=0">
|
.fc-load-more-btn {
|
||||||
|
grid-column: 1 / -1;
|
||||||
<div class="fc-bottom">
|
padding: 12px 30px;
|
||||||
<div class="fc-author-pill">
|
margin: 30px auto 10px;
|
||||||
<img src="${post.avatar}" class="fc-avatar" loading="lazy" onerror="this.src='https://cravatar.cn/avatar/default?s=50'">
|
background: #000; /* Black background */
|
||||||
<span class="fc-author-name">${post.author}</span>
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 30px;
|
||||||
|
color: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
-webkit-backdrop-filter: blur(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fc-load-more-btn:hover {
|
||||||
|
background: #333;
|
||||||
|
color: #fff;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .fc-load-more-btn {
|
||||||
|
background: #000;
|
||||||
|
border-color: rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="dark"] .fc-load-more-btn:hover {
|
||||||
|
background: #222;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Artalk Dark Mode Adjustment */
|
||||||
|
[data-theme="dark"] .artalk .atk-main-editor {
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.1) !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// 使用全局对象管理友圈逻辑
|
||||||
|
window.FriendCircle = {
|
||||||
|
allPosts: [],
|
||||||
|
currentIndex: 0,
|
||||||
|
BATCH_SIZE: 10,
|
||||||
|
|
||||||
|
timeAgo: function (dateStr) {
|
||||||
|
const date = new Date(dateStr);
|
||||||
|
const year = date.getFullYear();
|
||||||
|
const month = String(date.getMonth() + 1).padStart(2, "0");
|
||||||
|
const day = String(date.getDate()).padStart(2, "0");
|
||||||
|
return `${year}-${month}-${day}`;
|
||||||
|
},
|
||||||
|
|
||||||
|
renderPosts: function (posts) {
|
||||||
|
const container = document.getElementById("friend-circle-container");
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
const existingBtn = document.getElementById("fc-load-more-btn");
|
||||||
|
if (existingBtn) existingBtn.remove();
|
||||||
|
|
||||||
|
let html = "";
|
||||||
|
posts.forEach((post, index) => {
|
||||||
|
const dateStr = this.timeAgo(post.date);
|
||||||
|
// 减少延迟时间,避免动画积压
|
||||||
|
const delay = index * 0.05;
|
||||||
|
const bgImageSrc = post.image || post.avatar;
|
||||||
|
|
||||||
|
html += `
|
||||||
|
<a href="${post.link}" target="_blank" class="fc-item" style="animation-delay: ${delay}s">
|
||||||
|
<div class="fc-title" title="${post.title}">${post.title}</div>
|
||||||
|
<img src="${bgImageSrc}" class="fc-bg-image" loading="lazy" onerror="this.style.opacity=0">
|
||||||
|
<div class="fc-bottom">
|
||||||
|
<div class="fc-author-pill">
|
||||||
|
<img src="${post.avatar}" class="fc-avatar" loading="lazy" onerror="this.src='https://cravatar.cn/avatar/default?s=50'">
|
||||||
|
<span class="fc-author-name">${post.author}</span>
|
||||||
|
</div>
|
||||||
|
<div class="fc-date">
|
||||||
|
<i class="ri-calendar-line"></i> ${dateStr}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="fc-date">
|
</a>
|
||||||
<i class="ri-calendar-line"></i> ${dateStr}
|
`;
|
||||||
</div>
|
});
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
`;
|
|
||||||
});
|
|
||||||
|
|
||||||
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 {
|
if (window.fcUpdateTime) {
|
||||||
// Add update time footer if all loaded
|
const footer = document.createElement("div");
|
||||||
// We need to retrieve updateTime from somewhere, or just don't show it repeatedly.
|
footer.style.cssText =
|
||||||
// Let's store updateTime globally or just append it at the very end.
|
"grid-column: 1 / -1; text-align: center; margin-top: 20px; font-size: 12px; color: #999; opacity: 0.6;";
|
||||||
if (window.fcUpdateTime) {
|
footer.innerText = `最近更新: ${window.fcUpdateTime}`;
|
||||||
const footer = document.createElement('div');
|
container.appendChild(footer);
|
||||||
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}`;
|
}
|
||||||
container.appendChild(footer);
|
},
|
||||||
}
|
|
||||||
|
loadMore: function () {
|
||||||
|
const nextBatch = this.allPosts.slice(
|
||||||
|
this.currentIndex,
|
||||||
|
this.currentIndex + this.BATCH_SIZE,
|
||||||
|
);
|
||||||
|
this.currentIndex += this.BATCH_SIZE;
|
||||||
|
// 使用 requestAnimationFrame 优化渲染性能
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
this.renderPosts(nextBatch);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
init: function () {
|
||||||
|
const container = document.getElementById("friend-circle-container");
|
||||||
|
if (!container) return;
|
||||||
|
|
||||||
|
// 防止重复初始化导致掉帧
|
||||||
|
if (this.isFetching) return;
|
||||||
|
this.isFetching = true;
|
||||||
|
|
||||||
|
fetch("/json/friend_circle_data.json?t=" + new Date().getTime())
|
||||||
|
.then((res) => {
|
||||||
|
if (!res.ok) throw new Error("Network response was not ok");
|
||||||
|
return res.json();
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
if (!data.posts || data.posts.length === 0) {
|
||||||
|
container.innerHTML =
|
||||||
|
'<div class="empty" style="grid-column: 1/-1; text-align:center; padding:20px;">暂无动态</div>';
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function loadMore() {
|
this.allPosts = data.posts;
|
||||||
const nextBatch = allPosts.slice(currentIndex, currentIndex + BATCH_SIZE);
|
window.fcUpdateTime = new Date(data.updated).toLocaleString();
|
||||||
currentIndex += BATCH_SIZE;
|
this.currentIndex = 0;
|
||||||
renderPosts(nextBatch);
|
container.innerHTML = "";
|
||||||
}
|
this.loadMore();
|
||||||
|
})
|
||||||
|
.catch((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>';
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.isFetching = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
function loadFriendCircle() {
|
// 移除多余的 pjax:complete 监听,因为脚本在 PJAX 容器内,每次导航都会重新执行
|
||||||
const container = document.getElementById('friend-circle-container');
|
// 避免了重复触发 init 导致的性能问题
|
||||||
if (!container) return; // Prevent errors if container not found
|
|
||||||
|
|
||||||
// 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 (document.readyState === "loading") {
|
||||||
// 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.
|
document.addEventListener("DOMContentLoaded", () =>
|
||||||
|
window.FriendCircle.init(),
|
||||||
fetch('/json/friend_circle_data.json?t=' + new Date().getTime())
|
);
|
||||||
.then(res => {
|
} else {
|
||||||
if (!res.ok) throw new Error('Network response was not ok');
|
window.FriendCircle.init();
|
||||||
return res.json();
|
}
|
||||||
})
|
</script>
|
||||||
.then(data => {
|
|
||||||
if (!data.posts || data.posts.length === 0) {
|
|
||||||
container.innerHTML = '<div class="empty" style="grid-column: 1/-1; text-align:center; padding:20px;">暂无动态</div>';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
allPosts = data.posts;
|
|
||||||
window.fcUpdateTime = new Date(data.updated).toLocaleString();
|
|
||||||
currentIndex = 0;
|
|
||||||
|
|
||||||
// Clear loading spinner
|
|
||||||
container.innerHTML = '';
|
|
||||||
|
|
||||||
loadMore(); // Load first batch
|
|
||||||
})
|
|
||||||
.catch(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>';
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initial load
|
|
||||||
document.addEventListener('DOMContentLoaded', loadFriendCircle);
|
|
||||||
|
|
||||||
// PJAX support - listen for pjax:complete event
|
|
||||||
document.addEventListener('pjax:complete', loadFriendCircle);
|
|
||||||
|
|
||||||
if (document.readyState === 'interactive' || document.readyState === 'complete') {
|
|
||||||
loadFriendCircle();
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user