微件:Card:修订间差异
跳转到导航
跳转到搜索
小 功能测试 |
小 功能测试 |
||
| 第1行: | 第1行: | ||
<includeonly> | |||
<style> | <style> | ||
/* 卡牌容器 */ | /* 卡牌容器 */ | ||
| 第170行: | 第162行: | ||
transform: translateY(0); | transform: translateY(0); | ||
box-shadow: 0 2px 10px rgba(102, 126, 234, 0.4); | box-shadow: 0 2px 10px rgba(102, 126, 234, 0.4); | ||
} | |||
/* 闪光按钮状态 */ | |||
.spark-button.loading { | |||
opacity: 0.7; | |||
cursor: wait; | |||
} | |||
.spark-button.cached::after { | |||
content: " ✓"; | |||
color: #90EE90; | |||
} | } | ||
/* 闪光图标动画 */ | /* 闪光图标动画 */ | ||
.spark-button::before { | .spark-button:not(.loading)::before { | ||
content: "✦ "; | content: "✦ "; | ||
animation: sparkle 1.5s ease-in-out infinite; | animation: sparkle 1.5s ease-in-out infinite; | ||
} | } | ||
| 第246行: | 第244行: | ||
50% { content: ".."; } | 50% { content: ".."; } | ||
75% { content: "..."; } | 75% { content: "..."; } | ||
} | |||
/* 加载进度 */ | |||
.spark-loading-progress { | |||
color: #aaa; | |||
font-size: 14px; | |||
margin-top: 10px; | |||
} | } | ||
| 第254行: | 第259行: | ||
text-align: center; | text-align: center; | ||
padding: 40px; | padding: 40px; | ||
} | |||
/* 缓存状态提示 */ | |||
.spark-cache-hint { | |||
color: #666; | |||
font-size: 12px; | |||
text-align: center; | |||
margin-top: 15px; | |||
} | } | ||
| 第343行: | 第356行: | ||
'use strict'; | 'use strict'; | ||
// | // ==================== 缓存管理器(优化版) ==================== | ||
var SparkCache = (function() { | |||
var CACHE_KEY = 'spark_card_cache_v2'; | |||
var CACHE_EXPIRE = 24 * 60 * 60 * 1000; // 24小时 | |||
// 内存缓存层 | |||
var memoryCache = null; | |||
var isDirty = false; | |||
// | var saveTimer = null; | ||
// 从 localStorage 加载到内存 | |||
function loadFromStorage() { | |||
if (memoryCache !== null) return memoryCache; | |||
try { | |||
var data = localStorage.getItem(CACHE_KEY); | |||
memoryCache = data ? JSON.parse(data) : {}; | |||
} catch (e) { | |||
memoryCache = {}; | |||
} | |||
return memoryCache; | |||
} | |||
// 延迟批量写入 localStorage | |||
function scheduleSave() { | |||
if (saveTimer) return; | |||
saveTimer = setTimeout(function() { | |||
saveTimer = null; | |||
if (isDirty && memoryCache) { | |||
try { | |||
localStorage.setItem(CACHE_KEY, JSON.stringify(memoryCache)); | |||
isDirty = false; | |||
} catch (e) { | |||
// 存储空间不足,清理旧数据 | |||
cleanup(true); | |||
} | |||
} | |||
}, 1000); // 1秒后批量写入 | |||
} | |||
// 清理过期缓存 | |||
function cleanup(force) { | |||
var cache = loadFromStorage(); | |||
var now = Date.now(); | |||
var hasChanges = false; | |||
var keys = Object.keys(cache); | |||
// 强制清理时,删除最旧的一半 | |||
if (force && keys.length > 10) { | |||
var items = keys.map(function(k) { | |||
return { key: k, time: cache[k].timestamp }; | |||
}).sort(function(a, b) { | |||
return a.time - b.time; | |||
}); | |||
var deleteCount = Math.floor(items.length / 2); | |||
for (var i = 0; i < deleteCount; i++) { | |||
delete cache[items[i].key]; | |||
hasChanges = true; | |||
} | |||
} else { | |||
// 正常清理过期数据 | |||
for (var cardId in cache) { | |||
if (cache.hasOwnProperty(cardId) && now - cache[cardId].timestamp > CACHE_EXPIRE) { | |||
delete cache[cardId]; | |||
hasChanges = true; | |||
} | |||
} | |||
} | |||
if (hasChanges) { | |||
isDirty = true; | |||
scheduleSave(); | |||
} | |||
} | } | ||
// | return { | ||
get: function(cardId) { | |||
var cache = loadFromStorage(); | |||
var item = cache[cardId]; | |||
if (!item) return null; | |||
if (Date.now() - item.timestamp > CACHE_EXPIRE) { | |||
delete cache[cardId]; | |||
isDirty = true; | |||
scheduleSave(); | |||
return null; | |||
} | |||
return item.html; | |||
}, | |||
set: function(cardId, html) { | |||
var cache = loadFromStorage(); | |||
cache[cardId] = { | |||
html: html, | |||
timestamp: Date.now() | |||
}; | |||
isDirty = true; | |||
scheduleSave(); | |||
}, | |||
has: function(cardId) { | |||
var cache = loadFromStorage(); | |||
var item = cache[cardId]; | |||
return item && (Date.now() - item.timestamp <= CACHE_EXPIRE); | |||
}, | |||
// 批量获取 | |||
getMultiple: function(cardIds) { | |||
var cache = loadFromStorage(); | |||
var now = Date.now(); | |||
var result = {}; | |||
var hasExpired = false; | |||
cardIds.forEach(function(cardId) { | |||
var item = cache[cardId]; | |||
if (item) { | |||
if (now - item.timestamp <= CACHE_EXPIRE) { | |||
result[cardId] = item.html; | |||
} else { | |||
delete cache[cardId]; | |||
hasExpired = true; | |||
} | |||
} | |||
}); | |||
if (hasExpired) { | |||
isDirty = true; | |||
scheduleSave(); | |||
} | |||
return result; | |||
}, | |||
// 批量设置 | |||
setMultiple: function(dataMap) { | |||
var cache = loadFromStorage(); | |||
var now = Date.now(); | |||
for (var cardId in dataMap) { | |||
if (dataMap.hasOwnProperty(cardId)) { | |||
cache[cardId] = { | |||
html: dataMap[cardId], | |||
timestamp: now | |||
}; | |||
} | |||
} | |||
isDirty = true; | |||
scheduleSave(); | |||
}, | |||
cleanup: cleanup, | |||
// 页面卸载时立即保存 | |||
flush: function() { | |||
if (saveTimer) { | |||
clearTimeout(saveTimer); | |||
saveTimer = null; | |||
} | |||
if (isDirty && memoryCache) { | |||
try { | |||
localStorage.setItem(CACHE_KEY, JSON.stringify(memoryCache)); | |||
isDirty = false; | |||
} catch (e) {} | |||
} | |||
} | |||
}; | |||
})(); | |||
// ==================== 批量API请求管理器 ==================== | |||
var BatchRequestManager = (function() { | |||
var pendingRequests = {}; // cardId -> [resolve, reject][] | |||
var requestQueue = []; | |||
var isProcessing = false; | |||
var batchSize = 5; // 每批并行请求数 | |||
var batchDelay = 50; // 批次间延迟 | |||
if ( | function processBatch() { | ||
if (isProcessing || requestQueue.length === 0) return; | |||
isProcessing = true; | |||
// 取出一批 | |||
var batch = requestQueue.splice(0, batchSize); | |||
var promises = batch.map(function(cardId) { | |||
return fetchSparkList(cardId); | |||
}); | |||
Promise.all(promises).then(function() { | |||
isProcessing = false; | |||
if (requestQueue.length > 0) { | |||
setTimeout(processBatch, batchDelay); | |||
} | |||
}); | |||
} | } | ||
var | function fetchSparkList(cardId) { | ||
return new Promise(function(resolve) { | |||
var api = new mw.Api(); | |||
api.get({ | |||
action: 'parse', | |||
text: '{{Card/spark/list|' + cardId + '}}', | |||
prop: 'text', | |||
disablelimitreport: true, | |||
format: 'json' | |||
}).done(function(data) { | |||
var html = ''; | |||
if (data.parse && data.parse.text) { | |||
html = data.parse.text['*']; | |||
SparkCache.set(cardId, html); | |||
} | |||
// 通知所有等待者 | |||
var callbacks = pendingRequests[cardId] || []; | |||
delete pendingRequests[cardId]; | |||
callbacks.forEach(function(cb) { | |||
cb.resolve(html); | |||
}); | |||
resolve(); | |||
}).fail(function(error) { | |||
var callbacks = pendingRequests[cardId] || []; | |||
delete pendingRequests[cardId]; | |||
callbacks.forEach(function(cb) { | |||
cb.reject(error); | |||
}); | |||
resolve(); | |||
}); | |||
}); | |||
} | |||
// | return { | ||
// 请求单个卡牌(会自动合并到批量请求) | |||
request: function(cardId) { | |||
return new Promise(function(resolve, reject) { | |||
// 先检查缓存 | |||
var cached = SparkCache.get(cardId); | |||
if (cached !== null) { | |||
resolve(cached); | |||
return; | |||
} | |||
// 检查是否已在请求中 | |||
if (pendingRequests[cardId]) { | |||
pendingRequests[cardId].push({ resolve: resolve, reject: reject }); | |||
return; | |||
} | |||
// 新请求 | |||
pendingRequests[cardId] = [{ resolve: resolve, reject: reject }]; | |||
requestQueue.push(cardId); | |||
// 启动处理 | |||
if (!isProcessing) { | |||
setTimeout(processBatch, 10); | |||
} | |||
}); | |||
}, | |||
// 预加载多个卡牌(低优先级) | |||
preload: function(cardIds) { | |||
// 过滤掉已缓存和已在队列中的 | |||
var toLoad = cardIds.filter(function(cardId) { | |||
return !SparkCache.has(cardId) && | |||
!pendingRequests[cardId] && | |||
requestQueue.indexOf(cardId) === -1; | |||
}); | |||
if (toLoad.length === 0) return; | |||
// 添加到队列末尾 | |||
toLoad.forEach(function(cardId) { | |||
pendingRequests[cardId] = []; | |||
requestQueue.push(cardId); | |||
}); | |||
// 启动处理 | |||
if (!isProcessing) { | |||
setTimeout(processBatch, 100); | |||
} | |||
}, | |||
// 提升优先级 | |||
prioritize: function(cardId) { | |||
var idx = requestQueue.indexOf(cardId); | |||
if (idx > 0) { | |||
requestQueue.splice(idx, 1); | |||
requestQueue.unshift(cardId); | |||
} | |||
} | |||
}; | |||
})(); | |||
// ==================== DOM 管理器 ==================== | |||
var DOMManager = (function() { | |||
var modalOverlay = null; | |||
var sparkModalOverlay = null; | |||
var modalContent = null; | |||
var sparkModalContent = null; | |||
var currentEnlargedContainer = null; | var currentEnlargedContainer = null; | ||
var processedCards = new WeakSet(); // 使用 WeakSet 跟踪已处理的卡牌 | |||
// | // 创建模态框 | ||
function createModals() { | |||
if (!modalOverlay) { | |||
modalOverlay = document.createElement('div'); | |||
modalOverlay.className = 'card-modal-overlay'; | |||
modalOverlay.innerHTML = '<span class="card-modal-close">×</span><div class="card-modal-content"></div>'; | |||
document.body.appendChild(modalOverlay); | |||
modalContent = modalOverlay.querySelector('.card-modal-content'); | |||
} | |||
if (!sparkModalOverlay) { | |||
sparkModalOverlay = document.createElement('div'); | |||
sparkModalOverlay.className = 'spark-modal-overlay'; | |||
sparkModalOverlay.innerHTML = '<span class="spark-modal-close">×</span><div class="spark-modal-content"></div>'; | |||
document.body.appendChild(sparkModalOverlay); | |||
sparkModalContent = sparkModalOverlay.querySelector('.spark-modal-content'); | |||
} | |||
} | |||
// 关闭主模态框 | |||
function closeModal() { | function closeModal() { | ||
if (!modalOverlay) return; | |||
modalOverlay.classList.add('fade-out'); | modalOverlay.classList.add('fade-out'); | ||
setTimeout(function() { | setTimeout(function() { | ||
| 第389行: | 第693行: | ||
} | } | ||
// | // 关闭闪光模态框 | ||
function closeSparkModal() { | function closeSparkModal() { | ||
closeEnlargedCard(); | |||
if ( | |||
if (!sparkModalOverlay) return; | |||
sparkModalOverlay.classList.add('fade-out'); | sparkModalOverlay.classList.add('fade-out'); | ||
| 第412行: | 第714行: | ||
} | } | ||
// | // 打开主模态框 | ||
function openModal(cardElement, cardId, sparkEnable) { | function openModal(cardElement, cardId, sparkEnable) { | ||
createModals(); | |||
var cardClone = cardElement.cloneNode(true); | var cardClone = cardElement.cloneNode(true); | ||
modalContent.innerHTML = ''; | modalContent.innerHTML = ''; | ||
modalContent.appendChild(cardClone); | modalContent.appendChild(cardClone); | ||
// | // 如果启用了闪光功能 | ||
if (sparkEnable && sparkEnable.trim() !== '') { | if (sparkEnable && sparkEnable.trim() !== '') { | ||
var sparkButton = document.createElement('button'); | var sparkButton = document.createElement('button'); | ||
| 第427行: | 第728行: | ||
sparkButton.textContent = '闪光'; | sparkButton.textContent = '闪光'; | ||
sparkButton.dataset.cardId = cardId; | sparkButton.dataset.cardId = cardId; | ||
sparkButton. | |||
if (SparkCache.has(cardId)) { | |||
} | sparkButton.classList.add('cached'); | ||
} else { | |||
BatchRequestManager.prioritize(cardId); | |||
} | |||
modalContent.appendChild(sparkButton); | modalContent.appendChild(sparkButton); | ||
} | } | ||
| 第436行: | 第741行: | ||
document.body.classList.add('card-modal-open'); | document.body.classList.add('card-modal-open'); | ||
requestAnimationFrame(function() { | requestAnimationFrame(function() { | ||
modalOverlay.classList.add('fade-in'); | modalOverlay.classList.add('fade-in'); | ||
| 第444行: | 第748行: | ||
// 打开闪光卡牌模态框 | // 打开闪光卡牌模态框 | ||
function openSparkModal(cardId) { | function openSparkModal(cardId) { | ||
createModals(); | |||
var cachedHtml = SparkCache.get(cardId); | |||
sparkModalOverlay.classList.add('active'); | if (cachedHtml) { | ||
requestAnimationFrame(function() { | showSparkContent(cachedHtml, true); | ||
} else { | |||
}); | sparkModalContent.innerHTML = '<div class="spark-modal-title">闪光卡牌列表</div><div class="spark-loading">加载中</div>'; | ||
sparkModalOverlay.classList.add('active'); | |||
requestAnimationFrame(function() { | |||
sparkModalOverlay.classList.add('fade-in'); | |||
}); | |||
BatchRequestManager.request(cardId).then(function(html) { | |||
showSparkContent(html, false); | |||
}).catch(function() { | |||
sparkModalContent.innerHTML = '<div class="spark-modal-title">闪光卡牌列表</div><div class="spark-empty">加载失败,请重试</div>'; | |||
}); | |||
} | |||
} | |||
function showSparkContent(html, fromCache) { | |||
var tempDiv = document.createElement('div'); | |||
tempDiv.innerHTML = html; | |||
var cards = tempDiv.querySelectorAll('.card-deck-trans'); | |||
var cacheHint = fromCache ? '<div class="spark-cache-hint">已从缓存加载</div>' : ''; | |||
if (cards.length > 0) { | |||
sparkModalContent.innerHTML = '<div class="spark-modal-title">闪光卡牌列表</div>' + html + cacheHint; | |||
} else { | |||
sparkModalContent.innerHTML = '<div class="spark-modal-title">闪光卡牌列表</div><div class="spark-empty">暂无闪光卡牌</div>' + cacheHint; | |||
} | |||
if (!sparkModalOverlay.classList.contains('active')) { | |||
sparkModalOverlay.classList.add('active'); | |||
requestAnimationFrame(function() { | |||
sparkModalOverlay.classList.add('fade-in'); | |||
}); | |||
} | |||
} | |||
// 显示放大的闪光卡牌 | |||
function showEnlargedCard(cardElement) { | |||
closeEnlargedCard(); | |||
var enlargedContainer = document.createElement('div'); | |||
enlargedContainer.className = 'spark-enlarged-container'; | |||
var closeBtn = document.createElement('span'); | |||
closeBtn.className = 'spark-enlarged-close'; | |||
closeBtn.innerHTML = '×'; | |||
enlargedContainer.appendChild(closeBtn); | |||
var cardContent = document.createElement('div'); | |||
cardContent.className = 'spark-enlarged-card'; | |||
cardContent.appendChild(cardElement.cloneNode(true)); | |||
enlargedContainer.appendChild(cardContent); | |||
document.body.appendChild(enlargedContainer); | |||
currentEnlargedContainer = enlargedContainer; | |||
} | |||
// 处理新添加的卡牌节点 | |||
function processNewCards(nodes) { | |||
var sparkCardIds = []; | |||
nodes.forEach(function(node) { | |||
if (node.nodeType !== 1) return; | |||
var cards = []; | |||
if (node.classList && node.classList.contains('card-deck-trans')) { | |||
cards.push(node); | |||
} | |||
if (node.querySelectorAll) { | |||
var found = node.querySelectorAll('.card-deck-trans'); | |||
if ( | for (var i = 0; i < found.length; i++) { | ||
cards.push(found[i]); | |||
var | |||
} | } | ||
} | } | ||
cards.forEach(function(card) { | |||
// 跳过已处理的和模态框内的 | |||
if (processedCards.has(card)) return; | |||
if (card.closest('.card-modal-overlay') || | |||
card.closest('.spark-modal-overlay') || | |||
card.closest('.spark-enlarged-container')) return; | |||
processedCards.add(card); | |||
// | // 收集需要预加载的卡牌ID | ||
var sparkEnable = card.dataset.sparkEnable; | |||
if ( | if (sparkEnable && sparkEnable.trim() !== '') { | ||
var wrapper = card.closest('.card-wrapper'); | |||
var cardId = wrapper ? wrapper.dataset.cardId : ''; | |||
if (cardId && sparkCardIds.indexOf(cardId) === -1) { | |||
sparkCardIds.push(cardId); | |||
} | } | ||
} | } | ||
}); | }); | ||
}); | }); | ||
// 延迟预加载 | |||
if (sparkCardIds.length > 0) { | |||
setTimeout(function() { | |||
BatchRequestManager.preload(sparkCardIds); | |||
}, 2000); | |||
} | |||
} | } | ||
// | // 初始化页面上已有的卡牌 | ||
function initExistingCards() { | |||
var cards = document.querySelectorAll('.card-deck-trans'); | |||
var nodeArray = []; | |||
} | cards.forEach(function(card) { | ||
nodeArray.push(card); | |||
}); | |||
processNewCards(nodeArray); | |||
} | |||
return { | |||
createModals: createModals, | |||
closeModal: closeModal, | |||
closeSparkModal: closeSparkModal, | |||
closeEnlargedCard: closeEnlargedCard, | |||
openModal: openModal, | |||
openSparkModal: openSparkModal, | |||
showEnlargedCard: showEnlargedCard, | |||
processNewCards: processNewCards, | |||
initExistingCards: initExistingCards, | |||
getModalOverlay: function() { return modalOverlay; }, | |||
getSparkModalOverlay: function() { return sparkModalOverlay; } | |||
}; | |||
})(); | |||
// ==================== 事件委托管理器 ==================== | |||
var EventManager = (function() { | |||
var hoverTimer = null; | |||
// | // 获取卡牌信息 | ||
function getCardInfo(cardElement) { | |||
var wrapper = cardElement.closest('.card-wrapper'); | |||
return { | |||
} | cardId: wrapper ? wrapper.dataset.cardId : '', | ||
} | sparkEnable: cardElement.dataset.sparkEnable || '' | ||
}; | |||
} | |||
// 检查是否是有效的卡牌点击 | |||
function isValidCardClick(target) { | |||
var card = target.closest('.card-deck-trans'); | |||
if (!card) return null; | |||
} | |||
// 排除模态框内的卡牌(除了闪光列表中的) | |||
var inMainModal = card.closest('.card-modal-content'); | |||
var inSparkList = card.closest('.spark-card-list'); | |||
var inEnlarged = card.closest('.spark-enlarged-container'); | |||
if (inMainModal && !inSparkList) return null; | |||
if (inEnlarged) return null; | |||
return card; | |||
} | |||
// | // 初始化事件监听 | ||
document.addEventListener(' | function init() { | ||
// 使用事件委托处理所有点击事件 | |||
// | document.addEventListener('click', function(e) { | ||
var target = e.target; | |||
// 关闭按钮 | |||
if (target.classList.contains('card-modal-close')) { | |||
e.stopPropagation(); | |||
DOMManager.closeModal(); | |||
return; | |||
} | } | ||
if (target.classList.contains('spark-modal-close')) { | |||
e.stopPropagation(); | |||
DOMManager.closeSparkModal(); | |||
return; | |||
} | |||
if (target.classList.contains('spark-enlarged-close')) { | |||
e.stopPropagation(); | |||
DOMManager.closeEnlargedCard(); | |||
return; | |||
} | |||
// 闪光按钮 | |||
if (target.classList.contains('spark-button')) { | |||
e.preventDefault(); | |||
var cardId = target.dataset.cardId; | |||
if (cardId) { | |||
DOMManager.openSparkModal(cardId); | |||
} | |||
return; | |||
} | |||
// 点击遮罩关闭 | |||
if (target.classList.contains('card-modal-overlay')) { | |||
DOMManager.closeModal(); | |||
return; | |||
} | |||
if (target.classList.contains('spark-modal-overlay')) { | |||
DOMManager.closeSparkModal(); | |||
return; | |||
} | |||
if (target.classList.contains('spark-enlarged-container')) { | |||
DOMManager.closeEnlargedCard(); | |||
return; | |||
} | |||
// 卡牌点击 | |||
var card = isValidCardClick(target); | |||
if (card) { | |||
e.preventDefault(); | |||
var inSparkList = card.closest('.spark-card-list'); | |||
var info = getCardInfo(card); | |||
if (inSparkList) { | |||
// 闪光列表中的卡牌 - 显示放大 | |||
DOMManager.showEnlargedCard(card); | |||
} else { | |||
// 普通卡牌 - 打开模态框 | |||
DOMManager.openModal(card, info.cardId, info.sparkEnable); | |||
} | |||
} | |||
}, true); | |||
// 使用事件委托处理鼠标悬停 | |||
document.addEventListener('mouseenter', function(e) { | |||
var card = e.target.closest && e.target.closest('.card-deck-trans'); | |||
card | if (!card) return; | ||
// 排除模态框内的卡牌 | // 排除模态框内的卡牌 | ||
if (card.closest('.card-modal-overlay') || card.closest('.spark-modal-overlay') || card.closest('.spark-enlarged-container')) return; | if (card.closest('.card-modal-overlay') || | ||
card.closest('.spark-modal-overlay') || | |||
card.closest('.spark-enlarged-container')) return; | |||
card. | var info = getCardInfo(card); | ||
if (info.sparkEnable && info.sparkEnable.trim() !== '' && info.cardId) { | |||
// 清除之前的定时器 | |||
if (hoverTimer) { | |||
clearTimeout(hoverTimer); | |||
} | |||
// | // 延迟触发预加载,避免快速滑过时的无效请求 | ||
hoverTimer = setTimeout(function() { | |||
BatchRequestManager.prioritize(info.cardId); | |||
if (!SparkCache.has(info.cardId)) { | |||
BatchRequestManager.request(info.cardId); | |||
} | |||
}, 150); | |||
} | |||
}, true); | |||
document.addEventListener('mouseleave', function(e) { | |||
var card = e.target.closest && e.target.closest('.card-deck-trans'); | |||
if (card && hoverTimer) { | |||
clearTimeout(hoverTimer); | |||
hoverTimer = null; | |||
} | |||
}, true); | |||
// ESC键关闭 | |||
document.addEventListener('keydown', function(e) { | |||
if (e.key === 'Escape') { | |||
var sparkModal = DOMManager.getSparkModalOverlay(); | |||
var mainModal = DOMManager.getModalOverlay(); | |||
// | // 按层级关闭 | ||
if (document.querySelector('.spark-enlarged-container')) { | |||
DOMManager.closeEnlargedCard(); | |||
} else if (sparkModal && sparkModal.classList.contains('active')) { | |||
DOMManager.closeSparkModal(); | |||
} | } else if (mainModal && mainModal.classList.contains('active')) { | ||
DOMManager.closeModal(); | |||
} | |||
} | |||
}); | |||
// 页面卸载时保存缓存 | |||
window.addEventListener('beforeunload', function() { | |||
SparkCache.flush(); | |||
}); | |||
// 页面可见性变化 | |||
document.addEventListener('visibilitychange', function() { | |||
if (document.hidden) { | |||
SparkCache.flush(); | |||
} | |||
}); | }); | ||
} | } | ||
// | return { | ||
init: init | |||
}; | |||
})(); | |||
// ==================== 初始化 ==================== | |||
function init() { | |||
// 清理过期缓存 | |||
SparkCache.cleanup(); | |||
// 创建模态框 | |||
DOMManager.createModals(); | |||
// 初始化事件监听(事件委托) | |||
EventManager.init(); | |||
// | // 处理已有卡牌 | ||
DOMManager.initExistingCards(); | |||
// 监听DOM变化 - 只处理新添加的节点 | |||
var observer = new MutationObserver(function(mutations) { | var observer = new MutationObserver(function(mutations) { | ||
var | var newNodes = []; | ||
mutations.forEach(function(mutation) { | mutations.forEach(function(mutation) { | ||
if (mutation.addedNodes.length > 0) { | if (mutation.addedNodes.length > 0) { | ||
mutation.addedNodes. | for (var i = 0; i < mutation.addedNodes.length; i++) { | ||
newNodes.push(mutation.addedNodes[i]); | |||
} | |||
} | |||
} | } | ||
}); | }); | ||
if ( | |||
if (newNodes.length > 0) { | |||
// 使用 requestIdleCallback 或 setTimeout 延迟处理 | |||
if (window.requestIdleCallback) { | |||
requestIdleCallback(function() { | |||
DOMManager.processNewCards(newNodes); | |||
}, { timeout: 500 }); | |||
} else { | |||
setTimeout(function() { | |||
DOMManager.processNewCards(newNodes); | |||
}, 100); | |||
} | |||
} | } | ||
}); | }); | ||
| 第625行: | 第1,096行: | ||
subtree: true | subtree: true | ||
}); | }); | ||
} | |||
// 启动 | |||
if (document.readyState === 'loading') { | |||
document.addEventListener('DOMContentLoaded', init); | |||
} else { | |||
init(); | |||
} | } | ||
})(); | })(); | ||
</script> | </script> | ||
</includeonly> | </includeonly> | ||