fixed 弹幕修复 bug
This commit is contained in:
1 parent
a4c74cbb0d
commit
0714b8eb48
11 files changed
+446
-139
No files matched your search
@@ -140,6 +140,12 @@ hasCJKLanguage = true
|
|||||||
# 一言弹幕头像,留空则默认使用博客头像
|
# 一言弹幕头像,留空则默认使用博客头像
|
||||||
hitokotoAvatar = ""
|
hitokotoAvatar = ""
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# 无限加载配置 (Infinite Scroll Configuration)
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
[params.infiniteScroll]
|
||||||
|
enable = false
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
# AI 评论配置 (AI Comment Configuration)
|
# AI 评论配置 (AI Comment Configuration)
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -747,71 +747,73 @@ ul {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border: 1px solid rgba(0, 0, 0, 0.08); /* Very subtle border for minimalism */
|
border-bottom: 1px solid rgba(0, 0, 0, 0.1); /* Minimalist bottom border only */
|
||||||
border-radius: 99px; /* Fully rounded pill */
|
border-radius: 0;
|
||||||
padding: 2px 15px;
|
padding: 2px 0;
|
||||||
background: transparent; /* No background by default */
|
background: transparent;
|
||||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
transition: all 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-input-group:hover {
|
.search-input-group:hover {
|
||||||
border-color: rgba(0, 0, 0, 0.15);
|
border-bottom-color: rgba(0, 0, 0, 0.3);
|
||||||
background: rgba(0, 0, 0, 0.01);
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-input-group:focus-within {
|
.search-input-group:focus-within {
|
||||||
background: #fff;
|
background: transparent;
|
||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.08); /* Soft diffuse shadow */
|
border-bottom-color: var(--theme-color, #4a90e2);
|
||||||
transform: translateY(-1px);
|
box-shadow: none;
|
||||||
|
transform: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="dark"] .search-input-group {
|
[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;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="dark"] .search-input-group:hover {
|
[data-theme="dark"] .search-input-group:hover {
|
||||||
border-color: rgba(255, 255, 255, 0.2);
|
border-bottom-color: rgba(255, 255, 255, 0.3);
|
||||||
background: rgba(255, 255, 255, 0.02);
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="dark"] .search-input-group:focus-within {
|
[data-theme="dark"] .search-input-group:focus-within {
|
||||||
background: #1f2933;
|
background: transparent;
|
||||||
border-color: 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 {
|
.search-input-group input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border: none;
|
border: none;
|
||||||
background: transparent;
|
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;
|
font-size: 0.95rem;
|
||||||
color: var(--text-title-color);
|
color: var(--text-title-color);
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Search Icon visible */
|
||||||
.search-icon {
|
.search-icon {
|
||||||
position: absolute;
|
|
||||||
left: 15px;
|
|
||||||
font-size: 1.1rem;
|
|
||||||
color: #9ca3af;
|
color: #9ca3af;
|
||||||
pointer-events: none;
|
font-size: 1.1rem;
|
||||||
transition: color 0.3s;
|
margin-right: 6px;
|
||||||
|
transition: color 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-input-group:focus-within .search-icon {
|
.search-input-group:focus-within .search-icon {
|
||||||
color: #4b5563;
|
color: var(--text-title-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="dark"] .search-input-group:focus-within .search-icon {
|
[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 {
|
.close-icon {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 15px;
|
right: 0;
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
color: #9ca3af;
|
color: #9ca3af;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
@@ -826,7 +828,7 @@ ul {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.close-icon:hover {
|
.close-icon:hover {
|
||||||
color: #ef4444;
|
color: var(--theme-color, #4a90e2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-dropdown {
|
.search-dropdown {
|
||||||
@@ -834,19 +836,18 @@ ul {
|
|||||||
top: 100%;
|
top: 100%;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
background: rgba(255, 255, 255, 0.9);
|
background: rgba(255, 255, 255, 0.98); /* Less transparent for readability */
|
||||||
backdrop-filter: blur(12px);
|
/* backdrop-filter: blur(12px); Removed for cleaner look */
|
||||||
-webkit-backdrop-filter: blur(12px);
|
border-radius: 4px; /* Sharper corners */
|
||||||
border-radius: 12px;
|
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05); /* Softer shadow */
|
||||||
box-shadow: 0 10px 40px -10px rgba(0, 0, 0, 0.1);
|
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
margin-top: 5px;
|
||||||
margin-top: 12px;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
z-index: 50;
|
z-index: 50;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(10px);
|
transform: translateY(5px);
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
transition: all 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-dropdown.show {
|
.search-dropdown.show {
|
||||||
@@ -856,58 +857,61 @@ ul {
|
|||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="dark"] .search-dropdown {
|
[data-theme="dark"] .search-dropdown {
|
||||||
background: rgba(31, 41, 51, 0.9);
|
background: #1f2933;
|
||||||
border: 1px solid rgba(255, 255, 255, 0.05);
|
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 {
|
.search-result-item {
|
||||||
padding: 10px 15px;
|
padding: 8px 10px;
|
||||||
border-bottom: 1px solid rgba(0,0,0,0.03);
|
border-bottom: none; /* Clean list */
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
transition: background-color 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-result-meta {
|
.search-result-meta {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: flex-start; /* Left align */
|
||||||
font-size: 0.8rem;
|
gap: 10px;
|
||||||
|
font-size: 0.75rem;
|
||||||
color: #999;
|
color: #999;
|
||||||
margin-top: 4px;
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-result-summary-text {
|
.search-result-summary-text {
|
||||||
max-width: 70%;
|
max-width: 80%;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-result {
|
.no-result {
|
||||||
text-align: center;
|
text-align: left;
|
||||||
color: #999;
|
color: #999;
|
||||||
padding: 15px;
|
padding: 10px;
|
||||||
|
font-size: 0.9rem;
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="dark"] .search-result-item {
|
[data-theme="dark"] .search-result-item {
|
||||||
border-bottom: 1px solid rgba(255,255,255,0.05);
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-result-item:hover {
|
.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 {
|
[data-theme="dark"] .search-result-item:hover {
|
||||||
background: rgba(255,255,255,0.02);
|
background: rgba(255,255,255,0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-result-title {
|
.search-result-title {
|
||||||
display: block;
|
display: block;
|
||||||
font-weight: 600;
|
font-weight: normal; /* Lighter weight */
|
||||||
font-size: 0.95rem;
|
font-size: 0.9rem;
|
||||||
color: var(--text-title-color);
|
color: var(--text-title-color);
|
||||||
margin-bottom: 4px;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
[data-theme="dark"] .search-result-title {
|
[data-theme="dark"] .search-result-title {
|
||||||
@@ -3112,6 +3116,9 @@ input[type=submit] {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
/* GPU Acceleration for container */
|
||||||
|
transform: translateZ(0);
|
||||||
|
will-change: transform;
|
||||||
}
|
}
|
||||||
|
|
||||||
.floating-tools a {
|
.floating-tools a {
|
||||||
@@ -3135,6 +3142,9 @@ input[type=submit] {
|
|||||||
backdrop-filter: blur(10px) saturate(180%);
|
backdrop-filter: blur(10px) saturate(180%);
|
||||||
-webkit-backdrop-filter: blur(10px) saturate(180%);
|
-webkit-backdrop-filter: blur(10px) saturate(180%);
|
||||||
border: 1px solid rgba(255, 255, 255, 0.4);
|
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 {
|
.floating-tools a:hover {
|
||||||
@@ -3165,10 +3175,12 @@ input[type=submit] {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
overflow: hidden;
|
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;
|
pointer-events: none;
|
||||||
border-width: 0;
|
border-width: 0;
|
||||||
transition: opacity 0.3s ease, transform 0.3s ease, visibility 0.3s, height 0.3s ease, border-width 0.3s ease;
|
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;
|
will-change: opacity, transform, height;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3178,13 +3190,13 @@ input[type=submit] {
|
|||||||
height: 40px;
|
height: 40px;
|
||||||
width: 40px;
|
width: 40px;
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
transform: scale(1) translateY(0);
|
transform: translate3d(0, 0, 0) scale(1);
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
border-width: 1px;
|
border-width: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#back-to-top.show:hover {
|
#back-to-top.show:hover {
|
||||||
transform: scale(1) translateY(-3px);
|
transform: translate3d(0, -3px, 0) scale(1);
|
||||||
}
|
}
|
||||||
/* GenericStrong */ [data-theme="dark"] .chroma .gs { }
|
/* GenericStrong */ [data-theme="dark"] .chroma .gs { }
|
||||||
/* GenericSubheading */ [data-theme="dark"] .chroma .gu { font-weight:bold }
|
/* GenericSubheading */ [data-theme="dark"] .chroma .gu { font-weight:bold }
|
||||||
@@ -4195,7 +4207,7 @@ html[data-theme="light"] .atk-loading-spinner svg circle {
|
|||||||
border: none;
|
border: none;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding: 5px 10px;
|
padding: 5px 5px; /* Reduced side padding */
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
outline: none;
|
outline: none;
|
||||||
color: #333;
|
color: #333;
|
||||||
@@ -4203,7 +4215,8 @@ html[data-theme="light"] .atk-loading-spinner svg circle {
|
|||||||
|
|
||||||
.search-input-group .search-icon {
|
.search-input-group .search-icon {
|
||||||
color: #999;
|
color: #999;
|
||||||
font-size: 16px;
|
font-size: 18px; /* Slightly larger */
|
||||||
|
margin-right: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-input-group .close-icon {
|
.search-input-group .close-icon {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
function getArtalkComments() {
|
function getArtalkComments() {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
let isResolved = false;
|
let isResolved = false;
|
||||||
|
let interval = null;
|
||||||
|
|
||||||
// 安全获取评论列表的辅助函数
|
// 安全获取评论列表的辅助函数
|
||||||
const safeGetComments = () => {
|
const safeGetComments = () => {
|
||||||
@@ -22,66 +23,58 @@
|
|||||||
const tryResolve = (data, source) => {
|
const tryResolve = (data, source) => {
|
||||||
if (isResolved) return;
|
if (isResolved) return;
|
||||||
|
|
||||||
// 调试日志
|
if (Array.isArray(data)) {
|
||||||
if (data && Array.isArray(data)) {
|
// 如果是事件触发(说明已加载完成),或者是轮询且有数据,则认为成功
|
||||||
console.log(`[Barrage] 数据检查 (${source}): ${data.length} 条`);
|
if (source === 'event' || data.length > 0) {
|
||||||
} else {
|
console.log(`[Barrage] 数据获取成功 (${source}): ${data.length} 条`);
|
||||||
console.log(`[Barrage] 数据检查 (${source}): 无效数据`, data);
|
isResolved = true;
|
||||||
}
|
if (interval) clearInterval(interval);
|
||||||
|
resolve(data);
|
||||||
if (Array.isArray(data) && data.length > 0) {
|
}
|
||||||
isResolved = true;
|
|
||||||
resolve(data);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// 1. 立即检查
|
// 检查 Artalk 实例并尝试获取数据
|
||||||
const immediateData = safeGetComments();
|
const checkArtalk = () => {
|
||||||
if (immediateData) {
|
if (isResolved) return;
|
||||||
tryResolve(immediateData, 'immediate');
|
|
||||||
} else {
|
|
||||||
console.log('[Barrage] Artalk 实例未就绪 (Immediate)');
|
|
||||||
}
|
|
||||||
|
|
||||||
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. 监听加载完成事件 (推荐)
|
// 2. 尝试直接获取数据
|
||||||
if (window.artalk && typeof window.artalk.on === 'function') {
|
const data = safeGetComments();
|
||||||
window.artalk.on('list-loaded', (data) => {
|
if (data) {
|
||||||
console.log('[Barrage] list-loaded 事件触发');
|
tryResolve(data, 'poll');
|
||||||
// 优先使用事件传递的数据(如果有),否则尝试主动获取
|
}
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
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(() => {
|
setTimeout(() => {
|
||||||
if (!isResolved) {
|
if (!isResolved) {
|
||||||
clearInterval(interval);
|
if (interval) clearInterval(interval);
|
||||||
console.warn('[Barrage] 等待评论数据超时 (5s)');
|
console.warn('[Barrage] 获取数据超时');
|
||||||
|
|
||||||
// 尝试从 DOM 获取作为最后兜底
|
// 最后的尝试:DOM 兜底 (适用于静态页面或 API 失败但 DOM 已渲染的情况)
|
||||||
const domComments = document.querySelectorAll('.atk-comment-wrap');
|
const domComments = document.querySelectorAll('.atk-comment-wrap');
|
||||||
if (domComments.length > 0) {
|
if (domComments.length > 0) {
|
||||||
console.log(`[Barrage] 从 DOM 挽救数据: ${domComments.length} 条`);
|
console.log(`[Barrage] 从 DOM 挽救数据: ${domComments.length} 条`);
|
||||||
const fallbackData = Array.from(domComments).map(el => {
|
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 id = idStr.replace('atk-comment-', '');
|
||||||
const contentEl = el.querySelector('.atk-content');
|
const contentEl = el.querySelector('.atk-content');
|
||||||
const avatarEl = el.querySelector('.atk-avatar img');
|
const avatarEl = el.querySelector('.atk-avatar img');
|
||||||
@@ -92,21 +85,19 @@
|
|||||||
id: id,
|
id: id,
|
||||||
content: contentEl.innerText || contentEl.textContent,
|
content: contentEl.innerText || contentEl.textContent,
|
||||||
avatar: avatarEl ? avatarEl.src : '',
|
avatar: avatarEl ? avatarEl.src : '',
|
||||||
// 模拟 Artalk 数据结构
|
|
||||||
ua: null
|
ua: null
|
||||||
};
|
};
|
||||||
}).filter(Boolean);
|
}).filter(Boolean);
|
||||||
|
|
||||||
if (fallbackData.length > 0) {
|
if (fallbackData.length > 0) {
|
||||||
isResolved = true;
|
|
||||||
resolve(fallbackData);
|
resolve(fallbackData);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve([]); // 真的没有数据
|
resolve([]);
|
||||||
}
|
}
|
||||||
}, 5000);
|
}, 8000);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,7 +191,10 @@
|
|||||||
this.stop();
|
this.stop();
|
||||||
this.clearDOM();
|
this.clearDOM();
|
||||||
this.isDestroyed = false;
|
this.isDestroyed = false;
|
||||||
|
this.updateData(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateData(data = []) {
|
||||||
// 数据预处理:去重 & ID 规范化
|
// 数据预处理:去重 & ID 规范化
|
||||||
const uniqueData = [];
|
const uniqueData = [];
|
||||||
const seenIds = new Set();
|
const seenIds = new Set();
|
||||||
@@ -214,11 +208,33 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.data = uniqueData;
|
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() {
|
stop() {
|
||||||
@@ -226,24 +242,24 @@
|
|||||||
clearTimeout(this.timer);
|
clearTimeout(this.timer);
|
||||||
this.timer = null;
|
this.timer = null;
|
||||||
}
|
}
|
||||||
this.activeIds.clear();
|
// 注意:不清除 activeIds,允许已发射的弹幕飞完
|
||||||
}
|
}
|
||||||
|
|
||||||
clearDOM() {
|
clearDOM() {
|
||||||
if (this.container) {
|
if (this.container) {
|
||||||
this.container.innerHTML = '';
|
this.container.innerHTML = '';
|
||||||
}
|
}
|
||||||
|
this.activeIds.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
this.isDestroyed = true;
|
this.isDestroyed = true;
|
||||||
this.stop();
|
this.stop();
|
||||||
this.clearDOM();
|
this.clearDOM();
|
||||||
// 移除事件监听需要保存引用,这里因为容器可能被销毁,且是单例,暂不处理复杂的解绑
|
|
||||||
}
|
}
|
||||||
|
|
||||||
loop() {
|
loop() {
|
||||||
if (this.isDestroyed) return;
|
if (this.isDestroyed || this.isPaused) return;
|
||||||
|
|
||||||
// 容器检测
|
// 容器检测
|
||||||
if (!document.body.contains(this.container)) {
|
if (!document.body.contains(this.container)) {
|
||||||
@@ -251,7 +267,7 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.isPaused && this.data.length > 0) {
|
if (this.data.length > 0) {
|
||||||
// 只有当前屏幕数量未满时才发射
|
// 只有当前屏幕数量未满时才发射
|
||||||
if (this.activeIds.size < this.maxActiveItems) {
|
if (this.activeIds.size < this.maxActiveItems) {
|
||||||
this.tryShoot();
|
this.tryShoot();
|
||||||
@@ -264,6 +280,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
tryShoot() {
|
tryShoot() {
|
||||||
|
if (this.data.length === 0) return;
|
||||||
|
|
||||||
let attempts = 0;
|
let attempts = 0;
|
||||||
const maxAttempts = this.data.length;
|
const maxAttempts = this.data.length;
|
||||||
|
|
||||||
@@ -294,8 +312,9 @@
|
|||||||
|
|
||||||
let html = '';
|
let html = '';
|
||||||
// 优化:只有当有头像时才渲染 img 标签
|
// 优化:只有当有头像时才渲染 img 标签
|
||||||
|
// 移除 loading="lazy" 以修复弹幕头像加载问题
|
||||||
if (avatar) {
|
if (avatar) {
|
||||||
html += `<img src="${avatar}" class="barrage-avatar" draggable="false" loading="lazy">`;
|
html += `<img src="${avatar}" class="barrage-avatar" draggable="false" onerror="this.src='https://cravatar.cn/avatar/default?d=mp'">`;
|
||||||
}
|
}
|
||||||
html += `<div class="barrage-bubble"><span class="barrage-text">${text}</span></div>`;
|
html += `<div class="barrage-bubble"><span class="barrage-text">${text}</span></div>`;
|
||||||
item.innerHTML = html;
|
item.innerHTML = html;
|
||||||
@@ -334,6 +353,19 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检测是否存在评论区
|
||||||
|
const hasCommentSection = document.getElementById('artalk') ||
|
||||||
|
document.getElementById('Comments') ||
|
||||||
|
document.querySelector('.atk-comment-wrap');
|
||||||
|
|
||||||
|
if (!hasCommentSection) {
|
||||||
|
// console.log('[Barrage] 当前页面无评论区,隐藏弹幕');
|
||||||
|
if (barrageInstance) {
|
||||||
|
barrageInstance.hide();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 1. 获取容器,如果没有则动态创建
|
// 1. 获取容器,如果没有则动态创建
|
||||||
let container = document.querySelector('.barrager');
|
let container = document.querySelector('.barrager');
|
||||||
if (!container) {
|
if (!container) {
|
||||||
@@ -343,10 +375,9 @@
|
|||||||
document.body.appendChild(container);
|
document.body.appendChild(container);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 清理旧实例
|
// 确保显示(无论是否有评论区,都允许弹幕存在,可能显示的是上一页的数据或 Hitokoto)
|
||||||
if (barrageInstance) {
|
if (barrageInstance) {
|
||||||
barrageInstance.destroy();
|
barrageInstance.show();
|
||||||
barrageInstance = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -371,13 +402,27 @@
|
|||||||
if (!text.trim()) return null;
|
if (!text.trim()) return null;
|
||||||
|
|
||||||
let avatar = '';
|
let avatar = '';
|
||||||
const domComment = document.getElementById(`atk-comment-${c.id}`);
|
|
||||||
if (domComment) {
|
// 1. 优先尝试使用 email_encrypted 构造 Gravatar (适用于跨页面无 DOM 的情况)
|
||||||
const img = domComment.querySelector('.atk-avatar img');
|
if (c.email_encrypted) {
|
||||||
if (img && img.src) avatar = img.src;
|
avatar = `https://cravatar.cn/avatar/${c.email_encrypted}?d=mp&s=240`;
|
||||||
|
} else if (c.email_hash) {
|
||||||
|
avatar = `https://cravatar.cn/avatar/${c.email_hash}?d=mp&s=240`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 2. 如果没有 hash,尝试从 API 对象直接获取
|
||||||
|
if (!avatar && c.avatar) {
|
||||||
|
avatar = c.avatar;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 尝试从 UA 获取
|
||||||
|
if (!avatar && c.ua && c.ua.avatar) {
|
||||||
|
avatar = c.ua.avatar;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 兜底默认头像
|
||||||
if (!avatar) {
|
if (!avatar) {
|
||||||
avatar = c.avatar || (c.ua && c.ua.avatar) || 'https://cravatar.cn/avatar/default?d=mp';
|
avatar = 'https://cravatar.cn/avatar/default?d=mp';
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -403,17 +448,18 @@
|
|||||||
|
|
||||||
if (barrageList.length === 0) {
|
if (barrageList.length === 0) {
|
||||||
console.log('[Barrage] 无弹幕数据');
|
console.log('[Barrage] 无弹幕数据');
|
||||||
|
// 即使无数据,也不要销毁实例,保持空转即可
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 再次检查实例 (竞态保护)
|
// 更新实例或创建新实例
|
||||||
if (barrageInstance) {
|
if (barrageInstance) {
|
||||||
barrageInstance.destroy();
|
barrageInstance.updateData(barrageList);
|
||||||
|
} else {
|
||||||
|
console.log(`[Barrage] 初始化: ${barrageList.length} 条`);
|
||||||
|
barrageInstance = new BarrageEngine({ container });
|
||||||
|
barrageInstance.start(barrageList);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`[Barrage] 最终加载: ${barrageList.length} 条`);
|
|
||||||
barrageInstance = new BarrageEngine({ container });
|
|
||||||
barrageInstance.start(barrageList);
|
|
||||||
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('[Barrage] 加载异常:', e);
|
console.warn('[Barrage] 加载异常:', e);
|
||||||
@@ -427,5 +473,10 @@
|
|||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
setTimeout(loadBarrager, 500);
|
setTimeout(loadBarrager, 500);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// PJAX 适配
|
||||||
|
document.addEventListener('pjax:complete', () => {
|
||||||
|
setTimeout(loadBarrager, 500);
|
||||||
|
});
|
||||||
|
|
||||||
})();
|
})();
|
||||||
@@ -27,4 +27,5 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
if (window.initCodeFold) window.initCodeFold();
|
if (window.initCodeFold) window.initCodeFold();
|
||||||
if (window.initCodeCopy) window.initCodeCopy();
|
if (window.initCodeCopy) window.initCodeCopy();
|
||||||
if (window.initParagraphComments) window.initParagraphComments();
|
if (window.initParagraphComments) window.initParagraphComments();
|
||||||
|
if (window.initInfiniteScroll && window.enableInfiniteScroll) window.initInfiniteScroll();
|
||||||
});
|
});
|
||||||
@@ -76,9 +76,36 @@ window.initArtalk = function() {
|
|||||||
var remoteConf = artalk.conf || (artalk.ctx && artalk.ctx.conf);
|
var remoteConf = artalk.conf || (artalk.ctx && artalk.ctx.conf);
|
||||||
|
|
||||||
if (remoteConf) {
|
if (remoteConf) {
|
||||||
// We only really want to cache the fields that come from the server,
|
// Filter out non-serializable properties (like DOM elements, circular refs)
|
||||||
// but saving the whole object is easier. We just have to be careful when merging.
|
var safeConf = {};
|
||||||
localStorage.setItem(confKey, JSON.stringify(remoteConf));
|
for (var key in remoteConf) {
|
||||||
|
var val = remoteConf[key];
|
||||||
|
// Skip if value is a DOM element
|
||||||
|
if (val instanceof Element || val instanceof Node) continue;
|
||||||
|
// Skip if value is window or document
|
||||||
|
if (val === window || val === document) continue;
|
||||||
|
// Skip internal private properties starting with $ or _
|
||||||
|
if (key.startsWith('$') || key.startsWith('_')) continue;
|
||||||
|
// Only allow simple types or simple objects
|
||||||
|
var type = typeof val;
|
||||||
|
if (type === 'string' || type === 'number' || type === 'boolean' || val === null) {
|
||||||
|
safeConf[key] = val;
|
||||||
|
} else if (type === 'object') {
|
||||||
|
// Simple array check
|
||||||
|
if (Array.isArray(val)) {
|
||||||
|
safeConf[key] = val;
|
||||||
|
} else {
|
||||||
|
// For objects, deep check might be too expensive/complex here.
|
||||||
|
// We'll try to shallow copy if it looks like a plain object
|
||||||
|
// and doesn't have circular markers like 'el'
|
||||||
|
if (val.constructor === Object && !val.el && !val.$el) {
|
||||||
|
safeConf[key] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.setItem(confKey, JSON.stringify(safeConf));
|
||||||
// console.log('Artalk: Config cached to', confKey);
|
// console.log('Artalk: Config cached to', confKey);
|
||||||
}
|
}
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
|
|||||||
@@ -58,11 +58,16 @@ window.initFloatingTools = function() {
|
|||||||
// Global Scroll Handler (Singleton)
|
// Global Scroll Handler (Singleton)
|
||||||
if (!window.backToTopScrollHandler) {
|
if (!window.backToTopScrollHandler) {
|
||||||
window.backToTopScrollHandler = function() {
|
window.backToTopScrollHandler = function() {
|
||||||
|
// Performance Optimization: Check scroll less frequently?
|
||||||
|
// No, rAF is best. But we can add a check if the button is even in DOM/Visible?
|
||||||
|
// But checking DOM layout is expensive.
|
||||||
|
// Keep rAF but ensure 'update' is light.
|
||||||
if (!ticking) {
|
if (!ticking) {
|
||||||
window.requestAnimationFrame(update);
|
window.requestAnimationFrame(update);
|
||||||
ticking = true;
|
ticking = true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
// Use passive listener to prevent scroll blocking
|
||||||
window.addEventListener('scroll', window.backToTopScrollHandler, { passive: true });
|
window.addEventListener('scroll', window.backToTopScrollHandler, { passive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,132 @@
|
|||||||
|
|
||||||
|
window.initInfiniteScroll = function() {
|
||||||
|
// Configuration
|
||||||
|
const containerSelector = '.posts';
|
||||||
|
const itemSelector = '.post';
|
||||||
|
const paginationSelector = '.pagination';
|
||||||
|
const nextLinkSelector = '.pagination .page-next .next';
|
||||||
|
const loadingText = '加载中...';
|
||||||
|
const noMoreText = '没有更多了';
|
||||||
|
|
||||||
|
const container = document.querySelector(containerSelector);
|
||||||
|
if (!container) return; // Not on a page with posts
|
||||||
|
|
||||||
|
// Check if we already have an observer (to prevent duplicates on PJAX re-init)
|
||||||
|
if (window.infiniteScrollObserver) {
|
||||||
|
window.infiniteScrollObserver.disconnect();
|
||||||
|
window.infiniteScrollObserver = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove existing sentinel if any
|
||||||
|
const existingSentinel = document.getElementById('infinite-scroll-sentinel');
|
||||||
|
if (existingSentinel) existingSentinel.remove();
|
||||||
|
|
||||||
|
// Find next link
|
||||||
|
let nextLink = document.querySelector(nextLinkSelector);
|
||||||
|
if (!nextLink) return; // No next page
|
||||||
|
|
||||||
|
// Hide default pagination
|
||||||
|
const pagination = document.querySelector(paginationSelector);
|
||||||
|
if (pagination) pagination.style.display = 'none';
|
||||||
|
|
||||||
|
// Create sentinel
|
||||||
|
const sentinel = document.createElement('div');
|
||||||
|
sentinel.id = 'infinite-scroll-sentinel';
|
||||||
|
sentinel.innerHTML = `<div class="infinite-scroll-loading" style="text-align:center; padding: 20px; color: #999;">${loadingText}</div>`;
|
||||||
|
|
||||||
|
// Insert after container
|
||||||
|
container.parentNode.insertBefore(sentinel, container.nextSibling);
|
||||||
|
|
||||||
|
let isLoading = false;
|
||||||
|
|
||||||
|
const loadNextPage = async () => {
|
||||||
|
if (isLoading) return;
|
||||||
|
|
||||||
|
// Re-check next link from the DOM (it should be updated after previous fetch)
|
||||||
|
nextLink = document.querySelector(nextLinkSelector);
|
||||||
|
if (!nextLink) {
|
||||||
|
sentinel.innerHTML = `<div class="infinite-scroll-end" style="text-align:center; padding: 20px; color: #999;">${noMoreText}</div>`;
|
||||||
|
if (window.infiniteScrollObserver) window.infiniteScrollObserver.disconnect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoading = true;
|
||||||
|
const url = nextLink.href;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(url);
|
||||||
|
if (!response.ok) throw new Error('Network response was not ok');
|
||||||
|
const text = await response.text();
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const doc = parser.parseFromString(text, 'text/html');
|
||||||
|
|
||||||
|
// 1. Get new posts
|
||||||
|
const newPosts = doc.querySelectorAll(`${containerSelector} ${itemSelector}`);
|
||||||
|
|
||||||
|
// 2. Append posts
|
||||||
|
if (newPosts.length > 0) {
|
||||||
|
newPosts.forEach(post => {
|
||||||
|
// Remove UIkit scrollspy attributes to prevent interference and use manual animation
|
||||||
|
post.removeAttribute('uk-scrollspy-class');
|
||||||
|
post.style.opacity = 0;
|
||||||
|
container.appendChild(post);
|
||||||
|
|
||||||
|
// Trigger reflow
|
||||||
|
void post.offsetWidth;
|
||||||
|
|
||||||
|
post.style.transition = 'opacity 0.5s ease';
|
||||||
|
post.style.opacity = 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Update pagination (crucial for next link)
|
||||||
|
const newPagination = doc.querySelector(paginationSelector);
|
||||||
|
const oldPagination = document.querySelector(paginationSelector);
|
||||||
|
if (oldPagination && newPagination) {
|
||||||
|
oldPagination.innerHTML = newPagination.innerHTML;
|
||||||
|
oldPagination.style.display = 'none'; // Keep hidden
|
||||||
|
} else if (oldPagination && !newPagination) {
|
||||||
|
oldPagination.remove();
|
||||||
|
sentinel.innerHTML = `<div class="infinite-scroll-end" style="text-align:center; padding: 20px; color: #999;">${noMoreText}</div>`;
|
||||||
|
if (window.infiniteScrollObserver) window.infiniteScrollObserver.disconnect();
|
||||||
|
isLoading = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Re-init plugins for new content
|
||||||
|
if (typeof lazyLoadInit === 'function') {
|
||||||
|
lazyLoadInit({
|
||||||
|
offsetBottom: 800,
|
||||||
|
offsetTopm: 800,
|
||||||
|
showTime: 1100
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (window.ViewImage) {
|
||||||
|
ViewImage.init('.post-content img');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Check if there is a next page in the NEW content (double check)
|
||||||
|
const newNextLink = document.querySelector(nextLinkSelector);
|
||||||
|
if (!newNextLink) {
|
||||||
|
sentinel.innerHTML = `<div class="infinite-scroll-end" style="text-align:center; padding: 20px; color: #999;">${noMoreText}</div>`;
|
||||||
|
if (window.infiniteScrollObserver) window.infiniteScrollObserver.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Infinite scroll error:', error);
|
||||||
|
sentinel.innerHTML = `<div class="infinite-scroll-error" style="text-align:center; padding: 20px; color: red; cursor: pointer;" onclick="initInfiniteScroll()">加载失败,点击重试</div>`;
|
||||||
|
} finally {
|
||||||
|
isLoading = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
window.infiniteScrollObserver = new IntersectionObserver((entries) => {
|
||||||
|
if (entries[0].isIntersecting) {
|
||||||
|
loadNextPage();
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
rootMargin: '200px'
|
||||||
|
});
|
||||||
|
|
||||||
|
window.infiniteScrollObserver.observe(sentinel);
|
||||||
|
};
|
||||||
@@ -11,19 +11,21 @@
|
|||||||
display: none;
|
display: none;
|
||||||
background: var(--artalk-editer-color, #333);
|
background: var(--artalk-editer-color, #333);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
padding: 6px 12px;
|
padding: 8px 16px;
|
||||||
border-radius: 6px;
|
border-radius: 50px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
z-index: 2147483647;
|
z-index: 2147483647;
|
||||||
transform: translate(-50%, -120%);
|
transform: translate(-50%, -120%);
|
||||||
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
|
box-shadow: 0 4px 20px rgba(0,0,0,0.25);
|
||||||
transition: opacity 0.2s, transform 0.2s;
|
transition: opacity 0.2s, transform 0.2s;
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
animation: popIn 0.2s cubic-bezier(0.18, 0.89, 0.32, 1.28);
|
animation: popIn 0.2s cubic-bezier(0.18, 0.89, 0.32, 1.28);
|
||||||
|
border: 1px solid rgba(255,255,255,0.1);
|
||||||
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
#${POPUP_ID}::after {
|
#${POPUP_ID}::after {
|
||||||
content: '';
|
content: '';
|
||||||
@@ -172,7 +174,8 @@
|
|||||||
// Create Popup Element
|
// Create Popup Element
|
||||||
var popup = document.createElement('div');
|
var popup = document.createElement('div');
|
||||||
popup.id = POPUP_ID;
|
popup.id = POPUP_ID;
|
||||||
popup.innerHTML = '<span class="icon" style="width:14px;height:14px;fill:currentColor;display:inline-block;"><svg viewBox="0 0 1024 1024" width="100%" height="100%"><path d="M576 128v298.666667h298.666667c0 164.906667-133.76 298.666667-298.666667 298.666666v170.666667c259.2 0 469.333333-210.133333 469.333333-469.333333H576zM149.333333 725.333333c0-164.906667 133.76-298.666667 298.666667-298.666666V128C188.8 128 0 338.133333 0 597.333333h469.333333v-298.666666H170.666667A298.666667 298.666667 0 0 0 149.333333 725.333333z"></path></svg></span> 评论';
|
// 使用 Remix Icon 的引用图标
|
||||||
|
popup.innerHTML = '<i class="ri-double-quotes-l" style="font-size:14px;margin-right:4px;"></i> <span>引用评论</span>';
|
||||||
document.body.appendChild(popup);
|
document.body.appendChild(popup);
|
||||||
|
|
||||||
// Create Modal Element
|
// Create Modal Element
|
||||||
@@ -494,11 +497,60 @@
|
|||||||
popup.style.opacity = 0;
|
popup.style.opacity = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Custom Smooth Scroll with Easing
|
||||||
|
function smoothScrollTo(targetPosition, duration) {
|
||||||
|
var startPosition = window.scrollY;
|
||||||
|
var distance = targetPosition - startPosition;
|
||||||
|
var startTime = null;
|
||||||
|
|
||||||
|
function animation(currentTime) {
|
||||||
|
if (startTime === null) startTime = currentTime;
|
||||||
|
var timeElapsed = currentTime - startTime;
|
||||||
|
var run = ease(timeElapsed, startPosition, distance, duration);
|
||||||
|
window.scrollTo(0, run);
|
||||||
|
if (timeElapsed < duration) requestAnimationFrame(animation);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ease-in-out Cubic
|
||||||
|
function ease(t, b, c, d) {
|
||||||
|
t /= d / 2;
|
||||||
|
if (t < 1) return c / 2 * t * t * t + b;
|
||||||
|
t -= 2;
|
||||||
|
return c / 2 * (t * t * t + 2) + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame(animation);
|
||||||
|
}
|
||||||
|
|
||||||
function handleQuoteClick() {
|
function handleQuoteClick() {
|
||||||
if (!window.artalk) return;
|
if (!window.artalk) return;
|
||||||
|
|
||||||
var commentsEl = document.getElementById('Comments');
|
var commentsEl = document.getElementById('Comments');
|
||||||
if (commentsEl) commentsEl.scrollIntoView({ behavior: 'smooth' });
|
if (commentsEl) {
|
||||||
|
// Target the editor area directly if possible, or the top of comments
|
||||||
|
// Adjust offset to show the editor more clearly (e.g. 100px from top)
|
||||||
|
var targetEl = document.querySelector('.atk-main-editor') || commentsEl;
|
||||||
|
var elementPosition = targetEl.getBoundingClientRect().top + window.scrollY;
|
||||||
|
|
||||||
|
// Align to 1/3 of the screen height (more comfortable for reading/typing)
|
||||||
|
var offsetPosition = elementPosition - (window.innerHeight / 3);
|
||||||
|
|
||||||
|
// Use custom smooth scroll (800ms)
|
||||||
|
smoothScrollTo(offsetPosition, 800);
|
||||||
|
|
||||||
|
// Highlight effect after scroll (Subtle Scale only, no blue border)
|
||||||
|
setTimeout(function() {
|
||||||
|
var editorEl = document.querySelector('.atk-main-editor');
|
||||||
|
if (editorEl) {
|
||||||
|
editorEl.style.transition = 'transform 0.3s ease';
|
||||||
|
editorEl.style.transform = 'scale(1.02)';
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
editorEl.style.transform = '';
|
||||||
|
}, 600);
|
||||||
|
}
|
||||||
|
}, 800);
|
||||||
|
}
|
||||||
|
|
||||||
var quoteContent = `> ${selectedText}\n\n`;
|
var quoteContent = `> ${selectedText}\n\n`;
|
||||||
var editor = window.artalk.editor;
|
var editor = window.artalk.editor;
|
||||||
|
|||||||
@@ -149,6 +149,14 @@ function initInlineSearch() {
|
|||||||
input.parentNode.replaceChild(newInput, input);
|
input.parentNode.replaceChild(newInput, input);
|
||||||
input = newInput;
|
input = newInput;
|
||||||
|
|
||||||
|
// Handle Search Icon Click (Focus input)
|
||||||
|
var searchIcon = searchContainer.querySelector('.search-icon');
|
||||||
|
if (searchIcon) {
|
||||||
|
searchIcon.addEventListener('click', function() {
|
||||||
|
input.focus();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Handle Close Icon Logic
|
// Handle Close Icon Logic
|
||||||
var closeIcon = searchContainer.querySelector('.close-icon');
|
var closeIcon = searchContainer.querySelector('.close-icon');
|
||||||
if (closeIcon) {
|
if (closeIcon) {
|
||||||
|
|||||||
@@ -98,7 +98,14 @@ document.addEventListener("pjax:complete", function () {
|
|||||||
if (typeof window.initParagraphComments === 'function') {
|
if (typeof window.initParagraphComments === 'function') {
|
||||||
try {
|
try {
|
||||||
window.initParagraphComments();
|
window.initParagraphComments();
|
||||||
} catch(e) { console.error('initParagraphComments error:', e); }
|
} catch (e) {
|
||||||
|
console.error('initParagraphComments error:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化无限加载
|
||||||
|
if (typeof window.initInfiniteScroll === 'function' && window.enableInfiniteScroll) {
|
||||||
|
window.initInfiniteScroll();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化友链状态
|
// 初始化友链状态
|
||||||
|
|||||||
@@ -67,12 +67,17 @@
|
|||||||
{{ $search := resources.Get "js/modules/search.js" }}
|
{{ $search := resources.Get "js/modules/search.js" }}
|
||||||
{{ $floatingTools := resources.Get "js/modules/floating-tools.js" }}
|
{{ $floatingTools := resources.Get "js/modules/floating-tools.js" }}
|
||||||
{{ $paragraphComments := resources.Get "js/modules/paragraph-comments.js" }}
|
{{ $paragraphComments := resources.Get "js/modules/paragraph-comments.js" }}
|
||||||
|
{{ $infiniteScroll := resources.Get "js/modules/infinite-scroll.js" }}
|
||||||
{{ $artalkModule := resources.Get "js/modules/artalk.js" }}
|
{{ $artalkModule := resources.Get "js/modules/artalk.js" }}
|
||||||
{{ $pjaxLib := resources.Get "js/pjax.js" }}
|
{{ $pjaxLib := resources.Get "js/pjax.js" }}
|
||||||
|
|
||||||
{{ $scripts := slice $iconfont $lightgallery $uikit $easylazyload $viewimage $artalk $barrage $utils $loader $search $floatingTools $paragraphComments $artalkModule $nprogress $pjaxLib $main $mypjax $message $echarts $tiaozhuan | resources.Concat "js/bundle.js" | resources.Minify | resources.Fingerprint }}
|
{{ $scripts := slice $iconfont $lightgallery $uikit $easylazyload $viewimage $artalk $barrage $utils $loader $search $floatingTools $paragraphComments $infiniteScroll $artalkModule $nprogress $pjaxLib $main $mypjax $message $echarts $tiaozhuan | resources.Concat "js/bundle.js" | resources.Minify | resources.Fingerprint }}
|
||||||
<script src="{{ $scripts.RelPermalink }}"></script>
|
<script src="{{ $scripts.RelPermalink }}"></script>
|
||||||
<script async src="/js/bsz.js"></script>
|
<script>
|
||||||
|
// Global configuration from Hugo
|
||||||
|
window.enableInfiniteScroll = {{ .Site.Params.infiniteScroll.enable | default false }};
|
||||||
|
</script>
|
||||||
|
<script async src="/js/bsz.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
Reference in new issue
Block a user