diff --git a/hugo.toml b/hugo.toml
index e4fb3cc3..db63b4a2 100644
--- a/hugo.toml
+++ b/hugo.toml
@@ -140,6 +140,12 @@ hasCJKLanguage = true
# 一言弹幕头像,留空则默认使用博客头像
hitokotoAvatar = ""
+ # ----------------------------------------------------------------------------
+ # 无限加载配置 (Infinite Scroll Configuration)
+ # ----------------------------------------------------------------------------
+ [params.infiniteScroll]
+ enable = false
+
# ----------------------------------------------------------------------------
# AI 评论配置 (AI Comment Configuration)
# ----------------------------------------------------------------------------
diff --git a/themes/Ying/assets/css/main.css b/themes/Ying/assets/css/main.css
index fed7a591..24a6ac6d 100644
--- a/themes/Ying/assets/css/main.css
+++ b/themes/Ying/assets/css/main.css
@@ -747,71 +747,73 @@ ul {
display: flex;
align-items: center;
width: 100%;
- border: 1px solid rgba(0, 0, 0, 0.08); /* Very subtle border for minimalism */
- border-radius: 99px; /* Fully rounded pill */
- padding: 2px 15px;
- background: transparent; /* No background by default */
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1); /* Minimalist bottom border only */
+ border-radius: 0;
+ padding: 2px 0;
+ background: transparent;
+ transition: all 0.3s ease;
}
.search-input-group:hover {
- border-color: rgba(0, 0, 0, 0.15);
- background: rgba(0, 0, 0, 0.01);
+ border-bottom-color: rgba(0, 0, 0, 0.3);
+ background: transparent;
}
.search-input-group:focus-within {
- background: #fff;
+ background: transparent;
border-color: transparent;
- box-shadow: 0 8px 30px rgba(0, 0, 0, 0.08); /* Soft diffuse shadow */
- transform: translateY(-1px);
+ border-bottom-color: var(--theme-color, #4a90e2);
+ box-shadow: none;
+ transform: none;
}
[data-theme="dark"] .search-input-group {
- border-color: rgba(255, 255, 255, 0.1);
+ border-bottom-color: rgba(255, 255, 255, 0.1);
background: transparent;
}
[data-theme="dark"] .search-input-group:hover {
- border-color: rgba(255, 255, 255, 0.2);
- background: rgba(255, 255, 255, 0.02);
+ border-bottom-color: rgba(255, 255, 255, 0.3);
+ background: transparent;
}
[data-theme="dark"] .search-input-group:focus-within {
- background: #1f2933;
+ background: transparent;
border-color: transparent;
- box-shadow: 0 8px 30px rgba(0, 0, 0, 0.4);
+ border-bottom-color: var(--theme-color, #4a90e2);
+ box-shadow: none;
}
.search-input-group input {
width: 100%;
border: none;
background: transparent;
- padding: 10px 10px 10px 30px;
+ padding: 5px 30px 5px 0; /* Left align text, space for close icon on right */
font-size: 0.95rem;
color: var(--text-title-color);
outline: none;
}
+/* Search Icon visible */
.search-icon {
- position: absolute;
- left: 15px;
- font-size: 1.1rem;
color: #9ca3af;
- pointer-events: none;
- transition: color 0.3s;
+ font-size: 1.1rem;
+ margin-right: 6px;
+ transition: color 0.3s ease;
}
.search-input-group:focus-within .search-icon {
- color: #4b5563;
+ color: var(--text-title-color);
}
[data-theme="dark"] .search-input-group:focus-within .search-icon {
- color: #d1d5db;
+ color: var(--text-title-dark-color);
}
+/* Close icon on the right */
.close-icon {
position: absolute;
- right: 15px;
+ right: 0;
font-size: 1.1rem;
color: #9ca3af;
cursor: pointer;
@@ -826,7 +828,7 @@ ul {
}
.close-icon:hover {
- color: #ef4444;
+ color: var(--theme-color, #4a90e2);
}
.search-dropdown {
@@ -834,19 +836,18 @@ ul {
top: 100%;
left: 0;
right: 0;
- background: rgba(255, 255, 255, 0.9);
- backdrop-filter: blur(12px);
- -webkit-backdrop-filter: blur(12px);
- border-radius: 12px;
- box-shadow: 0 10px 40px -10px rgba(0, 0, 0, 0.1);
- border: 1px solid rgba(255, 255, 255, 0.2);
- margin-top: 12px;
+ background: rgba(255, 255, 255, 0.98); /* Less transparent for readability */
+ /* backdrop-filter: blur(12px); Removed for cleaner look */
+ border-radius: 4px; /* Sharper corners */
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05); /* Softer shadow */
+ border: 1px solid rgba(0, 0, 0, 0.05);
+ margin-top: 5px;
overflow: hidden;
z-index: 50;
opacity: 0;
- transform: translateY(10px);
+ transform: translateY(5px);
visibility: hidden;
- transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+ transition: all 0.2s ease;
}
.search-dropdown.show {
@@ -856,58 +857,61 @@ ul {
}
[data-theme="dark"] .search-dropdown {
- background: rgba(31, 41, 51, 0.9);
+ background: #1f2933;
border: 1px solid rgba(255, 255, 255, 0.05);
- box-shadow: 0 10px 40px -10px rgba(0, 0, 0, 0.5);
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
}
.search-result-item {
- padding: 10px 15px;
- border-bottom: 1px solid rgba(0,0,0,0.03);
+ padding: 8px 10px;
+ border-bottom: none; /* Clean list */
display: flex;
flex-direction: column;
+ transition: background-color 0.2s;
}
.search-result-meta {
display: flex;
- justify-content: space-between;
- font-size: 0.8rem;
+ justify-content: flex-start; /* Left align */
+ gap: 10px;
+ font-size: 0.75rem;
color: #999;
- margin-top: 4px;
+ margin-top: 2px;
}
.search-result-summary-text {
- max-width: 70%;
+ max-width: 80%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.no-result {
- text-align: center;
+ text-align: left;
color: #999;
- padding: 15px;
+ padding: 10px;
+ font-size: 0.9rem;
cursor: default;
}
[data-theme="dark"] .search-result-item {
- border-bottom: 1px solid rgba(255,255,255,0.05);
+ border-bottom: none;
}
.search-result-item:hover {
- background: rgba(0,0,0,0.02);
+ background: rgba(0,0,0,0.03);
}
[data-theme="dark"] .search-result-item:hover {
- background: rgba(255,255,255,0.02);
+ background: rgba(255,255,255,0.05);
}
.search-result-title {
display: block;
- font-weight: 600;
- font-size: 0.95rem;
+ font-weight: normal; /* Lighter weight */
+ font-size: 0.9rem;
color: var(--text-title-color);
- margin-bottom: 4px;
+ margin-bottom: 0;
}
[data-theme="dark"] .search-result-title {
@@ -3112,6 +3116,9 @@ input[type=submit] {
flex-direction: column;
gap: 10px;
align-items: center;
+ /* GPU Acceleration for container */
+ transform: translateZ(0);
+ will-change: transform;
}
.floating-tools a {
@@ -3135,6 +3142,9 @@ input[type=submit] {
backdrop-filter: blur(10px) saturate(180%);
-webkit-backdrop-filter: blur(10px) saturate(180%);
border: 1px solid rgba(255, 255, 255, 0.4);
+ /* GPU Acceleration for individual items */
+ transform: translate3d(0, 0, 0);
+ will-change: transform, opacity;
}
.floating-tools a:hover {
@@ -3165,10 +3175,12 @@ input[type=submit] {
margin: 0;
padding: 0;
overflow: hidden;
- transform: scale(0.8) translateY(10px);
+ /* Use translate3d for GPU acceleration instead of scale/translateY combo which might cause repaint */
+ transform: translate3d(0, 10px, 0) scale(0.8);
pointer-events: none;
border-width: 0;
transition: opacity 0.3s ease, transform 0.3s ease, visibility 0.3s, height 0.3s ease, border-width 0.3s ease;
+ /* Explicitly tell browser to promote this layer */
will-change: opacity, transform, height;
}
@@ -3178,13 +3190,13 @@ input[type=submit] {
height: 40px;
width: 40px;
margin-top: 0;
- transform: scale(1) translateY(0);
+ transform: translate3d(0, 0, 0) scale(1);
pointer-events: auto;
border-width: 1px;
}
#back-to-top.show:hover {
- transform: scale(1) translateY(-3px);
+ transform: translate3d(0, -3px, 0) scale(1);
}
/* GenericStrong */ [data-theme="dark"] .chroma .gs { }
/* GenericSubheading */ [data-theme="dark"] .chroma .gu { font-weight:bold }
@@ -4195,7 +4207,7 @@ html[data-theme="light"] .atk-loading-spinner svg circle {
border: none;
background: transparent;
flex: 1;
- padding: 5px 10px;
+ padding: 5px 5px; /* Reduced side padding */
font-size: 14px;
outline: none;
color: #333;
@@ -4203,7 +4215,8 @@ html[data-theme="light"] .atk-loading-spinner svg circle {
.search-input-group .search-icon {
color: #999;
- font-size: 16px;
+ font-size: 18px; /* Slightly larger */
+ margin-right: 4px;
}
.search-input-group .close-icon {
diff --git a/themes/Ying/assets/js/barrage.js b/themes/Ying/assets/js/barrage.js
index f6173c1b..ad7518bc 100644
--- a/themes/Ying/assets/js/barrage.js
+++ b/themes/Ying/assets/js/barrage.js
@@ -6,6 +6,7 @@
function getArtalkComments() {
return new Promise((resolve) => {
let isResolved = false;
+ let interval = null;
// 安全获取评论列表的辅助函数
const safeGetComments = () => {
@@ -22,66 +23,58 @@
const tryResolve = (data, source) => {
if (isResolved) return;
- // 调试日志
- if (data && Array.isArray(data)) {
- console.log(`[Barrage] 数据检查 (${source}): ${data.length} 条`);
- } else {
- console.log(`[Barrage] 数据检查 (${source}): 无效数据`, data);
- }
-
- if (Array.isArray(data) && data.length > 0) {
- isResolved = true;
- resolve(data);
+ if (Array.isArray(data)) {
+ // 如果是事件触发(说明已加载完成),或者是轮询且有数据,则认为成功
+ if (source === 'event' || data.length > 0) {
+ console.log(`[Barrage] 数据获取成功 (${source}): ${data.length} 条`);
+ isResolved = true;
+ if (interval) clearInterval(interval);
+ resolve(data);
+ }
}
};
- // 1. 立即检查
- const immediateData = safeGetComments();
- if (immediateData) {
- tryResolve(immediateData, 'immediate');
- } else {
- console.log('[Barrage] Artalk 实例未就绪 (Immediate)');
- }
+ // 检查 Artalk 实例并尝试获取数据
+ const checkArtalk = () => {
+ if (isResolved) return;
- if (isResolved) return;
+ if (window.artalk) {
+ // 1. 尝试绑定事件 (只绑定一次)
+ if (!window.artalk._barrageListenerAttached && typeof window.artalk.on === 'function') {
+ window.artalk.on('list-loaded', (data) => {
+ tryResolve(data, 'event');
+ });
+ window.artalk._barrageListenerAttached = true;
+ console.log('[Barrage] Artalk 事件监听已绑定');
+ }
- // 2. 监听加载完成事件 (推荐)
- if (window.artalk && typeof window.artalk.on === 'function') {
- window.artalk.on('list-loaded', (data) => {
- console.log('[Barrage] list-loaded 事件触发');
- // 优先使用事件传递的数据(如果有),否则尝试主动获取
- const comments = (Array.isArray(data) && data.length > 0) ? data : (safeGetComments() || []);
- tryResolve(comments, 'event');
- });
- }
-
- // 3. 轮询 (后备)
- let pollCount = 0;
- const interval = setInterval(() => {
- if (isResolved) {
- clearInterval(interval);
- return;
+ // 2. 尝试直接获取数据
+ const data = safeGetComments();
+ if (data) {
+ tryResolve(data, 'poll');
+ }
}
- pollCount++;
-
- const pollData = safeGetComments();
- if (pollData && Array.isArray(pollData) && pollData.length > 0) {
- tryResolve(pollData, `poll-${pollCount}`);
- }
- }, 500);
+ };
- // 4. 超时处理 (5秒)
+ // 立即检查
+ checkArtalk();
+
+ // 轮询检查 (直到获取数据或超时)
+ // 增加轮询频率,以便更快响应 Artalk 初始化
+ interval = setInterval(checkArtalk, 200);
+
+ // 超时处理 (8秒)
setTimeout(() => {
if (!isResolved) {
- clearInterval(interval);
- console.warn('[Barrage] 等待评论数据超时 (5s)');
+ if (interval) clearInterval(interval);
+ console.warn('[Barrage] 获取数据超时');
- // 尝试从 DOM 获取作为最后兜底
+ // 最后的尝试:DOM 兜底 (适用于静态页面或 API 失败但 DOM 已渲染的情况)
const domComments = document.querySelectorAll('.atk-comment-wrap');
if (domComments.length > 0) {
console.log(`[Barrage] 从 DOM 挽救数据: ${domComments.length} 条`);
const fallbackData = Array.from(domComments).map(el => {
- const idStr = el.id || ''; // atk-comment-123
+ const idStr = el.id || '';
const id = idStr.replace('atk-comment-', '');
const contentEl = el.querySelector('.atk-content');
const avatarEl = el.querySelector('.atk-avatar img');
@@ -92,21 +85,19 @@
id: id,
content: contentEl.innerText || contentEl.textContent,
avatar: avatarEl ? avatarEl.src : '',
- // 模拟 Artalk 数据结构
ua: null
};
}).filter(Boolean);
if (fallbackData.length > 0) {
- isResolved = true;
resolve(fallbackData);
return;
}
}
- resolve([]); // 真的没有数据
+ resolve([]);
}
- }, 5000);
+ }, 8000);
});
}
@@ -200,7 +191,10 @@
this.stop();
this.clearDOM();
this.isDestroyed = false;
+ this.updateData(data);
+ }
+ updateData(data = []) {
// 数据预处理:去重 & ID 规范化
const uniqueData = [];
const seenIds = new Set();
@@ -214,11 +208,33 @@
});
this.data = uniqueData;
- console.log(`[Barrage] 启动: 原始 ${data.length}, 去重后 ${this.data.length}`);
+ // 如果数据更新时列表变短了,重置索引以防越界
+ if (this.index >= this.data.length) {
+ this.index = 0;
+ }
- if (this.data.length === 0) return;
+ console.log(`[Barrage] 数据更新: ${this.data.length} 条`);
- this.loop();
+ if (this.data.length > 0 && !this.timer && !this.isPaused) {
+ this.loop();
+ }
+ }
+
+ hide() {
+ if (this.container) {
+ this.container.style.opacity = '0';
+ this.container.style.pointerEvents = 'none';
+ }
+ this.isPaused = true;
+ }
+
+ show() {
+ if (this.container) {
+ this.container.style.opacity = '1';
+ this.container.style.pointerEvents = 'auto';
+ }
+ this.isPaused = false;
+ if (!this.timer) this.loop();
}
stop() {
@@ -226,24 +242,24 @@
clearTimeout(this.timer);
this.timer = null;
}
- this.activeIds.clear();
+ // 注意:不清除 activeIds,允许已发射的弹幕飞完
}
clearDOM() {
if (this.container) {
this.container.innerHTML = '';
}
+ this.activeIds.clear();
}
destroy() {
this.isDestroyed = true;
this.stop();
this.clearDOM();
- // 移除事件监听需要保存引用,这里因为容器可能被销毁,且是单例,暂不处理复杂的解绑
}
loop() {
- if (this.isDestroyed) return;
+ if (this.isDestroyed || this.isPaused) return;
// 容器检测
if (!document.body.contains(this.container)) {
@@ -251,7 +267,7 @@
return;
}
- if (!this.isPaused && this.data.length > 0) {
+ if (this.data.length > 0) {
// 只有当前屏幕数量未满时才发射
if (this.activeIds.size < this.maxActiveItems) {
this.tryShoot();
@@ -264,6 +280,8 @@
}
tryShoot() {
+ if (this.data.length === 0) return;
+
let attempts = 0;
const maxAttempts = this.data.length;
@@ -294,8 +312,9 @@
let html = '';
// 优化:只有当有头像时才渲染 img 标签
+ // 移除 loading="lazy" 以修复弹幕头像加载问题
if (avatar) {
- html += ``;
+ html += `
`;
}
html += `