优化性能提交新功能
This commit is contained in:
1 parent
9ab38b6e6a
commit
b83d6adb76
6 files changed
+506
-245
No files matched your search
@@ -154,75 +154,257 @@ window.initArtalk = function() {
|
||||
setTimeout(saveConf, 2000);
|
||||
setTimeout(saveConf, 5000);
|
||||
|
||||
// Handle list-loaded event for lazy loading avatars and animations
|
||||
artalk.on('list-loaded', function() {
|
||||
if (window.artalkConfig.aiComment) {
|
||||
var ai = window.artalkConfig.aiComment;
|
||||
var listBody = document.querySelector('.atk-list .atk-list-body');
|
||||
if (listBody && !listBody.querySelector('.atk-ai-comment')) {
|
||||
var aiCommentHtml = `
|
||||
<div class="atk-comment-wrap atk-comment atk-ai-comment" id="atk-comment-ai">
|
||||
<div class="atk-avatar" uk-scrollspy="cls: uk-animation-scale-up; delay: 200">
|
||||
<img src="${ai.avatar}" alt="${ai.name}">
|
||||
</div>
|
||||
<div class="atk-main" uk-scrollspy="cls: uk-animation-slide-right-small; delay: 300">
|
||||
<div class="atk-header">
|
||||
<span class="atk-nick" uk-scrollspy="cls: uk-animation-slide-top-small; delay: 350">${ai.name}</span>
|
||||
<span class="atk-badge">${window.artalkConfig.aiBadge || 'AI'}</span>
|
||||
<span class="atk-date">${ai.date}</span>
|
||||
</div>
|
||||
<div class="atk-body">
|
||||
<div class="atk-content">${ai.content}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
listBody.insertAdjacentHTML('afterbegin', aiCommentHtml);
|
||||
|
||||
// Hide "No Comment" placeholder if it exists
|
||||
var noComment = document.querySelector('.atk-list-no-comment');
|
||||
if (noComment) {
|
||||
noComment.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
// ── Badge system: 友链 / 友圈 / 评论等级 ──
|
||||
var badgesInjected = false;
|
||||
|
||||
// Add staggered animation to the comment list
|
||||
var list = document.querySelector('.atk-list');
|
||||
if (list) {
|
||||
list.setAttribute('uk-scrollspy', 'target: > .atk-comment-wrap; cls: uk-animation-slide-bottom-small; delay: 50');
|
||||
}
|
||||
var normalizeHost = function(h) {
|
||||
return (h || '').replace(/^www\./, '');
|
||||
};
|
||||
|
||||
// Apply granular animations to inner elements
|
||||
document.querySelectorAll('.atk-comment-wrap:not(.atk-ai-comment)').forEach(function(wrap) {
|
||||
var avatar = wrap.querySelector('.atk-avatar');
|
||||
if (avatar) avatar.setAttribute('uk-scrollspy', 'cls: uk-animation-scale-up; delay: 200');
|
||||
|
||||
var main = wrap.querySelector('.atk-main');
|
||||
if (main) main.setAttribute('uk-scrollspy', 'cls: uk-animation-slide-right-small; delay: 300');
|
||||
|
||||
var nick = wrap.querySelector('.atk-nick');
|
||||
if (nick) nick.setAttribute('uk-scrollspy', 'cls: uk-animation-slide-top-small; delay: 350');
|
||||
});
|
||||
|
||||
// Find all avatars in the comment list
|
||||
// We let lazyload.js handle the avatars automatically by calling lazyLoadAgain()
|
||||
// which will find these new images, set the placeholder, and observe them.
|
||||
|
||||
// Trigger lazy load update
|
||||
if (window.lazyLoadAgain) {
|
||||
window.lazyLoadAgain();
|
||||
}
|
||||
|
||||
// Force hide "No Comment" if AI comment exists
|
||||
if (window.artalkConfig.aiComment) {
|
||||
var noComment = document.querySelector('.atk-list-no-comment');
|
||||
var aiComment = document.querySelector('.atk-ai-comment');
|
||||
if (aiComment && noComment) {
|
||||
noComment.style.display = 'none';
|
||||
}
|
||||
}
|
||||
var buildMap = function(data) {
|
||||
var hosts = new Map();
|
||||
var names = new Map();
|
||||
if (data && data.hosts) {
|
||||
data.hosts.forEach(function(item) {
|
||||
if (item.host) hosts.set(item.host, item.title || item.host);
|
||||
});
|
||||
}
|
||||
if (data && data.names) {
|
||||
data.names.forEach(function(n) {
|
||||
if (n.lower) names.set(n.lower, n.title);
|
||||
});
|
||||
}
|
||||
return { hosts: hosts, names: names };
|
||||
};
|
||||
|
||||
var linkMap = null;
|
||||
var feedMap = null;
|
||||
|
||||
// Comment rank table — Song dynasty 九品十八阶
|
||||
var RANK_TABLE = [
|
||||
{ min: 2000, title: '正一品·太师', short: '太师', color: '#b8860b', bg: 'rgba(184,134,11,0.22)' },
|
||||
{ min: 1400, title: '从一品·太尉', short: '太尉', color: '#b8860b', bg: 'rgba(184,134,11,0.18)' },
|
||||
{ min: 1050, title: '正二品·参知政事', short: '参知政事', color: '#7c3aed', bg: 'rgba(124,58,237,0.18)' },
|
||||
{ min: 800, title: '从二品·节度使', short: '节度使', color: '#7c3aed', bg: 'rgba(124,58,237,0.15)' },
|
||||
{ min: 600, title: '正三品·御史中丞', short: '御史中丞', color: '#a855f7', bg: 'rgba(168,85,247,0.15)' },
|
||||
{ min: 480, title: '从三品·秘书监', short: '秘书监', color: '#a855f7', bg: 'rgba(168,85,247,0.12)' },
|
||||
{ min: 380, title: '正四品·谏议大夫', short: '谏议大夫', color: '#c084fc', bg: 'rgba(192,132,252,0.12)' },
|
||||
{ min: 300, title: '从四品·侍读学士', short: '侍读学士', color: '#c084fc', bg: 'rgba(192,132,252,0.10)' },
|
||||
{ min: 240, title: '正五品·给事中', short: '给事中', color: '#dc2626', bg: 'rgba(220,38,38,0.15)' },
|
||||
{ min: 190, title: '从五品·知州', short: '知州', color: '#dc2626', bg: 'rgba(220,38,38,0.12)' },
|
||||
{ min: 150, title: '正六品·侍御史', short: '侍御史', color: '#ef4444', bg: 'rgba(239,68,68,0.12)' },
|
||||
{ min: 115, title: '从六品·通判', short: '通判', color: '#ef4444', bg: 'rgba(239,68,68,0.10)' },
|
||||
{ min: 85, title: '正七品·知县', short: '知县', color: '#059669', bg: 'rgba(5,150,105,0.15)' },
|
||||
{ min: 60, title: '从七品·殿中侍御史', short: '殿中侍御史', color: '#059669', bg: 'rgba(5,150,105,0.12)' },
|
||||
{ min: 40, title: '正八品·大理评事', short: '大理评事', color: '#10b981', bg: 'rgba(16,185,129,0.12)' },
|
||||
{ min: 25, title: '从八品·录事参军', short: '录事参军', color: '#10b981', bg: 'rgba(16,185,129,0.10)' },
|
||||
{ min: 15, title: '正九品·主簿', short: '主簿', color: '#a16207', bg: 'rgba(161,98,7,0.15)' },
|
||||
{ min: 5, title: '从九品·司户参军', short: '司户参军', color: '#a16207', bg: 'rgba(161,98,7,0.12)' },
|
||||
{ min: 0, title: '庶民', short: '庶民', color: '#9ca3af', bg: 'rgba(156,163,175,0.12)' }
|
||||
];
|
||||
|
||||
var getRank = function(count) {
|
||||
for (var i = 0; i < RANK_TABLE.length; i++) {
|
||||
if (count >= RANK_TABLE[i].min) return RANK_TABLE[i];
|
||||
}
|
||||
return RANK_TABLE[RANK_TABLE.length - 1];
|
||||
};
|
||||
|
||||
// localStorage-based cross-page user comment counter with page dedup
|
||||
var USER_COUNT_KEY = 'atk_user_counts_v2';
|
||||
var loadUserCounts = function() {
|
||||
try {
|
||||
var raw = localStorage.getItem(USER_COUNT_KEY);
|
||||
return raw ? JSON.parse(raw) : { _pages: {} };
|
||||
} catch(e) { return { _pages: {} }; }
|
||||
};
|
||||
var saveUserCounts = function(counts) {
|
||||
try { localStorage.setItem(USER_COUNT_KEY, JSON.stringify(counts)); } catch(e) {}
|
||||
};
|
||||
var getPageKey = function() {
|
||||
return window.artalkConfig ? (window.artalkConfig.pageKey || window.location.pathname) : window.location.pathname;
|
||||
};
|
||||
|
||||
var processBadges = function() {
|
||||
if (!linkMap && !feedMap) return;
|
||||
|
||||
var data = loadUserCounts();
|
||||
var pageKey = getPageKey();
|
||||
var pages = data._pages || {};
|
||||
var isNewPage = !pages[pageKey];
|
||||
|
||||
// Count per-user comments on current page
|
||||
var pageCounts = {};
|
||||
document.querySelectorAll('.atk-comment-wrap:not(.atk-ai-comment)').forEach(function(wrap) {
|
||||
var nickEl = wrap.querySelector('.atk-nick');
|
||||
if (!nickEl) return;
|
||||
var name = (nickEl.textContent || '').trim();
|
||||
if (name) pageCounts[name] = (pageCounts[name] || 0) + 1;
|
||||
});
|
||||
|
||||
// Accumulate: only add counts if this page hasn't been processed before
|
||||
var userCounts = {};
|
||||
for (var name in pageCounts) {
|
||||
var prev = data[name] || 0;
|
||||
if (isNewPage) {
|
||||
data[name] = prev + pageCounts[name];
|
||||
}
|
||||
userCounts[name] = data[name];
|
||||
}
|
||||
if (isNewPage) {
|
||||
pages[pageKey] = true;
|
||||
data._pages = pages;
|
||||
}
|
||||
saveUserCounts(data);
|
||||
|
||||
document.querySelectorAll('.atk-comment-wrap:not(.atk-ai-comment)').forEach(function(wrap) {
|
||||
var nickEl = wrap.querySelector('.atk-nick');
|
||||
var avatarEl = wrap.querySelector('.atk-avatar');
|
||||
if (!nickEl) return;
|
||||
|
||||
var nickText = (nickEl.textContent || '').trim();
|
||||
var nickLower = nickText.toLowerCase();
|
||||
var nickLink = nickEl.querySelector('a[href]');
|
||||
var commentHost = null;
|
||||
if (nickLink) {
|
||||
try { commentHost = normalizeHost(new URL(nickLink.getAttribute('href')).hostname); } catch(e) {}
|
||||
}
|
||||
|
||||
// 1. 友链 badge (green gradient) — from /api/links?all=1
|
||||
if (linkMap && !nickEl.querySelector('.atk-link-badge')) {
|
||||
var linkMatch = null;
|
||||
if (commentHost && linkMap.hosts.has(commentHost)) {
|
||||
linkMatch = linkMap.hosts.get(commentHost);
|
||||
} else if (linkMap.names.has(nickLower)) {
|
||||
linkMatch = linkMap.names.get(nickLower);
|
||||
}
|
||||
if (linkMatch) {
|
||||
var lb = document.createElement('span');
|
||||
lb.className = 'atk-link-badge';
|
||||
lb.textContent = '友链';
|
||||
lb.title = '友链: ' + linkMatch;
|
||||
nickEl.appendChild(lb);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 友圈 badge (gold gradient) — from /api/feeds
|
||||
if (feedMap && !nickEl.querySelector('.atk-feed-badge')) {
|
||||
var feedMatch = null;
|
||||
if (commentHost && feedMap.hosts.has(commentHost)) {
|
||||
feedMatch = feedMap.hosts.get(commentHost);
|
||||
} else if (feedMap.names.has(nickLower)) {
|
||||
feedMatch = feedMap.names.get(nickLower);
|
||||
}
|
||||
if (feedMatch) {
|
||||
var fb = document.createElement('span');
|
||||
fb.className = 'atk-feed-badge';
|
||||
fb.textContent = '友圈';
|
||||
fb.title = '友圈: ' + feedMatch;
|
||||
nickEl.appendChild(fb);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Comment rank badge — inline next to name
|
||||
if (!nickEl.querySelector('.atk-rank-badge')) {
|
||||
var count = userCounts[nickText] || 0;
|
||||
var rank = getRank(count);
|
||||
var rb = document.createElement('span');
|
||||
rb.className = 'atk-rank-badge';
|
||||
rb.textContent = rank.short;
|
||||
rb.title = rank.title + ' (' + count + '条评论)';
|
||||
var idx = RANK_TABLE.indexOf(rank);
|
||||
if (idx > 0) {
|
||||
var next = RANK_TABLE[idx - 1];
|
||||
var diff = next.min - count;
|
||||
if (diff > 0) {
|
||||
var progress = Math.round((count - rank.min) / (next.min - rank.min) * 100);
|
||||
rb.title += ' — 距' + next.short + '还差' + diff + '条 (' + progress + '%)';
|
||||
}
|
||||
}
|
||||
nickEl.appendChild(rb);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var injectAllBadges = function() {
|
||||
if (badgesInjected) return;
|
||||
badgesInjected = true;
|
||||
|
||||
if (window.friendLinks) linkMap = buildMap(window.friendLinks);
|
||||
if (window.friendFeeds) feedMap = buildMap(window.friendFeeds);
|
||||
|
||||
if (linkMap || feedMap) {
|
||||
processBadges();
|
||||
}
|
||||
};
|
||||
|
||||
artalk.on('list-loaded', function() {
|
||||
// Badge injection
|
||||
badgesInjected = false;
|
||||
setTimeout(injectAllBadges, 300);
|
||||
|
||||
if (window.artalkConfig.aiComment) {
|
||||
var ai = window.artalkConfig.aiComment;
|
||||
var listBody = document.querySelector('.atk-list .atk-list-body');
|
||||
if (listBody && !listBody.querySelector('.atk-ai-comment')) {
|
||||
var aiCommentHtml = `
|
||||
<div class="atk-comment-wrap atk-comment atk-ai-comment" id="atk-comment-ai">
|
||||
<div class="atk-avatar" uk-scrollspy="cls: uk-animation-scale-up; delay: 200">
|
||||
<img src="${ai.avatar}" alt="${ai.name}">
|
||||
</div>
|
||||
<div class="atk-main" uk-scrollspy="cls: uk-animation-slide-right-small; delay: 300">
|
||||
<div class="atk-header">
|
||||
<span class="atk-nick" uk-scrollspy="cls: uk-animation-slide-top-small; delay: 350">${ai.name}</span>
|
||||
<span class="atk-badge">${window.artalkConfig.aiBadge || 'AI'}</span>
|
||||
<span class="atk-date">${ai.date}</span>
|
||||
</div>
|
||||
<div class="atk-body">
|
||||
<div class="atk-content">${ai.content}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
listBody.insertAdjacentHTML('afterbegin', aiCommentHtml);
|
||||
|
||||
var noComment = document.querySelector('.atk-list-no-comment');
|
||||
if (noComment) {
|
||||
noComment.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add staggered animation to the comment list
|
||||
var list = document.querySelector('.atk-list');
|
||||
if (list) {
|
||||
list.setAttribute('uk-scrollspy', 'target: > .atk-comment-wrap; cls: uk-animation-slide-bottom-small; delay: 50');
|
||||
}
|
||||
|
||||
// Apply granular animations to inner elements
|
||||
document.querySelectorAll('.atk-comment-wrap:not(.atk-ai-comment)').forEach(function(wrap) {
|
||||
var avatar = wrap.querySelector('.atk-avatar');
|
||||
if (avatar) avatar.setAttribute('uk-scrollspy', 'cls: uk-animation-scale-up; delay: 200');
|
||||
|
||||
var main = wrap.querySelector('.atk-main');
|
||||
if (main) main.setAttribute('uk-scrollspy', 'cls: uk-animation-slide-right-small; delay: 300');
|
||||
|
||||
var nick = wrap.querySelector('.atk-nick');
|
||||
if (nick) nick.setAttribute('uk-scrollspy', 'cls: uk-animation-slide-top-small; delay: 350');
|
||||
});
|
||||
|
||||
// Trigger lazy load update
|
||||
if (window.lazyLoadAgain) {
|
||||
window.lazyLoadAgain();
|
||||
}
|
||||
|
||||
// Force hide "No Comment" if AI comment exists
|
||||
if (window.artalkConfig.aiComment) {
|
||||
var noComment = document.querySelector('.atk-list-no-comment');
|
||||
var aiComment = document.querySelector('.atk-ai-comment');
|
||||
if (aiComment && noComment) {
|
||||
noComment.style.display = 'none';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
console.error('Artalk init failed:', e);
|
||||
|
||||
@@ -1,43 +1,41 @@
|
||||
function themeSweep(newTheme, callback) {
|
||||
var veil = document.createElement('div');
|
||||
var gradient = newTheme === 'dark'
|
||||
? 'linear-gradient(135deg,rgba(2,6,23,0.18),rgba(15,23,42,0.55) 45%,rgba(30,41,59,0.36))'
|
||||
: 'linear-gradient(135deg,rgba(255,247,237,0.28),rgba(252,250,245,0.62) 45%,rgba(254,243,199,0.32))';
|
||||
// View Transition API — browser-native crossfade, zero JS animation overhead.
|
||||
// Same principle as image lightbox transitions. Chrome/Edge 111+, Opera 97+.
|
||||
if (document.startViewTransition) {
|
||||
document.startViewTransition(function() { callback(); });
|
||||
return;
|
||||
}
|
||||
|
||||
veil.style.cssText =
|
||||
// Fallback: lightweight overlay for Safari / Firefox
|
||||
var oldTheme = document.documentElement.getAttribute('data-theme');
|
||||
var tint = oldTheme === 'dark'
|
||||
? 'rgba(15,23,42,0.95)'
|
||||
: 'rgba(255,255,255,0.95)';
|
||||
|
||||
var overlay = document.createElement('div');
|
||||
overlay.style.cssText =
|
||||
'position:fixed;inset:0;z-index:9999;pointer-events:none;' +
|
||||
'background:' + gradient + ';' +
|
||||
'opacity:0;transition:opacity 0.42s cubic-bezier(0.22,0.61,0.36,1);' +
|
||||
'background:' + tint + ';opacity:1;' +
|
||||
'transition:opacity 0.22s ease;' +
|
||||
'will-change:opacity;contain:paint;';
|
||||
document.body.appendChild(veil);
|
||||
document.body.appendChild(overlay);
|
||||
|
||||
var cleaned = false;
|
||||
function cleanup() {
|
||||
if (cleaned) return;
|
||||
cleaned = true;
|
||||
if (veil.parentNode) veil.parentNode.removeChild(veil);
|
||||
if (overlay.parentNode) overlay.parentNode.removeChild(overlay);
|
||||
}
|
||||
|
||||
var applied = false;
|
||||
function applyTheme() {
|
||||
if (applied) return;
|
||||
applied = true;
|
||||
callback();
|
||||
veil.style.opacity = '0';
|
||||
}
|
||||
callback();
|
||||
overlay.offsetWidth;
|
||||
overlay.style.opacity = '0';
|
||||
|
||||
veil.offsetWidth;
|
||||
veil.style.opacity = '1';
|
||||
|
||||
setTimeout(applyTheme, 300);
|
||||
veil.addEventListener('transitionend', function(e) {
|
||||
if (!applied || e.propertyName !== 'opacity') return;
|
||||
overlay.addEventListener('transitionend', function(e) {
|
||||
if (e.propertyName !== 'opacity') return;
|
||||
cleanup();
|
||||
});
|
||||
setTimeout(function() {
|
||||
applyTheme();
|
||||
cleanup();
|
||||
}, 950);
|
||||
setTimeout(cleanup, 380);
|
||||
}
|
||||
|
||||
// Toast Message Helper
|
||||
@@ -85,23 +83,36 @@ window.initThemeToggle = function() {
|
||||
window.initCodeFold = function() {
|
||||
var codeBlocks = document.querySelectorAll('.highlight');
|
||||
codeBlocks.forEach(function(block) {
|
||||
if (block.offsetHeight > 360) {
|
||||
if (block.parentNode.classList.contains('code-fold-container')) return;
|
||||
var wrapper = block.parentNode && block.parentNode.classList.contains('code-fold-container') ? block.parentNode : null;
|
||||
if (!wrapper && block.scrollHeight <= 360 && block.offsetHeight <= 360) return;
|
||||
|
||||
var wrapper = document.createElement('div');
|
||||
if (!wrapper) {
|
||||
wrapper = document.createElement('div');
|
||||
wrapper.className = 'code-fold-container collapsed';
|
||||
block.parentNode.insertBefore(wrapper, block);
|
||||
wrapper.appendChild(block);
|
||||
}
|
||||
|
||||
var btn = document.createElement('button');
|
||||
var btn = wrapper.querySelector(':scope > .code-expand-btn');
|
||||
if (!btn) {
|
||||
btn = document.createElement('button');
|
||||
btn.type = 'button';
|
||||
btn.className = 'code-expand-btn';
|
||||
btn.innerHTML = '<span><i class="ri-arrow-down-s-line" style="margin-right:4px;"></i> 展开代码 </span>';
|
||||
btn.onclick = function() {
|
||||
wrapper.classList.remove('collapsed');
|
||||
btn.style.display = 'none';
|
||||
};
|
||||
wrapper.appendChild(btn);
|
||||
}
|
||||
|
||||
var syncButton = function() {
|
||||
var collapsed = wrapper.classList.contains('collapsed');
|
||||
btn.innerHTML = collapsed
|
||||
? '<span><i class="ri-arrow-down-s-line" style="margin-right:4px;"></i> 展开代码 </span>'
|
||||
: '<span><i class="ri-arrow-up-s-line" style="margin-right:4px;"></i> 收起代码 </span>';
|
||||
};
|
||||
|
||||
btn.onclick = function() {
|
||||
wrapper.classList.toggle('collapsed');
|
||||
syncButton();
|
||||
};
|
||||
syncButton();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user