Common.js:修订间差异
来自卡厄思梦境WIKI
无编辑摘要 |
无编辑摘要 |
||
| 第179行: | 第179行: | ||
if (window.cardSystemInitialized) return; | if (window.cardSystemInitialized) return; | ||
window.cardSystemInitialized = true; | window.cardSystemInitialized = true; | ||
// 缓存DOM元素 | // 缓存DOM元素 | ||
var overlayCache = null; | var overlayCache = null; | ||
var containerCache = null; | var containerCache = null; | ||
// 保存当前卡牌信息,用于返回 | // 保存当前卡牌信息,用于返回 | ||
var currentCardInfo = null; | var currentCardInfo = null; | ||
// 添加历史栈来记录浏览历史 | // 添加历史栈来记录浏览历史 | ||
var viewHistory = []; | var viewHistory = []; | ||
// API缓存 | // API缓存 | ||
var apiCache = {}; | var apiCache = {}; | ||
// 预加载队列 | // 预加载队列 | ||
var preloadQueue = new Set(); | var preloadQueue = new Set(); | ||
var preloadWorker = null; | var preloadWorker = null; | ||
// 添加请求队列控制并发 | |||
var requestQueue = []; | |||
var activeRequests = 0; | |||
var MAX_CONCURRENT_REQUESTS = 3; // 限制最大并发请求数 | |||
// 添加请求超时控制 | |||
var REQUEST_TIMEOUT = 10000; // 10秒超时 | |||
// 智能缓存管理器 | // 智能缓存管理器 | ||
var cacheManager = { | var cacheManager = { | ||
maxSize: 100, | maxSize: 100, | ||
accessTime: {}, | accessTime: {}, | ||
set: function(key, value) { | set: function(key, value) { | ||
apiCache[key] = value; | apiCache[key] = value; | ||
this.accessTime[key] = Date.now(); | this.accessTime[key] = Date.now(); | ||
var keys = Object.keys(apiCache); | var keys = Object.keys(apiCache); | ||
if (keys.length > this.maxSize) { | if (keys.length > this.maxSize) { | ||
var oldestKey = keys[0]; | var oldestKey = keys[0]; | ||
var oldestTime = this.accessTime[oldestKey] || 0; | var oldestTime = this.accessTime[oldestKey] || 0; | ||
for (var i = 1; i < keys.length; i++) { | for (var i = 1; i < keys.length; i++) { | ||
var time = this.accessTime[keys[i]] || 0; | var time = this.accessTime[keys[i]] || 0; | ||
| 第220行: | 第226行: | ||
} | } | ||
} | } | ||
delete apiCache[oldestKey]; | delete apiCache[oldestKey]; | ||
delete this.accessTime[oldestKey]; | delete this.accessTime[oldestKey]; | ||
} | } | ||
}, | }, | ||
get: function(key) { | get: function(key) { | ||
if (apiCache[key]) { | if (apiCache[key]) { | ||
| 第234行: | 第240行: | ||
} | } | ||
}; | }; | ||
// 添加Loading动画CSS | // 添加Loading动画CSS | ||
function addLoadingStyles() { | function addLoadingStyles() { | ||
| 第256行: | 第262行: | ||
} | } | ||
} | } | ||
// 创建loading占位符 | // 创建loading占位符 | ||
function createLoadingPlaceholder(text, scale) { | function createLoadingPlaceholder(text, scale) { | ||
var placeholder = document.createElement('div'); | var placeholder = document.createElement('div'); | ||
placeholder.className = 'card-loading'; | placeholder.className = 'card-loading'; | ||
var baseStyle = 'width: 168px; height: 230px; background: rgba(255,255,255,0.1); border: 2px dashed rgba(255,255,255,0.3); border-radius: 8px; display: flex; align-items: center; justify-content: center; color: white;'; | var baseStyle = 'width: 168px; height: 230px; background: rgba(255,255,255,0.1); border: 2px dashed rgba(255,255,255,0.3); border-radius: 8px; display: flex; align-items: center; justify-content: center; color: white;'; | ||
if (scale) { | if (scale) { | ||
baseStyle += ' transform: scale(' + scale + '); transform-origin: center center;'; | baseStyle += ' transform: scale(' + scale + '); transform-origin: center center;'; | ||
} | } | ||
placeholder.style.cssText = baseStyle; | placeholder.style.cssText = baseStyle; | ||
placeholder.innerHTML = '<div style="text-align: center;">' + | placeholder.innerHTML = '<div style="text-align: center;">' + | ||
| 第275行: | 第281行: | ||
return placeholder; | return placeholder; | ||
} | } | ||
// 预加载管理器 | // 错误占位符 | ||
function createErrorPlaceholder(text, scale) { | |||
var placeholder = document.createElement('div'); | |||
placeholder.className = 'card-error'; | |||
var baseStyle = 'width: 168px; height: 230px; background: rgba(255,0,0,0.1); border: 2px dashed rgba(255,0,0,0.3); border-radius: 8px; display: flex; align-items: center; justify-content: center; color: #ff6b6b;'; | |||
if (scale) { | |||
baseStyle += ' transform: scale(' + scale + '); transform-origin: center center;'; | |||
} | |||
placeholder.style.cssText = baseStyle; | |||
placeholder.innerHTML = '<div style="text-align: center; padding: 10px;">' + | |||
'<div style="font-size: 30px; margin-bottom: 10px;">⚠</div>' + | |||
'<div style="font-size: 12px;">' + (text || '加载失败') + '</div>' + | |||
'</div>'; | |||
return placeholder; | |||
} | |||
// 请求队列处理器 | |||
function processRequestQueue() { | |||
if (requestQueue.length === 0 || activeRequests >= MAX_CONCURRENT_REQUESTS) { | |||
return; | |||
} | |||
var request = requestQueue.shift(); | |||
activeRequests++; | |||
request.execute(function() { | |||
activeRequests--; | |||
processRequestQueue(); // 处理下一个请求 | |||
}); | |||
} | |||
// 添加到请求队列 | |||
function queueRequest(executeFn) { | |||
return new Promise(function(resolve, reject) { | |||
requestQueue.push({ | |||
execute: function(done) { | |||
executeFn() | |||
.then(function(result) { | |||
resolve(result); | |||
done(); | |||
}) | |||
.catch(function(error) { | |||
reject(error); | |||
done(); | |||
}); | |||
} | |||
}); | |||
processRequestQueue(); | |||
}); | |||
} | |||
// 优化的API请求函数,添加超时和错误处理 | |||
function fetchCardHTML(character, cardName, deckType, callback) { | |||
var cacheKey = character + '|' + cardName + '|' + (deckType || ''); | |||
// 检查缓存 | |||
var cached = cacheManager.get(cacheKey); | |||
if (cached) { | |||
callback(cached, null); | |||
return; | |||
} | |||
// 使用请求队列 | |||
queueRequest(function() { | |||
return new Promise(function(resolve, reject) { | |||
var api = new mw.Api(); | |||
var wikitext = '{{#invoke:卡牌|main|' + character + '|' + cardName + '|' + (deckType || '') + '}}'; | |||
var timeoutId = setTimeout(function() { | |||
reject(new Error('请求超时')); | |||
}, REQUEST_TIMEOUT); | |||
api.parse(wikitext) | |||
.done(function(html) { | |||
clearTimeout(timeoutId); | |||
cacheManager.set(cacheKey, html); | |||
resolve(html); | |||
}) | |||
.fail(function(error) { | |||
clearTimeout(timeoutId); | |||
console.error('加载卡牌失败:', character, cardName, deckType, error); | |||
reject(error); | |||
}); | |||
}); | |||
}) | |||
.then(function(html) { | |||
callback(html, null); | |||
}) | |||
.catch(function(error) { | |||
callback(null, error); | |||
}); | |||
} | |||
// 批量获取卡牌HTML - 优化版本 | |||
function fetchCardHTMLBatch(requests, callback) { | |||
var results = {}; | |||
var errors = {}; | |||
var pending = []; | |||
// 先检查缓存 | |||
requests.forEach(function(req) { | |||
var cacheKey = req.character + '|' + req.cardName + '|' + (req.deckType || ''); | |||
var cached = cacheManager.get(cacheKey); | |||
if (cached) { | |||
results[cacheKey] = cached; | |||
} else { | |||
pending.push(req); | |||
} | |||
}); | |||
// 如果全部都有缓存,直接返回 | |||
if (pending.length === 0) { | |||
callback(results, errors); | |||
return; | |||
} | |||
// 限制批量请求数量,分批处理 | |||
var batchSize = 5; | |||
var batches = []; | |||
for (var i = 0; i < pending.length; i += batchSize) { | |||
batches.push(pending.slice(i, i + batchSize)); | |||
} | |||
var currentBatch = 0; | |||
function processBatch() { | |||
if (currentBatch >= batches.length) { | |||
callback(results, errors); | |||
return; | |||
} | |||
var batch = batches[currentBatch]; | |||
var completed = 0; | |||
batch.forEach(function(req) { | |||
fetchCardHTML(req.character, req.cardName, req.deckType, function(html, error) { | |||
var cacheKey = req.character + '|' + req.cardName + '|' + (req.deckType || ''); | |||
if (error) { | |||
errors[cacheKey] = error; | |||
} else { | |||
results[cacheKey] = html; | |||
} | |||
completed++; | |||
if (completed === batch.length) { | |||
currentBatch++; | |||
// 延迟处理下一批,避免请求过快 | |||
setTimeout(processBatch, 200); | |||
} | |||
}); | |||
}); | |||
} | |||
processBatch(); | |||
} | |||
// 预加载管理器 - 降低优先级 | |||
function preloadCardData(character, cardName, deckType) { | function preloadCardData(character, cardName, deckType) { | ||
var cacheKey = character + '|' + cardName + '|' + (deckType || ''); | var cacheKey = character + '|' + cardName + '|' + (deckType || ''); | ||
if (!cacheManager.get(cacheKey) && !preloadQueue.has(cacheKey)) { | if (!cacheManager.get(cacheKey) && !preloadQueue.has(cacheKey)) { | ||
preloadQueue.add(cacheKey); | preloadQueue.add(cacheKey); | ||
if (!preloadWorker) { | if (!preloadWorker) { | ||
preloadWorker = setTimeout(processPreloadQueue, | preloadWorker = setTimeout(processPreloadQueue, 500); // 增加延迟 | ||
} | } | ||
} | } | ||
} | } | ||
// 批量处理预加载队列 | // 批量处理预加载队列 - 降低频率 | ||
function processPreloadQueue() { | function processPreloadQueue() { | ||
preloadWorker = null; | preloadWorker = null; | ||
if (preloadQueue.size === 0) return; | if (preloadQueue.size === 0) return; | ||
var batch = Array.from(preloadQueue).slice(0, | var batch = Array.from(preloadQueue).slice(0, 2); // 减少每批数量 | ||
batch.forEach(function(key) { | batch.forEach(function(key) { | ||
preloadQueue.delete(key); | preloadQueue.delete(key); | ||
}); | }); | ||
batch.forEach(function(cacheKey) { | batch.forEach(function(cacheKey) { | ||
var parts = cacheKey.split('|'); | var parts = cacheKey.split('|'); | ||
| 第306行: | 第474行: | ||
}); | }); | ||
}); | }); | ||
// 如果还有剩余,继续处理 | // 如果还有剩余,继续处理 | ||
if (preloadQueue.size > 0) { | if (preloadQueue.size > 0) { | ||
preloadWorker = setTimeout(processPreloadQueue, | preloadWorker = setTimeout(processPreloadQueue, 1000); // 增加间隔 | ||
} | } | ||
} | } | ||
// 创建遮罩层和容器 | // 创建遮罩层和容器 | ||
function createCardOverlay() { | function createCardOverlay() { | ||
| 第318行: | 第486行: | ||
return { overlay: overlayCache, container: containerCache }; | return { overlay: overlayCache, container: containerCache }; | ||
} | } | ||
var overlay = document.createElement('div'); | var overlay = document.createElement('div'); | ||
overlay.id = 'card-overlay'; | overlay.id = 'card-overlay'; | ||
overlay.style.cssText = 'display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); z-index: 9999; overflow-y: auto;'; | overlay.style.cssText = 'display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); z-index: 9999; overflow-y: auto;'; | ||
var container = document.createElement('div'); | var container = document.createElement('div'); | ||
container.id = 'card-display-container'; | container.id = 'card-display-container'; | ||
| 第330行: | 第496行: | ||
overlay.appendChild(container); | overlay.appendChild(container); | ||
document.body.appendChild(overlay); | document.body.appendChild(overlay); | ||
overlay.addEventListener('click', function(e) { | overlay.addEventListener('click', function(e) { | ||
if (e.target === overlay) { | if (e.target === overlay) { | ||
| 第337行: | 第502行: | ||
} | } | ||
}); | }); | ||
overlayCache = overlay; | overlayCache = overlay; | ||
containerCache = container; | containerCache = container; | ||
return { overlay: overlay, container: container }; | return { overlay: overlay, container: container }; | ||
} | } | ||
// 创建关闭按钮 | // 创建关闭按钮 | ||
function createCloseButton() { | function createCloseButton() { | ||
| 第362行: | 第527行: | ||
return closeBtn; | return closeBtn; | ||
} | } | ||
// 关闭卡牌展示 | // 关闭卡牌展示 | ||
function closeCardDisplay() { | function closeCardDisplay() { | ||
| 第374行: | 第539行: | ||
} | } | ||
currentCardInfo = null; | currentCardInfo = null; | ||
viewHistory = []; | viewHistory = []; | ||
} | } | ||
// 返回上一层 | // 返回上一层 | ||
function goBack() { | function goBack() { | ||
if (viewHistory.length > 1) { | if (viewHistory.length > 1) { | ||
viewHistory.pop(); | viewHistory.pop(); | ||
var previousView = viewHistory[viewHistory.length - 1]; | var previousView = viewHistory[viewHistory.length - 1]; | ||
if (previousView.type === 'enlarged') { | if (previousView.type === 'enlarged') { | ||
showEnlargedCard(previousView.data.element, -1); | showEnlargedCard(previousView.data.element, -1); | ||
} else if (previousView.type === 'derivedCards') { | } else if (previousView.type === 'derivedCards') { | ||
showAllDerivedCards(previousView.data.character, previousView.data.derivedCardsList, -1); | showAllDerivedCards(previousView.data.character, previousView.data.derivedCardsList, -1); | ||
| 第393行: | 第556行: | ||
} | } | ||
} else if (viewHistory.length === 1) { | } else if (viewHistory.length === 1) { | ||
closeCardDisplay(); | closeCardDisplay(); | ||
} else { | } else { | ||
closeCardDisplay(); | closeCardDisplay(); | ||
} | } | ||
} | } | ||
// 检查卡牌变体 - 优化版本,添加错误处理 | |||
// 检查卡牌变体 | |||
function checkCardVariants(character, cardName, callback) { | function checkCardVariants(character, cardName, callback) { | ||
var variants = { lingguang: false, shenguang: false }; | var variants = { lingguang: false, shenguang: false }; | ||
var checkCount = 0; | var checkCount = 0; | ||
var totalChecks = 2; | var totalChecks = 2; | ||
function checkComplete() { | function checkComplete() { | ||
checkCount++; | checkCount++; | ||
| 第473行: | 第574行: | ||
} | } | ||
} | } | ||
fetchCardHTML(character, cardName, '灵光一闪', function(html, error) { | |||
fetchCardHTML(character, cardName, '灵光一闪', function(html) { | if (!error && html && !html.includes('找不到卡组')) { | ||
if (html && !html.includes('找不到卡组')) { | |||
variants.lingguang = true; | variants.lingguang = true; | ||
} | } | ||
checkComplete(); | checkComplete(); | ||
}); | }); | ||
fetchCardHTML(character, cardName, '神之一闪', function(html) { | fetchCardHTML(character, cardName, '神之一闪', function(html, error) { | ||
if (html && !html.includes('找不到卡组')) { | if (!error && html && !html.includes('找不到卡组')) { | ||
variants.shenguang = true; | variants.shenguang = true; | ||
} | } | ||
| 第489行: | 第589行: | ||
}); | }); | ||
} | } | ||
// 放大显示卡牌 | // 放大显示卡牌 - 添加错误处理 | ||
function showEnlargedCard(cardElement, addToHistory) { | function showEnlargedCard(cardElement, addToHistory) { | ||
var elements = createCardOverlay(); | var elements = createCardOverlay(); | ||
| 第499行: | 第599行: | ||
var derivedCards = cardElement.dataset.derivedCards; | var derivedCards = cardElement.dataset.derivedCards; | ||
var hasMechanism = cardElement.dataset.hasMechanism === 'true'; | var hasMechanism = cardElement.dataset.hasMechanism === 'true'; | ||
addLoadingStyles(); | addLoadingStyles(); | ||
currentCardInfo = { | currentCardInfo = { | ||
element: cardElement, | element: cardElement, | ||
| 第511行: | 第609行: | ||
derivedCards: derivedCards | derivedCards: derivedCards | ||
}; | }; | ||
if (addToHistory === -1) { | if (addToHistory === -1) { | ||
if (viewHistory.length > 0) { | if (viewHistory.length > 0) { | ||
viewHistory[viewHistory.length - 1] = { | viewHistory[viewHistory.length - 1] = { | ||
| 第522行: | 第618行: | ||
} | } | ||
} else if (addToHistory !== false) { | } else if (addToHistory !== false) { | ||
viewHistory.push({ | viewHistory.push({ | ||
type: 'enlarged', | type: 'enlarged', | ||
| 第528行: | 第623行: | ||
}); | }); | ||
} | } | ||
container.style.cssText = 'position: relative; min-height: 100vh; padding: 40px 20px; display: flex; align-items: center; justify-content: center;'; | container.style.cssText = 'position: relative; min-height: 100vh; padding: 40px 20px; display: flex; align-items: center; justify-content: center;'; | ||
var contentWrapper = document.createElement('div'); | var contentWrapper = document.createElement('div'); | ||
contentWrapper.style.cssText = 'display: flex; align-items: flex-start; gap: 0px; position: relative;'; | contentWrapper.style.cssText = 'display: flex; align-items: flex-start; gap: 0px; position: relative;'; | ||
var centerContainer = document.createElement('div'); | var centerContainer = document.createElement('div'); | ||
centerContainer.style.cssText = 'display: flex; flex-direction: column; align-items: center; gap: 0px;'; | centerContainer.style.cssText = 'display: flex; flex-direction: column; align-items: center; gap: 0px;'; | ||
var mainCardContainer = document.createElement('div'); | var mainCardContainer = document.createElement('div'); | ||
mainCardContainer.style.cssText = 'width: 336px; height: 460px; display: flex; align-items: center; justify-content: center; position: relative;'; | mainCardContainer.style.cssText = 'width: 336px; height: 460px; display: flex; align-items: center; justify-content: center; position: relative;'; | ||
var cardInner = document.createElement('div'); | var cardInner = document.createElement('div'); | ||
cardInner.style.cssText = 'transform: scale(2); position: relative;'; | cardInner.style.cssText = 'transform: scale(2); position: relative;'; | ||
var enlargedCard = cardElement.cloneNode(true); | var enlargedCard = cardElement.cloneNode(true); | ||
enlargedCard.style.width = '168px'; | enlargedCard.style.width = '168px'; | ||
| 第554行: | 第646行: | ||
enlargedCard.onclick = null; | enlargedCard.onclick = null; | ||
enlargedCard.classList.add('card-fade-in'); | enlargedCard.classList.add('card-fade-in'); | ||
cardInner.appendChild(enlargedCard); | cardInner.appendChild(enlargedCard); | ||
mainCardContainer.appendChild(cardInner); | mainCardContainer.appendChild(cardInner); | ||
centerContainer.appendChild(mainCardContainer); | centerContainer.appendChild(mainCardContainer); | ||
// 处理衍生卡牌 | // 处理衍生卡牌 - 添加错误处理 | ||
if (derivedCards && derivedCards.trim() !== '') { | if (derivedCards && derivedCards.trim() !== '') { | ||
var leftContainer = document.createElement('div'); | var leftContainer = document.createElement('div'); | ||
leftContainer.style.cssText = 'display: flex; flex-direction: column; gap: 0px; align-items: center;'; | leftContainer.style.cssText = 'display: flex; flex-direction: column; gap: 0px; align-items: center;'; | ||
var derivedCardsList = derivedCards.split('、'); | var derivedCardsList = derivedCards.split('、'); | ||
var derivedCardWrapper = document.createElement('div'); | var derivedCardWrapper = document.createElement('div'); | ||
derivedCardWrapper.style.cssText = 'width: 252px; height: 345px; display: flex; align-items: center; justify-content: center;'; | derivedCardWrapper.style.cssText = 'width: 252px; height: 345px; display: flex; align-items: center; justify-content: center;'; | ||
var placeholder = createLoadingPlaceholder('加载衍生卡牌...', 1.5); | var placeholder = createLoadingPlaceholder('加载衍生卡牌...', 1.5); | ||
derivedCardWrapper.appendChild(placeholder); | derivedCardWrapper.appendChild(placeholder); | ||
leftContainer.appendChild(derivedCardWrapper); | leftContainer.appendChild(derivedCardWrapper); | ||
fetchCardHTML(character, derivedCardsList[0].trim(), '', function(html, error) { | |||
fetchCardHTML(character, derivedCardsList[0].trim(), '', function(html) { | if (error || !html) { | ||
var errorPlaceholder = createErrorPlaceholder('加载失败', 1.5); | |||
derivedCardWrapper.removeChild(placeholder); | |||
derivedCardWrapper.appendChild(errorPlaceholder); | |||
return; | |||
} | |||
var firstDerivedCard = document.createElement('div'); | var firstDerivedCard = document.createElement('div'); | ||
firstDerivedCard.id = 'derived-cards-display'; | firstDerivedCard.id = 'derived-cards-display'; | ||
firstDerivedCard.style.cssText = 'transform: scale(1.5); transform-origin: center center; opacity: 0;'; | firstDerivedCard.style.cssText = 'transform: scale(1.5); transform-origin: center center; opacity: 0;'; | ||
firstDerivedCard.innerHTML = html; | firstDerivedCard.innerHTML = html; | ||
var cards = firstDerivedCard.querySelectorAll('.game-card'); | var cards = firstDerivedCard.querySelectorAll('.game-card'); | ||
cards.forEach(function(card) { | cards.forEach(function(card) { | ||
| 第589行: | 第686行: | ||
}; | }; | ||
}); | }); | ||
derivedCardWrapper.removeChild(placeholder); | derivedCardWrapper.removeChild(placeholder); | ||
derivedCardWrapper.appendChild(firstDerivedCard); | derivedCardWrapper.appendChild(firstDerivedCard); | ||
setTimeout(function() { | setTimeout(function() { | ||
firstDerivedCard.style.transition = 'opacity 0.3s'; | firstDerivedCard.style.transition = 'opacity 0.3s'; | ||
| 第599行: | 第695行: | ||
}, 10); | }, 10); | ||
}); | }); | ||
if (derivedCardsList.length > 1) { | if (derivedCardsList.length > 1) { | ||
var viewAllBtn = document.createElement('div'); | var viewAllBtn = document.createElement('div'); | ||
| 第612行: | 第707行: | ||
leftContainer.appendChild(viewAllBtn); | leftContainer.appendChild(viewAllBtn); | ||
} | } | ||
contentWrapper.appendChild(leftContainer); | contentWrapper.appendChild(leftContainer); | ||
} | } | ||
var buttonsContainer = document.createElement('div'); | var buttonsContainer = document.createElement('div'); | ||
buttonsContainer.style.cssText = 'display: flex; gap: 15px; white-space: nowrap; min-height: 50px; align-items: center; justify-content: center;'; | buttonsContainer.style.cssText = 'display: flex; gap: 15px; white-space: nowrap; min-height: 50px; align-items: center; justify-content: center;'; | ||
var checkingVariants = document.createElement('div'); | var checkingVariants = document.createElement('div'); | ||
checkingVariants.style.cssText = 'color: rgba(255,255,255,0.5); font-size: 12px;'; | checkingVariants.style.cssText = 'color: rgba(255,255,255,0.5); font-size: 12px;'; | ||
| 第626行: | 第719行: | ||
buttonsContainer.appendChild(checkingVariants); | buttonsContainer.appendChild(checkingVariants); | ||
centerContainer.appendChild(buttonsContainer); | centerContainer.appendChild(buttonsContainer); | ||
checkCardVariants(character, cardName, function(variants) { | checkCardVariants(character, cardName, function(variants) { | ||
buttonsContainer.removeChild(checkingVariants); | if (buttonsContainer.contains(checkingVariants)) { | ||
buttonsContainer.removeChild(checkingVariants); | |||
} | |||
if (variants.lingguang) { | if (variants.lingguang) { | ||
var lingguangBtn = document.createElement('div'); | var lingguangBtn = document.createElement('div'); | ||
| 第641行: | 第735行: | ||
}; | }; | ||
buttonsContainer.appendChild(lingguangBtn); | buttonsContainer.appendChild(lingguangBtn); | ||
setTimeout(function() { | setTimeout(function() { | ||
lingguangBtn.style.transition = 'opacity 0.3s'; | lingguangBtn.style.transition = 'opacity 0.3s'; | ||
| 第648行: | 第741行: | ||
}, 10); | }, 10); | ||
} | } | ||
if (variants.shenguang) { | if (variants.shenguang) { | ||
var shenguangBtn = document.createElement('div'); | var shenguangBtn = document.createElement('div'); | ||
| 第659行: | 第752行: | ||
}; | }; | ||
buttonsContainer.appendChild(shenguangBtn); | buttonsContainer.appendChild(shenguangBtn); | ||
setTimeout(function() { | setTimeout(function() { | ||
shenguangBtn.style.transition = 'opacity 0.3s'; | shenguangBtn.style.transition = 'opacity 0.3s'; | ||
| 第666行: | 第758行: | ||
}, 10); | }, 10); | ||
} | } | ||
if (!variants.lingguang && !variants.shenguang) { | if (!variants.lingguang && !variants.shenguang) { | ||
centerContainer.removeChild(buttonsContainer); | centerContainer.removeChild(buttonsContainer); | ||
} | } | ||
}); | }); | ||
contentWrapper.appendChild(centerContainer); | contentWrapper.appendChild(centerContainer); | ||
if (hasMechanism) { | if (hasMechanism) { | ||
var mechanismContainer = cardElement.nextElementSibling; | var mechanismContainer = cardElement.nextElementSibling; | ||
| 第683行: | 第773行: | ||
mechanismDisplay.innerHTML = mechanismContainer.innerHTML; | mechanismDisplay.innerHTML = mechanismContainer.innerHTML; | ||
mechanismDisplay.style.display = 'flex'; | mechanismDisplay.style.display = 'flex'; | ||
contentWrapper.appendChild(mechanismDisplay); | contentWrapper.appendChild(mechanismDisplay); | ||
} | } | ||
} | } | ||
container.innerHTML = ''; | container.innerHTML = ''; | ||
container.appendChild(contentWrapper); | container.appendChild(contentWrapper); | ||
if (viewHistory.length > 1) { | if (viewHistory.length > 1) { | ||
var backBtn = document.createElement('div'); | var backBtn = document.createElement('div'); | ||
| 第703行: | 第792行: | ||
container.appendChild(backBtn); | container.appendChild(backBtn); | ||
} | } | ||
container.appendChild(createCloseButton()); | container.appendChild(createCloseButton()); | ||
elements.overlay.style.display = 'block'; | elements.overlay.style.display = 'block'; | ||
} | } | ||
// 显示所有衍生卡牌 | // 显示所有衍生卡牌 - 添加错误处理 | ||
function showAllDerivedCards(character, derivedCardsList, addToHistory) { | function showAllDerivedCards(character, derivedCardsList, addToHistory) { | ||
var overlay = document.getElementById('card-overlay'); | var overlay = document.getElementById('card-overlay'); | ||
var container = document.getElementById('card-display-container'); | var container = document.getElementById('card-display-container'); | ||
if (addToHistory === -1) { | if (addToHistory === -1) { | ||
if (viewHistory.length > 0) { | if (viewHistory.length > 0) { | ||
viewHistory[viewHistory.length - 1] = { | viewHistory[viewHistory.length - 1] = { | ||
| 第727行: | 第814行: | ||
} | } | ||
} else if (addToHistory === true) { | } else if (addToHistory === true) { | ||
viewHistory.push({ | viewHistory.push({ | ||
type: 'derivedCards', | type: 'derivedCards', | ||
| 第736行: | 第822行: | ||
}); | }); | ||
} else if (addToHistory === 0) { | } else if (addToHistory === 0) { | ||
if (viewHistory.length > 0) { | if (viewHistory.length > 0) { | ||
viewHistory.pop(); | viewHistory.pop(); | ||
| 第748行: | 第833行: | ||
}); | }); | ||
} | } | ||
container.innerHTML = ''; | container.innerHTML = ''; | ||
container.style.cssText = 'position: relative; padding: 40px; min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center;'; | container.style.cssText = 'position: relative; padding: 40px; min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center;'; | ||
var title = document.createElement('div'); | var title = document.createElement('div'); | ||
title.style.cssText = 'color: white; font-size: 28px; font-weight: bold; margin-bottom: 30px; text-align: center;'; | title.style.cssText = 'color: white; font-size: 28px; font-weight: bold; margin-bottom: 30px; text-align: center;'; | ||
title.textContent = '衍生卡牌'; | title.textContent = '衍生卡牌'; | ||
container.appendChild(title); | container.appendChild(title); | ||
var cardsContainer = document.createElement('div'); | var cardsContainer = document.createElement('div'); | ||
cardsContainer.style.cssText = 'display: flex; flex-wrap: wrap; gap: 30px; justify-content: center; max-width: 1400px; transform: scale(1.5); transform-origin: center center; padding: 40px;'; | cardsContainer.style.cssText = 'display: flex; flex-wrap: wrap; gap: 30px; justify-content: center; max-width: 1400px; transform: scale(1.5); transform-origin: center center; padding: 40px;'; | ||
var uniqueCards = [...new Set(derivedCardsList.map(function(name) { return name.trim(); }))]; | var uniqueCards = [...new Set(derivedCardsList.map(function(name) { return name.trim(); }))]; | ||
var placeholders = []; | var placeholders = []; | ||
uniqueCards.forEach(function() { | uniqueCards.forEach(function() { | ||
var placeholder = createLoadingPlaceholder('加载中...', null); | var placeholder = createLoadingPlaceholder('加载中...', null); | ||
| 第772行: | 第853行: | ||
cardsContainer.appendChild(placeholder); | cardsContainer.appendChild(placeholder); | ||
}); | }); | ||
container.appendChild(cardsContainer); | container.appendChild(cardsContainer); | ||
var requests = uniqueCards.map(function(cardName) { | var requests = uniqueCards.map(function(cardName) { | ||
return { character: character, cardName: cardName, deckType: '' }; | return { character: character, cardName: cardName, deckType: '' }; | ||
}); | }); | ||
fetchCardHTMLBatch(requests, function(results) { | fetchCardHTMLBatch(requests, function(results, errors) { | ||
uniqueCards.forEach(function(cardName, index) { | uniqueCards.forEach(function(cardName, index) { | ||
var cacheKey = character + '|' + cardName + '|'; | var cacheKey = character + '|' + cardName + '|'; | ||
if (errors[cacheKey]) { | |||
var errorPlaceholder = createErrorPlaceholder('加载失败', null); | |||
cardsContainer.replaceChild(errorPlaceholder, placeholders[index]); | |||
return; | |||
} | |||
var html = results[cacheKey]; | var html = results[cacheKey]; | ||
if (html) { | if (html) { | ||
var cardWrapper = document.createElement('div'); | var cardWrapper = document.createElement('div'); | ||
cardWrapper.style.cssText = 'opacity: 0;'; | cardWrapper.style.cssText = 'opacity: 0;'; | ||
cardWrapper.innerHTML = html; | cardWrapper.innerHTML = html; | ||
var cards = cardWrapper.querySelectorAll('.game-card'); | var cards = cardWrapper.querySelectorAll('.game-card'); | ||
cards.forEach(function(card) { | cards.forEach(function(card) { | ||
| 第797行: | 第884行: | ||
e.preventDefault(); | e.preventDefault(); | ||
}; | }; | ||
var derivedCards = card.dataset.derivedCards; | var derivedCards = card.dataset.derivedCards; | ||
if (derivedCards && derivedCards.trim() !== '') { | if (derivedCards && derivedCards.trim() !== '') { | ||
| 第815行: | 第901行: | ||
viewDerivedBtn.onclick = function(e) { | viewDerivedBtn.onclick = function(e) { | ||
e.stopPropagation(); | e.stopPropagation(); | ||
showAllDerivedCards(character, derivedCards.split('、'), 0); | showAllDerivedCards(character, derivedCards.split('、'), 0); | ||
}; | }; | ||
| 第821行: | 第906行: | ||
buttonContainer.appendChild(viewDerivedBtn); | buttonContainer.appendChild(viewDerivedBtn); | ||
cardsContainer.replaceChild(buttonContainer, placeholders[index]); | cardsContainer.replaceChild(buttonContainer, placeholders[index]); | ||
setTimeout(function() { | setTimeout(function() { | ||
buttonContainer.style.transition = 'opacity 0.3s'; | buttonContainer.style.transition = 'opacity 0.3s'; | ||
| 第830行: | 第913行: | ||
}, 10); | }, 10); | ||
} else { | } else { | ||
cardsContainer.replaceChild(card, placeholders[index]); | cardsContainer.replaceChild(card, placeholders[index]); | ||
setTimeout(function() { | setTimeout(function() { | ||
card.style.transition = 'opacity 0.3s'; | card.style.transition = 'opacity 0.3s'; | ||
| 第844行: | 第925行: | ||
}); | }); | ||
var backBtn = document.createElement('div'); | var backBtn = document.createElement('div'); | ||
backBtn.style.cssText = 'position: fixed; top: 20px; left: 20px; padding: 10px 20px; background: linear-gradient(135deg, #4a90e2, #357abd); color: white; border-radius: 6px; cursor: pointer; font-weight: bold; box-shadow: 0 2px 8px rgba(0,0,0,0.3); z-index: 10001; transition: transform 0.2s;'; | backBtn.style.cssText = 'position: fixed; top: 20px; left: 20px; padding: 10px 20px; background: linear-gradient(135deg, #4a90e2, #357abd); color: white; border-radius: 6px; cursor: pointer; font-weight: bold; box-shadow: 0 2px 8px rgba(0,0,0,0.3); z-index: 10001; transition: transform 0.2s;'; | ||
| 第855行: | 第935行: | ||
container.appendChild(backBtn); | container.appendChild(backBtn); | ||
container.appendChild(createCloseButton()); | container.appendChild(createCloseButton()); | ||
} | } | ||
// 显示变体卡牌 | // 显示变体卡牌 - 添加错误处理 | ||
function showVariantCards(character, cardName, variantType, addToHistory) { | function showVariantCards(character, cardName, variantType, addToHistory) { | ||
var overlay = document.getElementById('card-overlay'); | var overlay = document.getElementById('card-overlay'); | ||
var container = document.getElementById('card-display-container'); | var container = document.getElementById('card-display-container'); | ||
if (addToHistory === -1) { | if (addToHistory === -1) { | ||
if (viewHistory.length > 0) { | if (viewHistory.length > 0) { | ||
viewHistory[viewHistory.length - 1] = { | viewHistory[viewHistory.length - 1] = { | ||
| 第878行: | 第955行: | ||
} | } | ||
} else if (addToHistory === true) { | } else if (addToHistory === true) { | ||
viewHistory.push({ | viewHistory.push({ | ||
type: 'variantCards', | type: 'variantCards', | ||
| 第889行: | 第965行: | ||
} | } | ||
container.innerHTML = ''; | container.innerHTML = ''; | ||
container.style.cssText = 'position: relative; padding: 40px; min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center;'; | container.style.cssText = 'position: relative; padding: 40px; min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center;'; | ||
var title = document.createElement('div'); | var title = document.createElement('div'); | ||
title.style.cssText = 'color: white; font-size: 28px; font-weight: bold; margin-bottom: 30px; text-align: center; background: linear-gradient(135deg, #667eea, #764ba2); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;'; | title.style.cssText = 'color: white; font-size: 28px; font-weight: bold; margin-bottom: 30px; text-align: center; background: linear-gradient(135deg, #667eea, #764ba2); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;'; | ||
| 第899行: | 第973行: | ||
container.appendChild(title); | container.appendChild(title); | ||
var cardsContainer = document.createElement('div'); | var cardsContainer = document.createElement('div'); | ||
cardsContainer.style.cssText = 'display: flex; flex-wrap: nowrap; gap: 0px; justify-content: center; max-width: 1400px; overflow-x: auto; padding: 40px; transform: scale(1.5); transform-origin: center center;'; | cardsContainer.style.cssText = 'display: flex; flex-wrap: nowrap; gap: 0px; justify-content: center; max-width: 1400px; overflow-x: auto; padding: 40px; transform: scale(1.5); transform-origin: center center;'; | ||
var loadingPlaceholder = createLoadingPlaceholder('加载变体卡牌...', null); | var loadingPlaceholder = createLoadingPlaceholder('加载变体卡牌...', null); | ||
cardsContainer.appendChild(loadingPlaceholder); | cardsContainer.appendChild(loadingPlaceholder); | ||
container.appendChild(cardsContainer); | container.appendChild(cardsContainer); | ||
fetchCardHTML(character, cardName, variantType, function(html, error) { | |||
fetchCardHTML(character, cardName, variantType, function(html) { | if (error || !html) { | ||
cardsContainer.removeChild(loadingPlaceholder); | |||
var errorPlaceholder = createErrorPlaceholder('加载变体失败', null); | |||
cardsContainer.appendChild(errorPlaceholder); | |||
return; | |||
} | |||
var tempContainer = document.createElement('div'); | var tempContainer = document.createElement('div'); | ||
tempContainer.innerHTML = html; | tempContainer.innerHTML = html; | ||
cardsContainer.removeChild(loadingPlaceholder); | cardsContainer.removeChild(loadingPlaceholder); | ||
var cards = tempContainer.querySelectorAll('.game-card'); | var cards = tempContainer.querySelectorAll('.game-card'); | ||
| 第925行: | 第1,001行: | ||
card.style.opacity = '0'; | card.style.opacity = '0'; | ||
var derivedCards = card.getAttribute('data-derived-cards'); | var derivedCards = card.getAttribute('data-derived-cards'); | ||
var hasDerivedCards = false; | var hasDerivedCards = false; | ||
if (derivedCards !== null && derivedCards !== undefined) { | if (derivedCards !== null && derivedCards !== undefined) { | ||
| 第942行: | 第1,016行: | ||
if (hasDerivedCards) { | if (hasDerivedCards) { | ||
var cardWrapper = document.createElement('div'); | var cardWrapper = document.createElement('div'); | ||
cardWrapper.style.cssText = 'display: flex; flex-direction: column; align-items: center; gap: 10px; flex-shrink: 0; opacity: 0;'; | cardWrapper.style.cssText = 'display: flex; flex-direction: column; align-items: center; gap: 10px; flex-shrink: 0; opacity: 0;'; | ||
var cardClone = card.cloneNode(true); | var cardClone = card.cloneNode(true); | ||
cardClone.style.cursor = 'default'; | cardClone.style.cursor = 'default'; | ||
| 第953行: | 第1,025行: | ||
cardWrapper.appendChild(cardClone); | cardWrapper.appendChild(cardClone); | ||
var viewDerivedBtn = document.createElement('div'); | var viewDerivedBtn = document.createElement('div'); | ||
viewDerivedBtn.style.cssText = 'padding: 8px 15px; background: linear-gradient(135deg, #4a90e2, #357abd); color: white; border-radius: 6px; cursor: pointer; white-space: nowrap; font-weight: bold; box-shadow: 0 2px 8px rgba(0,0,0,0.3); transition: transform 0.2s; font-size: 12px;'; | viewDerivedBtn.style.cssText = 'padding: 8px 15px; background: linear-gradient(135deg, #4a90e2, #357abd); color: white; border-radius: 6px; cursor: pointer; white-space: nowrap; font-weight: bold; box-shadow: 0 2px 8px rgba(0,0,0,0.3); transition: transform 0.2s; font-size: 12px;'; | ||
| 第960行: | 第1,031行: | ||
viewDerivedBtn.onmouseout = function() { this.style.transform = 'scale(1)'; }; | viewDerivedBtn.onmouseout = function() { this.style.transform = 'scale(1)'; }; | ||
viewDerivedBtn.setAttribute('data-derived-list', derivedCards); | viewDerivedBtn.setAttribute('data-derived-list', derivedCards); | ||
viewDerivedBtn.onclick = function(e) { | viewDerivedBtn.onclick = function(e) { | ||
e.stopPropagation(); | e.stopPropagation(); | ||
var derivedList = this.getAttribute('data-derived-list'); | var derivedList = this.getAttribute('data-derived-list'); | ||
showAllDerivedCards(character, derivedList.split('、'), true); | showAllDerivedCards(character, derivedList.split('、'), true); | ||
}; | }; | ||
| 第972行: | 第1,041行: | ||
cardsContainer.appendChild(cardWrapper); | cardsContainer.appendChild(cardWrapper); | ||
setTimeout(function() { | setTimeout(function() { | ||
cardWrapper.style.transition = 'opacity 0.3s'; | cardWrapper.style.transition = 'opacity 0.3s'; | ||
| 第978行: | 第1,046行: | ||
}, 50 * index); | }, 50 * index); | ||
} else { | } else { | ||
cardsContainer.appendChild(card); | cardsContainer.appendChild(card); | ||
setTimeout(function() { | setTimeout(function() { | ||
card.style.transition = 'opacity 0.3s'; | card.style.transition = 'opacity 0.3s'; | ||
| 第990行: | 第1,056行: | ||
}); | }); | ||
var backBtn = document.createElement('div'); | var backBtn = document.createElement('div'); | ||
backBtn.style.cssText = 'position: fixed; top: 20px; left: 20px; padding: 10px 20px; background: linear-gradient(135deg, #4a90e2, #357abd); color: white; border-radius: 6px; cursor: pointer; font-weight: bold; box-shadow: 0 2px 8px rgba(0,0,0,0.3); z-index: 10001; transition: transform 0.2s;'; | backBtn.style.cssText = 'position: fixed; top: 20px; left: 20px; padding: 10px 20px; background: linear-gradient(135deg, #4a90e2, #357abd); color: white; border-radius: 6px; cursor: pointer; font-weight: bold; box-shadow: 0 2px 8px rgba(0,0,0,0.3); z-index: 10001; transition: transform 0.2s;'; | ||
| 第1,001行: | 第1,066行: | ||
container.appendChild(backBtn); | container.appendChild(backBtn); | ||
container.appendChild(createCloseButton()); | container.appendChild(createCloseButton()); | ||
} | } | ||
// 鼠标悬停时预加载 | // 鼠标悬停时预加载 - 降低优先级,避免影响主流程 | ||
function initPreloadEvents() { | function initPreloadEvents() { | ||
var preloadTimeout = null; | var preloadTimeout = null; | ||
| 第1,012行: | 第1,076行: | ||
var card = e.target.closest('.game-card'); | var card = e.target.closest('.game-card'); | ||
if (card && !card.closest('#card-overlay')) { | if (card && !card.closest('#card-overlay')) { | ||
clearTimeout(preloadTimeout); | clearTimeout(preloadTimeout); | ||
preloadTimeout = setTimeout(function() { | preloadTimeout = setTimeout(function() { | ||
| 第1,021行: | 第1,084行: | ||
if (!character || !cardName) return; | if (!character || !cardName) return; | ||
// | // 只在空闲时预加载 | ||
preloadCardData(character, cardName, '灵光一闪'); | if (activeRequests < 2) { | ||
preloadCardData(character, cardName, '灵光一闪'); | |||
preloadCardData(character, cardName, '神之一闪'); | |||
if (derivedCards && derivedCards.trim() !== '') { | |||
var cardsList = derivedCards.split('、'); | |||
var preloadCount = Math.min(2, cardsList.length); // 减少预加载数量 | |||
for (var i = 0; i < preloadCount; i++) { | |||
preloadCardData(character, cardsList[i].trim(), ''); | |||
} | |||
} | } | ||
} | } | ||
}, | }, 500); // 增加延迟 | ||
} | } | ||
}); | }); | ||
| 第1,047行: | 第1,111行: | ||
// 初始化卡牌点击事件 | // 初始化卡牌点击事件 | ||
function initCardClickEvents() { | function initCardClickEvents() { | ||
document.addEventListener('click', function(e) { | document.addEventListener('click', function(e) { | ||
var card = e.target.closest('.game-card'); | var card = e.target.closest('.game-card'); | ||
| 第1,056行: | 第1,119行: | ||
}); | }); | ||
// | // 延迟初始化预加载,避免影响页面初始加载 | ||
initPreloadEvents(); | setTimeout(function() { | ||
initPreloadEvents(); | |||
}, 3000); // 页面加载3秒后才启用预加载 | |||
addLoadingStyles(); | addLoadingStyles(); | ||
} | } | ||
| 第1,120行: | 第1,158行: | ||
overlay.classList.remove('is-scrolling'); | overlay.classList.remove('is-scrolling'); | ||
}, 150); | }, 150); | ||
}); | }, { passive: true }); // 添加 passive 标志提升性能 | ||
} | } | ||
} | } | ||
// | // 清理函数 - 更激进的清理策略 | ||
function cleanup() { | function cleanup() { | ||
if (Object.keys(apiCache).length > cacheManager.maxSize * 1.2) { | |||
if (Object.keys(apiCache).length > cacheManager.maxSize * 1. | |||
var keys = Object.keys(apiCache); | var keys = Object.keys(apiCache); | ||
var toDelete = keys.length - cacheManager.maxSize; | var toDelete = keys.length - cacheManager.maxSize; | ||
keys.sort(function(a, b) { | keys.sort(function(a, b) { | ||
return (cacheManager.accessTime[a] || 0) - (cacheManager.accessTime[b] || 0); | return (cacheManager.accessTime[a] || 0) - (cacheManager.accessTime[b] || 0); | ||
}); | }); | ||
for (var i = 0; i < toDelete; i++) { | for (var i = 0; i < toDelete; i++) { | ||
delete apiCache[keys[i]]; | delete apiCache[keys[i]]; | ||
delete cacheManager.accessTime[keys[i]]; | delete cacheManager.accessTime[keys[i]]; | ||
} | } | ||
console.log('缓存清理完成,删除了 ' + toDelete + ' 条记录'); | |||
} | |||
// 清理预加载队列 | |||
if (preloadQueue.size > 10) { | |||
preloadQueue.clear(); | |||
console.log('预加载队列已清空'); | |||
} | } | ||
} | } | ||
// | // 定期清理(每3分钟) | ||
setInterval(cleanup, | setInterval(cleanup, 3 * 60 * 1000); | ||
// 页面卸载时清理 | |||
window.addEventListener('beforeunload', function() { | |||
apiCache = {}; | |||
cacheManager.accessTime = {}; | |||
preloadQueue.clear(); | |||
}); | |||
// 监控内存使用(仅在支持的浏览器中) | |||
function monitorMemory() { | |||
if (window.performance && window.performance.memory) { | |||
var memory = window.performance.memory; | |||
var usedPercent = (memory.usedJSHeapSize / memory.jsHeapSizeLimit) * 100; | |||
if (usedPercent > 80) { | |||
console.warn('内存使用率过高: ' + usedPercent.toFixed(2) + '%'); | |||
cleanup(); // 强制清理 | |||
} | |||
} | |||
} | |||
// 每分钟检查一次内存 | |||
setInterval(monitorMemory, 60 * 1000); | |||
// 页面加载完成后初始化 | // 页面加载完成后初始化 | ||
function init() { | |||
initCardClickEvents(); | |||
initKeyboardShortcuts(); | |||
// 延迟执行性能优化,避免阻塞主线程 | |||
setTimeout(function() { | |||
optimizeScrollPerformance(); | |||
}, 1000); | |||
console.log('卡牌系统初始化完成'); | |||
} | |||
if (document.readyState === 'loading') { | if (document.readyState === 'loading') { | ||
document.addEventListener('DOMContentLoaded', function() { | document.addEventListener('DOMContentLoaded', init); | ||
} else { | |||
init(); | |||
} | |||
})(); | |||
/* 卡牌文字滚动 */ | |||
(function() { | |||
'use strict'; | |||
function initCardScroll() { | |||
var cards = document.querySelectorAll('.game-card.card-description-scrollable'); | |||
cards.forEach(function(card) { | |||
var scrollContainer = card.querySelector('.card-description-scroll'); | |||
var scrollInner = card.querySelector('.card-description-scroll-inner'); | |||
var scrollText = card.querySelector('.card-description-text'); | |||
if (!scrollContainer || !scrollInner || !scrollText) { | |||
if ( | return; | ||
} | } | ||
// 使用 requestAnimationFrame 优化性能 | |||
requestAnimationFrame(function() { | |||
scrollInner.style.display = 'none'; | |||
scrollInner.offsetHeight; | |||
scrollInner.style.display = 'block'; | |||
}); | |||
}); | }); | ||
} | |||
// 延迟初始化,使用防抖优化 | |||
var initTimeout; | |||
function delayedInit() { | |||
clearTimeout(initTimeout); | |||
initTimeout = setTimeout(function() { | |||
initCardScroll(); | |||
}, 100); | |||
} | |||
if (document.readyState === 'loading') { | |||
document.addEventListener('DOMContentLoaded', delayedInit); | |||
} else { | } else { | ||
delayedInit(); | |||
} | |||
// 监听动态内容加载(例如 AJAX 加载) | |||
var observer = new MutationObserver(function(mutations) { | |||
var shouldInit = false; | |||
mutations.forEach(function(mutation) { | |||
if (mutation.addedNodes.length > 0) { | |||
mutation.addedNodes.forEach(function(node) { | |||
if (node.nodeType === 1 && | |||
(node.classList.contains('game-card') || | |||
node.querySelector('.game-card'))) { | |||
shouldInit = true; | |||
} | |||
}); | |||
} | |||
}); | |||
if (shouldInit) { | |||
if ( | delayedInit(); | ||
} | } | ||
} | }); | ||
// 只观察特定容器,减少性能开销 | |||
observer.observe(document.body, { | |||
childList: true, | |||
subtree: true | |||
}); | |||
})(); | })(); | ||
/* 卡牌文字滚动 */ | /* 卡牌文字滚动 */ | ||
(function() { | (function() { | ||
2025年10月9日 (四) 22:41的版本
/* 这里的任何JavaScript将为所有用户在每次页面加载时加载。 */
/* 切换标签 */
(function() {
'use strict';
function initTabSwitcher() {
var tabContainers = document.querySelectorAll('.resp-tabs');
tabContainers.forEach(function(container) {
var tabButtons = container.querySelectorAll('.czn-list-style');
if (tabButtons.length === 0) return;
var tabContents = container.querySelectorAll('.resp-tab-content');
// 初始化
tabButtons.forEach(function(button, index) {
button.classList.toggle('active', index === 0);
});
if (tabContents.length > 0) {
tabContents.forEach(function(content, index) {
content.style.display = index === 0 ? 'block' : 'none';
});
}
// 点击事件
tabButtons.forEach(function(button, index) {
button.addEventListener('click', function(e) {
e.preventDefault();
// 更新标签状态
tabButtons.forEach(function(btn, i) {
btn.classList.toggle('active', i === index);
});
// 切换内容
if (tabContents.length > 0) {
tabContents.forEach(function(content, i) {
content.style.display = i === index ? 'block' : 'none';
});
}
});
});
});
}
// 初始化
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initTabSwitcher);
} else {
initTabSwitcher();
}
})();
/* 角色立绘切换 */
$(document).ready(function() {
// 使用事件委托来确保动态内容也能响应
$(document).on('click', '.character-switch-btn', function() {
// 如果点击的是已经激活的按钮,不执行任何操作
if ($(this).hasClass('active')) {
return;
}
var targetType = $(this).attr('data-target');
var container = $(this).closest('#character-container');
var imageWrapper = container.find('.character-image-wrapper');
var allImages = imageWrapper.find('.character-image');
// 先将所有图片淡出
allImages.each(function() {
$(this).css('opacity', '0');
});
// 延迟后切换显示状态
setTimeout(function() {
allImages.each(function() {
$(this).css('display', 'none');
});
// 显示目标图片
imageWrapper.find('[data-image-type="' + targetType + '"]').each(function() {
$(this).css('display', 'block');
// 强制重绘
$(this)[0].offsetHeight;
$(this).css('opacity', '1');
});
}, 300);
// 更新按钮样式
container.find('.character-switch-btn').each(function() {
$(this).removeClass('active');
$(this).css({
'background': 'rgba(0,0,0,0.5)',
'cursor': 'pointer'
});
});
// 设置当前按钮为激活状态
$(this).addClass('active');
$(this).css({
'background': 'rgba(70, 130, 255, 0.8)',
'cursor': 'default'
});
});
// 鼠标悬停效果(仅对非激活按钮有效)
$(document).on('mouseenter', '.character-switch-btn:not(.active)', function() {
$(this).css('background', 'rgba(70, 130, 255, 0.5)');
});
$(document).on('mouseleave', '.character-switch-btn:not(.active)', function() {
$(this).css('background', 'rgba(0,0,0,0.5)');
});
});
/* 悬浮目录 */
$(document).ready(function() {
// 只在有目录的页面上执行
if ($('.toc').length > 0) {
// 创建侧边目录
var $sidebar = $('<div id="toc-sidebar"><div id="toc-sidebar-trigger">展开目录</div><div id="toc-sidebar-content"></div></div>');
$('body').append($sidebar);
// 提取并修复目录内容
var tocUl = $('<ul></ul>');
// 避免重复的目录项
var processedItems = new Set();
// 从原始目录构建新目录
$('.toc ul li').each(function() {
var $link = $(this).find('a').first();
var href = $link.attr('href');
var $number = $link.find('.tocnumber').first();
var $text = $link.find('.toctext').first();
// 创建唯一标识符,避免重复添加
var itemId = $number.text() + '-' + $text.text();
if (!processedItems.has(itemId)) {
processedItems.add(itemId);
// 创建新的目录项
var $li = $('<li></li>');
var $newLink = $('').attr('href', href);
// 如果有编号,则添加编号
if ($number.length) {
$newLink.append($('<span class="tocnumber"></span>').text($number.text()));
$newLink.append(' ');
}
$newLink.append($('<span class="toctext"></span>').text($text.text()));
$li.append($newLink);
tocUl.append($li);
}
});
$('#toc-sidebar-content').append(tocUl);
// 点击展开/折叠事件处理
$('#toc-sidebar-trigger').click(function() {
$('#toc-sidebar').toggleClass('expanded');
// 根据展开/折叠状态更改按钮文字
if ($('#toc-sidebar').hasClass('expanded')) {
$('#toc-sidebar-trigger').text('隐藏目录');
} else {
$('#toc-sidebar-trigger').text('展开目录');
}
});
}
});
/* 卡牌 */
(function() {
// 防止重复初始化
if (window.cardSystemInitialized) return;
window.cardSystemInitialized = true;
// 缓存DOM元素
var overlayCache = null;
var containerCache = null;
// 保存当前卡牌信息,用于返回
var currentCardInfo = null;
// 添加历史栈来记录浏览历史
var viewHistory = [];
// API缓存
var apiCache = {};
// 预加载队列
var preloadQueue = new Set();
var preloadWorker = null;
// 添加请求队列控制并发
var requestQueue = [];
var activeRequests = 0;
var MAX_CONCURRENT_REQUESTS = 3; // 限制最大并发请求数
// 添加请求超时控制
var REQUEST_TIMEOUT = 10000; // 10秒超时
// 智能缓存管理器
var cacheManager = {
maxSize: 100,
accessTime: {},
set: function(key, value) {
apiCache[key] = value;
this.accessTime[key] = Date.now();
var keys = Object.keys(apiCache);
if (keys.length > this.maxSize) {
var oldestKey = keys[0];
var oldestTime = this.accessTime[oldestKey] || 0;
for (var i = 1; i < keys.length; i++) {
var time = this.accessTime[keys[i]] || 0;
if (time < oldestTime) {
oldestKey = keys[i];
oldestTime = time;
}
}
delete apiCache[oldestKey];
delete this.accessTime[oldestKey];
}
},
get: function(key) {
if (apiCache[key]) {
this.accessTime[key] = Date.now();
return apiCache[key];
}
return null;
}
};
// 添加Loading动画CSS
function addLoadingStyles() {
if (!document.getElementById('card-loading-styles')) {
var style = document.createElement('style');
style.id = 'card-loading-styles';
style.textContent = `
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.card-fade-in {
animation: fadeIn 0.3s ease-in;
}
`;
document.head.appendChild(style);
}
}
// 创建loading占位符
function createLoadingPlaceholder(text, scale) {
var placeholder = document.createElement('div');
placeholder.className = 'card-loading';
var baseStyle = 'width: 168px; height: 230px; background: rgba(255,255,255,0.1); border: 2px dashed rgba(255,255,255,0.3); border-radius: 8px; display: flex; align-items: center; justify-content: center; color: white;';
if (scale) {
baseStyle += ' transform: scale(' + scale + '); transform-origin: center center;';
}
placeholder.style.cssText = baseStyle;
placeholder.innerHTML = '<div style="text-align: center;">' +
'<div class="loading-spinner" style="border: 3px solid rgba(255,255,255,0.3); border-top: 3px solid white; border-radius: 50%; width: 30px; height: 30px; animation: spin 1s linear infinite; margin: 0 auto 10px;"></div>' +
'<div style="font-size: 12px;">' + (text || '加载中...') + '</div>' +
'</div>';
return placeholder;
}
// 错误占位符
function createErrorPlaceholder(text, scale) {
var placeholder = document.createElement('div');
placeholder.className = 'card-error';
var baseStyle = 'width: 168px; height: 230px; background: rgba(255,0,0,0.1); border: 2px dashed rgba(255,0,0,0.3); border-radius: 8px; display: flex; align-items: center; justify-content: center; color: #ff6b6b;';
if (scale) {
baseStyle += ' transform: scale(' + scale + '); transform-origin: center center;';
}
placeholder.style.cssText = baseStyle;
placeholder.innerHTML = '<div style="text-align: center; padding: 10px;">' +
'<div style="font-size: 30px; margin-bottom: 10px;">⚠</div>' +
'<div style="font-size: 12px;">' + (text || '加载失败') + '</div>' +
'</div>';
return placeholder;
}
// 请求队列处理器
function processRequestQueue() {
if (requestQueue.length === 0 || activeRequests >= MAX_CONCURRENT_REQUESTS) {
return;
}
var request = requestQueue.shift();
activeRequests++;
request.execute(function() {
activeRequests--;
processRequestQueue(); // 处理下一个请求
});
}
// 添加到请求队列
function queueRequest(executeFn) {
return new Promise(function(resolve, reject) {
requestQueue.push({
execute: function(done) {
executeFn()
.then(function(result) {
resolve(result);
done();
})
.catch(function(error) {
reject(error);
done();
});
}
});
processRequestQueue();
});
}
// 优化的API请求函数,添加超时和错误处理
function fetchCardHTML(character, cardName, deckType, callback) {
var cacheKey = character + '|' + cardName + '|' + (deckType || '');
// 检查缓存
var cached = cacheManager.get(cacheKey);
if (cached) {
callback(cached, null);
return;
}
// 使用请求队列
queueRequest(function() {
return new Promise(function(resolve, reject) {
var api = new mw.Api();
var wikitext = '{{#invoke:卡牌|main|' + character + '|' + cardName + '|' + (deckType || '') + '}}';
var timeoutId = setTimeout(function() {
reject(new Error('请求超时'));
}, REQUEST_TIMEOUT);
api.parse(wikitext)
.done(function(html) {
clearTimeout(timeoutId);
cacheManager.set(cacheKey, html);
resolve(html);
})
.fail(function(error) {
clearTimeout(timeoutId);
console.error('加载卡牌失败:', character, cardName, deckType, error);
reject(error);
});
});
})
.then(function(html) {
callback(html, null);
})
.catch(function(error) {
callback(null, error);
});
}
// 批量获取卡牌HTML - 优化版本
function fetchCardHTMLBatch(requests, callback) {
var results = {};
var errors = {};
var pending = [];
// 先检查缓存
requests.forEach(function(req) {
var cacheKey = req.character + '|' + req.cardName + '|' + (req.deckType || '');
var cached = cacheManager.get(cacheKey);
if (cached) {
results[cacheKey] = cached;
} else {
pending.push(req);
}
});
// 如果全部都有缓存,直接返回
if (pending.length === 0) {
callback(results, errors);
return;
}
// 限制批量请求数量,分批处理
var batchSize = 5;
var batches = [];
for (var i = 0; i < pending.length; i += batchSize) {
batches.push(pending.slice(i, i + batchSize));
}
var currentBatch = 0;
function processBatch() {
if (currentBatch >= batches.length) {
callback(results, errors);
return;
}
var batch = batches[currentBatch];
var completed = 0;
batch.forEach(function(req) {
fetchCardHTML(req.character, req.cardName, req.deckType, function(html, error) {
var cacheKey = req.character + '|' + req.cardName + '|' + (req.deckType || '');
if (error) {
errors[cacheKey] = error;
} else {
results[cacheKey] = html;
}
completed++;
if (completed === batch.length) {
currentBatch++;
// 延迟处理下一批,避免请求过快
setTimeout(processBatch, 200);
}
});
});
}
processBatch();
}
// 预加载管理器 - 降低优先级
function preloadCardData(character, cardName, deckType) {
var cacheKey = character + '|' + cardName + '|' + (deckType || '');
if (!cacheManager.get(cacheKey) && !preloadQueue.has(cacheKey)) {
preloadQueue.add(cacheKey);
if (!preloadWorker) {
preloadWorker = setTimeout(processPreloadQueue, 500); // 增加延迟
}
}
}
// 批量处理预加载队列 - 降低频率
function processPreloadQueue() {
preloadWorker = null;
if (preloadQueue.size === 0) return;
var batch = Array.from(preloadQueue).slice(0, 2); // 减少每批数量
batch.forEach(function(key) {
preloadQueue.delete(key);
});
batch.forEach(function(cacheKey) {
var parts = cacheKey.split('|');
fetchCardHTML(parts[0], parts[1], parts[2], function() {
// 预加载完成,数据已缓存
});
});
// 如果还有剩余,继续处理
if (preloadQueue.size > 0) {
preloadWorker = setTimeout(processPreloadQueue, 1000); // 增加间隔
}
}
// 创建遮罩层和容器
function createCardOverlay() {
if (overlayCache && containerCache) {
return { overlay: overlayCache, container: containerCache };
}
var overlay = document.createElement('div');
overlay.id = 'card-overlay';
overlay.style.cssText = 'display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); z-index: 9999; overflow-y: auto;';
var container = document.createElement('div');
container.id = 'card-display-container';
container.style.cssText = 'position: relative; min-height: 100vh; padding: 40px 20px; display: flex; align-items: center; justify-content: center;';
overlay.appendChild(container);
document.body.appendChild(overlay);
overlay.addEventListener('click', function(e) {
if (e.target === overlay) {
closeCardDisplay();
}
});
overlayCache = overlay;
containerCache = container;
return { overlay: overlay, container: container };
}
// 创建关闭按钮
function createCloseButton() {
var closeBtn = document.createElement('div');
closeBtn.style.cssText = 'position: fixed; top: 20px; right: 20px; width: 40px; height: 40px; background: rgba(255,255,255,0.1); border: 2px solid white; border-radius: 50%; cursor: pointer; display: flex; align-items: center; justify-content: center; z-index: 10001; transition: all 0.3s;';
closeBtn.innerHTML = '<span style="color: white; font-size: 24px; font-weight: bold; line-height: 1;">×</span>';
closeBtn.onmouseover = function() {
this.style.background = 'rgba(255,255,255,0.2)';
this.style.transform = 'scale(1.1)';
};
closeBtn.onmouseout = function() {
this.style.background = 'rgba(255,255,255,0.1)';
this.style.transform = 'scale(1)';
};
closeBtn.onclick = function() {
closeCardDisplay();
};
return closeBtn;
}
// 关闭卡牌展示
function closeCardDisplay() {
var overlay = document.getElementById('card-overlay');
if (overlay) {
overlay.style.display = 'none';
var container = overlay.querySelector('#card-display-container');
if (container) {
container.innerHTML = '';
}
}
currentCardInfo = null;
viewHistory = [];
}
// 返回上一层
function goBack() {
if (viewHistory.length > 1) {
viewHistory.pop();
var previousView = viewHistory[viewHistory.length - 1];
if (previousView.type === 'enlarged') {
showEnlargedCard(previousView.data.element, -1);
} else if (previousView.type === 'derivedCards') {
showAllDerivedCards(previousView.data.character, previousView.data.derivedCardsList, -1);
} else if (previousView.type === 'variantCards') {
showVariantCards(previousView.data.character, previousView.data.cardName, previousView.data.variantType, -1);
}
} else if (viewHistory.length === 1) {
closeCardDisplay();
} else {
closeCardDisplay();
}
}
// 检查卡牌变体 - 优化版本,添加错误处理
function checkCardVariants(character, cardName, callback) {
var variants = { lingguang: false, shenguang: false };
var checkCount = 0;
var totalChecks = 2;
function checkComplete() {
checkCount++;
if (checkCount === totalChecks) {
callback(variants);
}
}
fetchCardHTML(character, cardName, '灵光一闪', function(html, error) {
if (!error && html && !html.includes('找不到卡组')) {
variants.lingguang = true;
}
checkComplete();
});
fetchCardHTML(character, cardName, '神之一闪', function(html, error) {
if (!error && html && !html.includes('找不到卡组')) {
variants.shenguang = true;
}
checkComplete();
});
}
// 放大显示卡牌 - 添加错误处理
function showEnlargedCard(cardElement, addToHistory) {
var elements = createCardOverlay();
var container = elements.container;
var cardName = cardElement.dataset.cardName;
var character = cardElement.dataset.character;
var deckType = cardElement.dataset.deckType;
var derivedCards = cardElement.dataset.derivedCards;
var hasMechanism = cardElement.dataset.hasMechanism === 'true';
addLoadingStyles();
currentCardInfo = {
element: cardElement,
cardName: cardName,
character: character,
deckType: deckType,
derivedCards: derivedCards
};
if (addToHistory === -1) {
if (viewHistory.length > 0) {
viewHistory[viewHistory.length - 1] = {
type: 'enlarged',
data: currentCardInfo
};
}
} else if (addToHistory !== false) {
viewHistory.push({
type: 'enlarged',
data: currentCardInfo
});
}
container.style.cssText = 'position: relative; min-height: 100vh; padding: 40px 20px; display: flex; align-items: center; justify-content: center;';
var contentWrapper = document.createElement('div');
contentWrapper.style.cssText = 'display: flex; align-items: flex-start; gap: 0px; position: relative;';
var centerContainer = document.createElement('div');
centerContainer.style.cssText = 'display: flex; flex-direction: column; align-items: center; gap: 0px;';
var mainCardContainer = document.createElement('div');
mainCardContainer.style.cssText = 'width: 336px; height: 460px; display: flex; align-items: center; justify-content: center; position: relative;';
var cardInner = document.createElement('div');
cardInner.style.cssText = 'transform: scale(2); position: relative;';
var enlargedCard = cardElement.cloneNode(true);
enlargedCard.style.width = '168px';
enlargedCard.style.height = '230px';
enlargedCard.style.cursor = 'default';
enlargedCard.style.position = 'relative';
enlargedCard.style.display = 'block';
enlargedCard.onclick = null;
enlargedCard.classList.add('card-fade-in');
cardInner.appendChild(enlargedCard);
mainCardContainer.appendChild(cardInner);
centerContainer.appendChild(mainCardContainer);
// 处理衍生卡牌 - 添加错误处理
if (derivedCards && derivedCards.trim() !== '') {
var leftContainer = document.createElement('div');
leftContainer.style.cssText = 'display: flex; flex-direction: column; gap: 0px; align-items: center;';
var derivedCardsList = derivedCards.split('、');
var derivedCardWrapper = document.createElement('div');
derivedCardWrapper.style.cssText = 'width: 252px; height: 345px; display: flex; align-items: center; justify-content: center;';
var placeholder = createLoadingPlaceholder('加载衍生卡牌...', 1.5);
derivedCardWrapper.appendChild(placeholder);
leftContainer.appendChild(derivedCardWrapper);
fetchCardHTML(character, derivedCardsList[0].trim(), '', function(html, error) {
if (error || !html) {
var errorPlaceholder = createErrorPlaceholder('加载失败', 1.5);
derivedCardWrapper.removeChild(placeholder);
derivedCardWrapper.appendChild(errorPlaceholder);
return;
}
var firstDerivedCard = document.createElement('div');
firstDerivedCard.id = 'derived-cards-display';
firstDerivedCard.style.cssText = 'transform: scale(1.5); transform-origin: center center; opacity: 0;';
firstDerivedCard.innerHTML = html;
var cards = firstDerivedCard.querySelectorAll('.game-card');
cards.forEach(function(card) {
card.style.cursor = 'default';
card.onclick = function(e) {
e.stopPropagation();
e.preventDefault();
};
});
derivedCardWrapper.removeChild(placeholder);
derivedCardWrapper.appendChild(firstDerivedCard);
setTimeout(function() {
firstDerivedCard.style.transition = 'opacity 0.3s';
firstDerivedCard.style.opacity = '1';
}, 10);
});
if (derivedCardsList.length > 1) {
var viewAllBtn = document.createElement('div');
viewAllBtn.style.cssText = 'padding: 10px 20px; background: linear-gradient(135deg, #4a90e2, #357abd); color: white; border-radius: 6px; cursor: pointer; white-space: nowrap; font-weight: bold; box-shadow: 0 2px 8px rgba(0,0,0,0.3); transition: transform 0.2s; margin-top: 0px;';
viewAllBtn.textContent = '查看所有衍生卡牌';
viewAllBtn.onmouseover = function() { this.style.transform = 'scale(1.05)'; };
viewAllBtn.onmouseout = function() { this.style.transform = 'scale(1)'; };
viewAllBtn.onclick = function() {
showAllDerivedCards(character, derivedCardsList, true);
};
leftContainer.appendChild(viewAllBtn);
}
contentWrapper.appendChild(leftContainer);
}
var buttonsContainer = document.createElement('div');
buttonsContainer.style.cssText = 'display: flex; gap: 15px; white-space: nowrap; min-height: 50px; align-items: center; justify-content: center;';
var checkingVariants = document.createElement('div');
checkingVariants.style.cssText = 'color: rgba(255,255,255,0.5); font-size: 12px;';
checkingVariants.textContent = '检查变体卡牌...';
buttonsContainer.appendChild(checkingVariants);
centerContainer.appendChild(buttonsContainer);
checkCardVariants(character, cardName, function(variants) {
if (buttonsContainer.contains(checkingVariants)) {
buttonsContainer.removeChild(checkingVariants);
}
if (variants.lingguang) {
var lingguangBtn = document.createElement('div');
lingguangBtn.style.cssText = 'padding: 10px 30px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 6px; cursor: pointer; font-weight: bold; box-shadow: 0 2px 8px rgba(0,0,0,0.3); transition: transform 0.2s; white-space: nowrap; min-width: 120px; text-align: center; opacity: 0;';
lingguangBtn.textContent = '灵光一闪';
lingguangBtn.onmouseover = function() { this.style.transform = 'scale(1.05)'; };
lingguangBtn.onmouseout = function() { this.style.transform = 'scale(1)'; };
lingguangBtn.onclick = function() {
showVariantCards(character, cardName, '灵光一闪', true);
};
buttonsContainer.appendChild(lingguangBtn);
setTimeout(function() {
lingguangBtn.style.transition = 'opacity 0.3s';
lingguangBtn.style.opacity = '1';
}, 10);
}
if (variants.shenguang) {
var shenguangBtn = document.createElement('div');
shenguangBtn.style.cssText = 'padding: 10px 30px; background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); color: white; border-radius: 6px; cursor: pointer; font-weight: bold; box-shadow: 0 2px 8px rgba(0,0,0,0.3); transition: transform 0.2s; white-space: nowrap; min-width: 120px; text-align: center; opacity: 0;';
shenguangBtn.textContent = '神之一闪';
shenguangBtn.onmouseover = function() { this.style.transform = 'scale(1.05)'; };
shenguangBtn.onmouseout = function() { this.style.transform = 'scale(1)'; };
shenguangBtn.onclick = function() {
showVariantCards(character, cardName, '神之一闪', true);
};
buttonsContainer.appendChild(shenguangBtn);
setTimeout(function() {
shenguangBtn.style.transition = 'opacity 0.3s';
shenguangBtn.style.opacity = '1';
}, 10);
}
if (!variants.lingguang && !variants.shenguang) {
centerContainer.removeChild(buttonsContainer);
}
});
contentWrapper.appendChild(centerContainer);
if (hasMechanism) {
var mechanismContainer = cardElement.nextElementSibling;
if (mechanismContainer && mechanismContainer.classList.contains('mechanism-container')) {
var mechanismDisplay = document.createElement('div');
mechanismDisplay.style.cssText = 'display: flex; flex-direction: column; gap: 0px; max-width: 320px; min-width: 280px;';
mechanismDisplay.innerHTML = mechanismContainer.innerHTML;
mechanismDisplay.style.display = 'flex';
contentWrapper.appendChild(mechanismDisplay);
}
}
container.innerHTML = '';
container.appendChild(contentWrapper);
if (viewHistory.length > 1) {
var backBtn = document.createElement('div');
backBtn.style.cssText = 'position: fixed; top: 20px; left: 20px; padding: 10px 20px; background: linear-gradient(135deg, #4a90e2, #357abd); color: white; border-radius: 6px; cursor: pointer; font-weight: bold; box-shadow: 0 2px 8px rgba(0,0,0,0.3); z-index: 10001; transition: transform 0.2s;';
backBtn.textContent = '← 返回';
backBtn.onmouseover = function() { this.style.transform = 'scale(1.05)'; };
backBtn.onmouseout = function() { this.style.transform = 'scale(1)'; };
backBtn.onclick = function() {
goBack();
};
container.appendChild(backBtn);
}
container.appendChild(createCloseButton());
elements.overlay.style.display = 'block';
}
// 显示所有衍生卡牌 - 添加错误处理
function showAllDerivedCards(character, derivedCardsList, addToHistory) {
var overlay = document.getElementById('card-overlay');
var container = document.getElementById('card-display-container');
if (addToHistory === -1) {
if (viewHistory.length > 0) {
viewHistory[viewHistory.length - 1] = {
type: 'derivedCards',
data: {
character: character,
derivedCardsList: derivedCardsList
}
};
}
} else if (addToHistory === true) {
viewHistory.push({
type: 'derivedCards',
data: {
character: character,
derivedCardsList: derivedCardsList
}
});
} else if (addToHistory === 0) {
if (viewHistory.length > 0) {
viewHistory.pop();
}
viewHistory.push({
type: 'derivedCards',
data: {
character: character,
derivedCardsList: derivedCardsList
}
});
}
container.innerHTML = '';
container.style.cssText = 'position: relative; padding: 40px; min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center;';
var title = document.createElement('div');
title.style.cssText = 'color: white; font-size: 28px; font-weight: bold; margin-bottom: 30px; text-align: center;';
title.textContent = '衍生卡牌';
container.appendChild(title);
var cardsContainer = document.createElement('div');
cardsContainer.style.cssText = 'display: flex; flex-wrap: wrap; gap: 30px; justify-content: center; max-width: 1400px; transform: scale(1.5); transform-origin: center center; padding: 40px;';
var uniqueCards = [...new Set(derivedCardsList.map(function(name) { return name.trim(); }))];
var placeholders = [];
uniqueCards.forEach(function() {
var placeholder = createLoadingPlaceholder('加载中...', null);
placeholders.push(placeholder);
cardsContainer.appendChild(placeholder);
});
container.appendChild(cardsContainer);
var requests = uniqueCards.map(function(cardName) {
return { character: character, cardName: cardName, deckType: '' };
});
fetchCardHTMLBatch(requests, function(results, errors) {
uniqueCards.forEach(function(cardName, index) {
var cacheKey = character + '|' + cardName + '|';
if (errors[cacheKey]) {
var errorPlaceholder = createErrorPlaceholder('加载失败', null);
cardsContainer.replaceChild(errorPlaceholder, placeholders[index]);
return;
}
var html = results[cacheKey];
if (html) {
var cardWrapper = document.createElement('div');
cardWrapper.style.cssText = 'opacity: 0;';
cardWrapper.innerHTML = html;
var cards = cardWrapper.querySelectorAll('.game-card');
cards.forEach(function(card) {
card.style.cursor = 'default';
card.onclick = function(e) {
e.stopPropagation();
e.preventDefault();
};
var derivedCards = card.dataset.derivedCards;
if (derivedCards && derivedCards.trim() !== '') {
var buttonContainer = document.createElement('div');
buttonContainer.style.cssText = 'display: flex; flex-direction: column; align-items: center; gap: 10px;';
var cardContainer = document.createElement('div');
cardContainer.appendChild(card.cloneNode(true));
buttonContainer.appendChild(cardContainer);
var viewDerivedBtn = document.createElement('div');
viewDerivedBtn.style.cssText = 'padding: 8px 15px; background: linear-gradient(135deg, #4a90e2, #357abd); color: white; border-radius: 6px; cursor: pointer; white-space: nowrap; font-weight: bold; box-shadow: 0 2px 8px rgba(0,0,0,0.3); transition: transform 0.2s; font-size: 12px;';
viewDerivedBtn.textContent = '查看衍生卡牌';
viewDerivedBtn.onmouseover = function() { this.style.transform = 'scale(1.05)'; };
viewDerivedBtn.onmouseout = function() { this.style.transform = 'scale(1)'; };
viewDerivedBtn.onclick = function(e) {
e.stopPropagation();
showAllDerivedCards(character, derivedCards.split('、'), 0);
};
buttonContainer.appendChild(viewDerivedBtn);
cardsContainer.replaceChild(buttonContainer, placeholders[index]);
setTimeout(function() {
buttonContainer.style.transition = 'opacity 0.3s';
buttonContainer.style.opacity = '1';
}, 10);
} else {
cardsContainer.replaceChild(card, placeholders[index]);
setTimeout(function() {
card.style.transition = 'opacity 0.3s';
card.style.opacity = '1';
}, 10);
}
});
}
});
});
var backBtn = document.createElement('div');
backBtn.style.cssText = 'position: fixed; top: 20px; left: 20px; padding: 10px 20px; background: linear-gradient(135deg, #4a90e2, #357abd); color: white; border-radius: 6px; cursor: pointer; font-weight: bold; box-shadow: 0 2px 8px rgba(0,0,0,0.3); z-index: 10001; transition: transform 0.2s;';
backBtn.textContent = '← 返回';
backBtn.onmouseover = function() { this.style.transform = 'scale(1.05)'; };
backBtn.onmouseout = function() { this.style.transform = 'scale(1)'; };
backBtn.onclick = function() {
goBack();
};
container.appendChild(backBtn);
container.appendChild(createCloseButton());
}
// 显示变体卡牌 - 添加错误处理
function showVariantCards(character, cardName, variantType, addToHistory) {
var overlay = document.getElementById('card-overlay');
var container = document.getElementById('card-display-container');
if (addToHistory === -1) {
if (viewHistory.length > 0) {
viewHistory[viewHistory.length - 1] = {
type: 'variantCards',
data: {
character: character,
cardName: cardName,
variantType: variantType
}
};
}
} else if (addToHistory === true) {
viewHistory.push({
type: 'variantCards',
data: {
character: character,
cardName: cardName,
variantType: variantType
}
});
}
container.innerHTML = '';
container.style.cssText = 'position: relative; padding: 40px; min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center;';
var title = document.createElement('div');
title.style.cssText = 'color: white; font-size: 28px; font-weight: bold; margin-bottom: 30px; text-align: center; background: linear-gradient(135deg, #667eea, #764ba2); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text;';
title.textContent = cardName + ' - ' + variantType;
container.appendChild(title);
var cardsContainer = document.createElement('div');
cardsContainer.style.cssText = 'display: flex; flex-wrap: nowrap; gap: 0px; justify-content: center; max-width: 1400px; overflow-x: auto; padding: 40px; transform: scale(1.5); transform-origin: center center;';
var loadingPlaceholder = createLoadingPlaceholder('加载变体卡牌...', null);
cardsContainer.appendChild(loadingPlaceholder);
container.appendChild(cardsContainer);
fetchCardHTML(character, cardName, variantType, function(html, error) {
if (error || !html) {
cardsContainer.removeChild(loadingPlaceholder);
var errorPlaceholder = createErrorPlaceholder('加载变体失败', null);
cardsContainer.appendChild(errorPlaceholder);
return;
}
var tempContainer = document.createElement('div');
tempContainer.innerHTML = html;
cardsContainer.removeChild(loadingPlaceholder);
var cards = tempContainer.querySelectorAll('.game-card');
cards.forEach(function(card, index) {
card.style.cursor = 'default';
card.onclick = null;
card.style.flexShrink = '0';
card.style.opacity = '0';
var derivedCards = card.getAttribute('data-derived-cards');
var hasDerivedCards = false;
if (derivedCards !== null && derivedCards !== undefined) {
var trimmedValue = String(derivedCards).trim();
if (trimmedValue.length > 0 &&
trimmedValue !== 'undefined' &&
trimmedValue !== 'null' &&
trimmedValue !== 'false' &&
trimmedValue !== '0') {
hasDerivedCards = true;
}
}
if (hasDerivedCards) {
var cardWrapper = document.createElement('div');
cardWrapper.style.cssText = 'display: flex; flex-direction: column; align-items: center; gap: 10px; flex-shrink: 0; opacity: 0;';
var cardClone = card.cloneNode(true);
cardClone.style.cursor = 'default';
cardClone.onclick = null;
cardClone.style.opacity = '1';
cardWrapper.appendChild(cardClone);
var viewDerivedBtn = document.createElement('div');
viewDerivedBtn.style.cssText = 'padding: 8px 15px; background: linear-gradient(135deg, #4a90e2, #357abd); color: white; border-radius: 6px; cursor: pointer; white-space: nowrap; font-weight: bold; box-shadow: 0 2px 8px rgba(0,0,0,0.3); transition: transform 0.2s; font-size: 12px;';
viewDerivedBtn.textContent = '查看衍生卡牌';
viewDerivedBtn.onmouseover = function() { this.style.transform = 'scale(1.05)'; };
viewDerivedBtn.onmouseout = function() { this.style.transform = 'scale(1)'; };
viewDerivedBtn.setAttribute('data-derived-list', derivedCards);
viewDerivedBtn.onclick = function(e) {
e.stopPropagation();
var derivedList = this.getAttribute('data-derived-list');
showAllDerivedCards(character, derivedList.split('、'), true);
};
cardWrapper.appendChild(viewDerivedBtn);
cardsContainer.appendChild(cardWrapper);
setTimeout(function() {
cardWrapper.style.transition = 'opacity 0.3s';
cardWrapper.style.opacity = '1';
}, 50 * index);
} else {
cardsContainer.appendChild(card);
setTimeout(function() {
card.style.transition = 'opacity 0.3s';
card.style.opacity = '1';
}, 50 * index);
}
});
});
var backBtn = document.createElement('div');
backBtn.style.cssText = 'position: fixed; top: 20px; left: 20px; padding: 10px 20px; background: linear-gradient(135deg, #4a90e2, #357abd); color: white; border-radius: 6px; cursor: pointer; font-weight: bold; box-shadow: 0 2px 8px rgba(0,0,0,0.3); z-index: 10001; transition: transform 0.2s;';
backBtn.textContent = '← 返回';
backBtn.onmouseover = function() { this.style.transform = 'scale(1.05)'; };
backBtn.onmouseout = function() { this.style.transform = 'scale(1)'; };
backBtn.onclick = function() {
goBack();
};
container.appendChild(backBtn);
container.appendChild(createCloseButton());
}
// 鼠标悬停时预加载 - 降低优先级,避免影响主流程
function initPreloadEvents() {
var preloadTimeout = null;
document.addEventListener('mouseover', function(e) {
var card = e.target.closest('.game-card');
if (card && !card.closest('#card-overlay')) {
clearTimeout(preloadTimeout);
preloadTimeout = setTimeout(function() {
var character = card.dataset.character;
var cardName = card.dataset.cardName;
var derivedCards = card.dataset.derivedCards;
if (!character || !cardName) return;
// 只在空闲时预加载
if (activeRequests < 2) {
preloadCardData(character, cardName, '灵光一闪');
preloadCardData(character, cardName, '神之一闪');
if (derivedCards && derivedCards.trim() !== '') {
var cardsList = derivedCards.split('、');
var preloadCount = Math.min(2, cardsList.length); // 减少预加载数量
for (var i = 0; i < preloadCount; i++) {
preloadCardData(character, cardsList[i].trim(), '');
}
}
}
}, 500); // 增加延迟
}
});
document.addEventListener('mouseout', function(e) {
var card = e.target.closest('.game-card');
if (card && !card.closest('#card-overlay')) {
clearTimeout(preloadTimeout);
}
});
}
// 初始化卡牌点击事件
function initCardClickEvents() {
document.addEventListener('click', function(e) {
var card = e.target.closest('.game-card');
if (card && !card.closest('#card-overlay')) {
e.preventDefault();
showEnlargedCard(card);
}
});
// 延迟初始化预加载,避免影响页面初始加载
setTimeout(function() {
initPreloadEvents();
}, 3000); // 页面加载3秒后才启用预加载
addLoadingStyles();
}
// 添加键盘快捷键支持
function initKeyboardShortcuts() {
document.addEventListener('keydown', function(e) {
var overlay = document.getElementById('card-overlay');
if (overlay && overlay.style.display !== 'none') {
if (e.key === 'Escape') {
e.preventDefault();
closeCardDisplay();
} else if (e.key === 'Backspace' || e.key === 'ArrowLeft') {
e.preventDefault();
goBack();
}
}
});
}
// 优化滚动性能
function optimizeScrollPerformance() {
var scrollTimeout;
var overlay = document.getElementById('card-overlay');
if (overlay) {
overlay.addEventListener('scroll', function() {
if (!overlay.classList.contains('is-scrolling')) {
overlay.classList.add('is-scrolling');
}
clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(function() {
overlay.classList.remove('is-scrolling');
}, 150);
}, { passive: true }); // 添加 passive 标志提升性能
}
}
// 清理函数 - 更激进的清理策略
function cleanup() {
if (Object.keys(apiCache).length > cacheManager.maxSize * 1.2) {
var keys = Object.keys(apiCache);
var toDelete = keys.length - cacheManager.maxSize;
keys.sort(function(a, b) {
return (cacheManager.accessTime[a] || 0) - (cacheManager.accessTime[b] || 0);
});
for (var i = 0; i < toDelete; i++) {
delete apiCache[keys[i]];
delete cacheManager.accessTime[keys[i]];
}
console.log('缓存清理完成,删除了 ' + toDelete + ' 条记录');
}
// 清理预加载队列
if (preloadQueue.size > 10) {
preloadQueue.clear();
console.log('预加载队列已清空');
}
}
// 定期清理(每3分钟)
setInterval(cleanup, 3 * 60 * 1000);
// 页面卸载时清理
window.addEventListener('beforeunload', function() {
apiCache = {};
cacheManager.accessTime = {};
preloadQueue.clear();
});
// 监控内存使用(仅在支持的浏览器中)
function monitorMemory() {
if (window.performance && window.performance.memory) {
var memory = window.performance.memory;
var usedPercent = (memory.usedJSHeapSize / memory.jsHeapSizeLimit) * 100;
if (usedPercent > 80) {
console.warn('内存使用率过高: ' + usedPercent.toFixed(2) + '%');
cleanup(); // 强制清理
}
}
}
// 每分钟检查一次内存
setInterval(monitorMemory, 60 * 1000);
// 页面加载完成后初始化
function init() {
initCardClickEvents();
initKeyboardShortcuts();
// 延迟执行性能优化,避免阻塞主线程
setTimeout(function() {
optimizeScrollPerformance();
}, 1000);
console.log('卡牌系统初始化完成');
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
/* 卡牌文字滚动 */
(function() {
'use strict';
function initCardScroll() {
var cards = document.querySelectorAll('.game-card.card-description-scrollable');
cards.forEach(function(card) {
var scrollContainer = card.querySelector('.card-description-scroll');
var scrollInner = card.querySelector('.card-description-scroll-inner');
var scrollText = card.querySelector('.card-description-text');
if (!scrollContainer || !scrollInner || !scrollText) {
return;
}
// 使用 requestAnimationFrame 优化性能
requestAnimationFrame(function() {
scrollInner.style.display = 'none';
scrollInner.offsetHeight;
scrollInner.style.display = 'block';
});
});
}
// 延迟初始化,使用防抖优化
var initTimeout;
function delayedInit() {
clearTimeout(initTimeout);
initTimeout = setTimeout(function() {
initCardScroll();
}, 100);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', delayedInit);
} else {
delayedInit();
}
// 监听动态内容加载(例如 AJAX 加载)
var observer = new MutationObserver(function(mutations) {
var shouldInit = false;
mutations.forEach(function(mutation) {
if (mutation.addedNodes.length > 0) {
mutation.addedNodes.forEach(function(node) {
if (node.nodeType === 1 &&
(node.classList.contains('game-card') ||
node.querySelector('.game-card'))) {
shouldInit = true;
}
});
}
});
if (shouldInit) {
delayedInit();
}
});
// 只观察特定容器,减少性能开销
observer.observe(document.body, {
childList: true,
subtree: true
});
})();
/* 卡牌文字滚动 */
(function() {
'use strict';
function initCardScroll() {
var cards = document.querySelectorAll('.game-card.card-description-scrollable');
cards.forEach(function(card) {
var scrollContainer = card.querySelector('.card-description-scroll');
var scrollInner = card.querySelector('.card-description-scroll-inner');
var scrollText = card.querySelector('.card-description-text');
if (!scrollContainer || !scrollInner || !scrollText) {
return;
}
// 强制触发重绘
scrollInner.style.display = 'none';
scrollInner.offsetHeight; // 触发重排
scrollInner.style.display = 'block';
});
}
// 延迟初始化,确保DOM完全加载
function delayedInit() {
setTimeout(initCardScroll, 100);
setTimeout(initCardScroll, 500);
setTimeout(initCardScroll, 1000);
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', delayedInit);
} else {
delayedInit();
}
})();
/* 卡牌Tooltip */
$(function() {
// 初始化所有卡牌悬浮
function initCardHoverElements() {
$('.card-hover-container').each(function() {
var $container = $(this);
// 只初始化尚未处理的元素
if ($container.data('initialized')) return;
$container.data('initialized', true);
// 获取卡牌数据
var character = $container.data('character');
var cardName = $container.data('card');
var deckType = $container.data('deck') || '';
var index = $container.data('index') || 0;
var $popup = $container.find('.card-popup');
// 预加载卡牌数据
$container.on('mouseenter', function() {
// 如果卡牌数据已经加载过,直接显示
if ($popup.data('loaded')) {
// 调整位置到右下方
positionCardPopup($container);
return;
}
// 加载卡牌数据
var params = {
action: 'parse',
text: '{{#invoke:卡牌|main|' + character + '|' + cardName + '|' + deckType + '|' + index + '}}',
prop: 'text',
disablelimitreport: true,
format: 'json'
};
$.ajax({
url: mw.util.wikiScript('api'),
data: params,
dataType: 'json',
success: function(data) {
if (data && data.parse && data.parse.text) {
var cardHtml = data.parse.text['*'];
$popup.html(cardHtml).data('loaded', true);
// 调整位置到右下方
positionCardPopup($container);
}
},
error: function() {
$popup.html('<div style="color: #721c24;">无法加载卡牌数据</div>').data('loaded', true);
}
});
});
});
}
// 调整卡牌弹出位置到右下方
function positionCardPopup($container) {
var $popupContainer = $container.find('.card-popup-container');
var containerWidth = $container.width();
var windowWidth = $(window).width();
var containerOffset = $container.offset();
var rightSpace = windowWidth - containerOffset.left - containerWidth;
// 重置位置
$popupContainer.css({
'left': 'auto',
'right': 'auto',
'top': '100%',
'margin-top': '5px'
});
// 如果右侧空间足够,放在右下方
if (rightSpace >= 168) { // 卡牌宽度大约168px
$popupContainer.css({
'left': '0',
});
}
// 如果右侧空间不够,但左侧空间足够,放在左下方
else if (containerOffset.left >= 168) {
$popupContainer.css({
'right': '0',
});
}
// 如果两侧都不够,尝试居中并确保完全可见
else {
var leftPosition = Math.max(0, Math.min(containerOffset.left - (168/2), windowWidth - 168));
$popupContainer.css({
'left': (leftPosition - containerOffset.left) + 'px'
});
}
}
// 初次加载页面时初始化
$(document).ready(function() {
initCardHoverElements();
});
// 使用 MutationObserver 监听 DOM 变化,处理动态加载的内容
if (window.MutationObserver) {
var observer = new MutationObserver(function(mutations) {
var shouldInit = false;
mutations.forEach(function(mutation) {
if (mutation.addedNodes && mutation.addedNodes.length) {
shouldInit = true;
}
});
if (shouldInit) {
initCardHoverElements();
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}
});
/* 词典Tooltip */
(function() {
var tooltipContainer = null;
var currentTerm = null;
var hideTimeout = null;
// 初始化
$(function() {
// 创建全局 tooltip 容器
tooltipContainer = $('<div class="dictionary-tooltip-container"></div>');
$('body').append(tooltipContainer);
// 鼠标进入词条
$(document).on('mouseenter', '.dictionary-term', function(e) {
var $term = $(this);
currentTerm = $term;
// 清除隐藏定时器
if (hideTimeout) {
clearTimeout(hideTimeout);
hideTimeout = null;
}
// 获取 tooltip 内容
var content = $term.attr('data-tooltip-content');
if (!content) return;
// 设置内容
tooltipContainer.html(content);
// 计算位置
updateTooltipPosition($term);
// 显示 tooltip
tooltipContainer.addClass('active');
});
// 鼠标离开词条
$(document).on('mouseleave', '.dictionary-term', function() {
hideTimeout = setTimeout(function() {
tooltipContainer.removeClass('active');
currentTerm = null;
}, 100);
});
// 鼠标进入 tooltip(防止快速隐藏)
tooltipContainer.on('mouseenter', function() {
if (hideTimeout) {
clearTimeout(hideTimeout);
hideTimeout = null;
}
});
// 鼠标离开 tooltip
tooltipContainer.on('mouseleave', function() {
hideTimeout = setTimeout(function() {
tooltipContainer.removeClass('active');
currentTerm = null;
}, 100);
});
// 窗口滚动时更新位置
$(window).on('scroll resize', function() {
if (currentTerm && tooltipContainer.hasClass('active')) {
updateTooltipPosition(currentTerm);
}
});
});
// 更新 tooltip 位置
function updateTooltipPosition($term) {
var rect = $term[0].getBoundingClientRect();
var scrollTop = $(window).scrollTop();
var scrollLeft = $(window).scrollLeft();
// 基本位置:词条下方
var top = rect.bottom + scrollTop + 2;
var left = rect.left + scrollLeft;
// 获取 tooltip 尺寸
var tooltipWidth = 250; // 固定宽度
var tooltipHeight = tooltipContainer.outerHeight();
// 获取窗口尺寸
var windowWidth = $(window).width();
var windowHeight = $(window).height();
// 检查右边界
if (left + tooltipWidth > windowWidth + scrollLeft) {
left = Math.max(scrollLeft, rect.right + scrollLeft - tooltipWidth);
}
// 检查下边界
if (rect.bottom + tooltipHeight > windowHeight) {
// 如果下方空间不足,显示在上方
if (rect.top - tooltipHeight > 0) {
top = rect.top + scrollTop - tooltipHeight - 2;
}
}
// 设置位置
tooltipContainer.css({
top: top + 'px',
left: left + 'px'
});
}
})();