fixed 弹幕修复 bug
This commit is contained in:
1 parent
a4c74cbb0d
commit
0714b8eb48
11 files changed
+446
-139
No files matched your search
@@ -76,9 +76,36 @@ window.initArtalk = function() {
|
||||
var remoteConf = artalk.conf || (artalk.ctx && artalk.ctx.conf);
|
||||
|
||||
if (remoteConf) {
|
||||
// We only really want to cache the fields that come from the server,
|
||||
// but saving the whole object is easier. We just have to be careful when merging.
|
||||
localStorage.setItem(confKey, JSON.stringify(remoteConf));
|
||||
// Filter out non-serializable properties (like DOM elements, circular refs)
|
||||
var safeConf = {};
|
||||
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);
|
||||
}
|
||||
} catch(e) {
|
||||
|
||||
@@ -58,11 +58,16 @@ window.initFloatingTools = function() {
|
||||
// Global Scroll Handler (Singleton)
|
||||
if (!window.backToTopScrollHandler) {
|
||||
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) {
|
||||
window.requestAnimationFrame(update);
|
||||
ticking = true;
|
||||
}
|
||||
};
|
||||
// Use passive listener to prevent scroll blocking
|
||||
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;
|
||||
background: var(--artalk-editer-color, #333);
|
||||
color: #fff;
|
||||
padding: 6px 12px;
|
||||
border-radius: 6px;
|
||||
padding: 8px 16px;
|
||||
border-radius: 50px;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
z-index: 2147483647;
|
||||
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;
|
||||
pointer-events: auto;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
white-space: nowrap;
|
||||
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 {
|
||||
content: '';
|
||||
@@ -172,7 +174,8 @@
|
||||
// Create Popup Element
|
||||
var popup = document.createElement('div');
|
||||
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);
|
||||
|
||||
// Create Modal Element
|
||||
@@ -494,11 +497,60 @@
|
||||
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() {
|
||||
if (!window.artalk) return;
|
||||
|
||||
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 editor = window.artalk.editor;
|
||||
|
||||
@@ -149,6 +149,14 @@ function initInlineSearch() {
|
||||
input.parentNode.replaceChild(newInput, input);
|
||||
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
|
||||
var closeIcon = searchContainer.querySelector('.close-icon');
|
||||
if (closeIcon) {
|
||||
|
||||
Reference in new issue
Block a user