微件:Card:修订间差异
跳转到导航
跳转到搜索
创建页面,内容为“<noinclude> 此Widget为卡牌显示添加交互效果: * 鼠标悬停时的动画效果 * 点击后显示放大的模态框 * 支持点击遮罩或关闭按钮关闭模态框 使用方法:在页面中添加 {{#widget:Card}} </noinclude><includeonly> <style> →卡牌悬停动画: .card-deck-trans { cursor: pointer; transition: transform 0.3s ease, box-shadow 0.3s ease; } .card-deck-trans:hover { transform: translateY(-8px); filter: brig…” |
小 功能测试 |
||
| 第3行: | 第3行: | ||
* 鼠标悬停时的动画效果 | * 鼠标悬停时的动画效果 | ||
* 点击后显示放大的模态框 | * 点击后显示放大的模态框 | ||
* | * 自动检测spark_enable属性显示闪光按钮 | ||
* 支持闪光卡牌列表显示 | |||
使用方法:在页面底部添加 <code>{{#widget:Card}}</code> | |||
</noinclude><includeonly> | </noinclude><includeonly> | ||
<style> | <style> | ||
/* 卡牌容器 */ | |||
.card-wrapper { | |||
display: inline-block; | |||
} | |||
/* 卡牌悬停动画 */ | /* 卡牌悬停动画 */ | ||
.card-deck-trans { | .card-deck-trans { | ||
cursor: pointer; | cursor: pointer; | ||
transition: transform 0.3s ease, box-shadow 0.3s ease; | transition: transform 0.3s ease, box-shadow 0.3s ease, filter 0.3s ease; | ||
} | } | ||
| 第23行: | 第29行: | ||
} | } | ||
/* | /* 模态框通用样式 */ | ||
.card-modal-overlay { | .card-modal-overlay, | ||
.spark-modal-overlay { | |||
display: none; | display: none; | ||
position: fixed; | position: fixed; | ||
| 第39行: | 第46行: | ||
} | } | ||
.card-modal-overlay.active { | .spark-modal-overlay { | ||
z-index: 10010; | |||
background-color: rgba(0, 0, 0, 0.9); | |||
} | |||
.card-modal-overlay.active, | |||
.spark-modal-overlay.active { | |||
display: flex; | display: flex; | ||
opacity: 1; | opacity: 1; | ||
} | } | ||
.card-modal-overlay.fade-in { | .card-modal-overlay.fade-in, | ||
.spark-modal-overlay.fade-in { | |||
opacity: 1; | opacity: 1; | ||
} | } | ||
.card-modal-overlay.fade-out { | .card-modal-overlay.fade-out, | ||
.spark-modal-overlay.fade-out { | |||
opacity: 0; | opacity: 0; | ||
} | } | ||
/* 关闭按钮 */ | /* 关闭按钮 */ | ||
.card-modal-close { | .card-modal-close, | ||
.spark-modal-close { | |||
position: fixed; | position: fixed; | ||
top: 20px; | top: 20px; | ||
| 第67行: | 第83行: | ||
} | } | ||
.card-modal-close:hover { | .spark-modal-close { | ||
z-index: 10012; | |||
} | |||
.card-modal-close:hover, | |||
.spark-modal-close:hover { | |||
color: #ff6b6b; | color: #ff6b6b; | ||
transform: scale(1.1); | transform: scale(1.1); | ||
| 第76行: | 第97行: | ||
position: relative; | position: relative; | ||
z-index: 10000; | z-index: 10000; | ||
display: flex; | |||
flex-direction: column; | |||
align-items: center; | |||
animation: cardZoomIn 0.3s ease; | |||
} | |||
.spark-modal-content { | |||
position: relative; | |||
z-index: 10011; | |||
max-width: 90vw; | |||
max-height: 85vh; | |||
overflow-y: auto; | |||
padding: 20px; | |||
animation: cardZoomIn 0.3s ease; | animation: cardZoomIn 0.3s ease; | ||
} | } | ||
| 第91行: | 第125行: | ||
/* 放大后的卡牌样式 */ | /* 放大后的卡牌样式 */ | ||
.card-modal-content .card-deck-trans { | .card-modal-content > .card-deck-trans, | ||
.card-modal-content > .card-wrapper > .card-deck-trans { | |||
width: 270px !important; | |||
height: 384px !important; | |||
transform: none !important; | |||
filter: none !important; | |||
cursor: default; | |||
} | |||
.card-modal-content > .card-deck-trans:hover, | |||
.card-modal-content > .card-wrapper > .card-deck-trans:hover { | |||
transform: none !important; | |||
filter: none !important; | |||
} | |||
.card-modal-content > .card-deck-trans > .card-deck, | |||
.card-modal-content > .card-wrapper > .card-deck-trans > .card-deck { | |||
transform: scale(0.6) !important; | |||
} | |||
/* 闪光按钮 */ | |||
.spark-button { | |||
margin-top: 20px; | |||
padding: 12px 36px; | |||
font-size: 18px; | |||
font-weight: bold; | |||
color: #fff; | |||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); | |||
border: 2px solid #a78bfa; | |||
border-radius: 8px; | |||
cursor: pointer; | |||
transition: all 0.3s ease; | |||
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4); | |||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3); | |||
} | |||
.spark-button:hover { | |||
background: linear-gradient(135deg, #764ba2 0%, #667eea 100%); | |||
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.6); | |||
transform: translateY(-2px); | |||
} | |||
.spark-button:active { | |||
transform: translateY(0); | |||
box-shadow: 0 2px 10px rgba(102, 126, 234, 0.4); | |||
} | |||
/* 闪光图标动画 */ | |||
.spark-button::before { | |||
content: "✦ "; | |||
animation: sparkle 1.5s ease-in-out infinite; | |||
} | |||
.spark-button::after { | |||
content: " ✦"; | |||
animation: sparkle 1.5s ease-in-out infinite 0.75s; | |||
} | |||
@keyframes sparkle { | |||
0%, 100% { opacity: 0.5; } | |||
50% { opacity: 1; } | |||
} | |||
/* 闪光卡牌列表样式 */ | |||
.spark-modal-title { | |||
text-align: center; | |||
color: #fff; | |||
font-size: 24px; | |||
font-weight: bold; | |||
margin-bottom: 20px; | |||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5); | |||
} | |||
.spark-modal-title::before { | |||
content: "✦ "; | |||
color: #a78bfa; | |||
} | |||
.spark-modal-title::after { | |||
content: " ✦"; | |||
color: #a78bfa; | |||
} | |||
.spark-card-list { | |||
display: flex; | |||
flex-wrap: wrap; | |||
justify-content: center; | |||
gap: 15px; | |||
} | |||
.spark-card-list .card-wrapper { | |||
display: inline-block; | |||
} | |||
.spark-card-list .card-deck-trans { | |||
cursor: pointer; | |||
} | |||
.spark-card-list .card-deck-trans:hover { | |||
transform: translateY(-8px); | |||
filter: brightness(1.1); | |||
} | |||
/* 加载提示 */ | |||
.spark-loading { | |||
color: #fff; | |||
font-size: 18px; | |||
text-align: center; | |||
padding: 40px; | |||
} | |||
.spark-loading::after { | |||
content: ""; | |||
animation: loadingDots 1.5s infinite; | |||
} | |||
@keyframes loadingDots { | |||
0% { content: ""; } | |||
25% { content: "."; } | |||
50% { content: ".."; } | |||
75% { content: "..."; } | |||
} | |||
/* 空列表提示 */ | |||
.spark-empty { | |||
color: #999; | |||
font-size: 16px; | |||
text-align: center; | |||
padding: 40px; | |||
} | |||
/* 放大的闪光卡牌 */ | |||
.spark-enlarged-container { | |||
position: fixed; | |||
top: 0; | |||
left: 0; | |||
width: 100%; | |||
height: 100%; | |||
display: flex; | |||
justify-content: center; | |||
align-items: center; | |||
background: rgba(0, 0, 0, 0.95); | |||
z-index: 10015; | |||
animation: cardZoomIn 0.3s ease; | |||
} | |||
.spark-enlarged-card { | |||
position: relative; | |||
display: flex; | |||
flex-direction: column; | |||
align-items: center; | |||
} | |||
.spark-enlarged-card .card-deck-trans { | |||
width: 270px !important; | width: 270px !important; | ||
height: 384px !important; | height: 384px !important; | ||
transform: none !important; | transform: none !important; | ||
filter: none !important; | filter: none !important; | ||
cursor: default; | |||
} | } | ||
. | .spark-enlarged-card .card-deck-trans:hover { | ||
transform: none !important; | transform: none !important; | ||
filter: none !important; | filter: none !important; | ||
} | } | ||
. | .spark-enlarged-card .card-deck { | ||
transform: scale(0.6) !important; | transform: scale(0.6) !important; | ||
} | |||
.spark-enlarged-close { | |||
position: fixed; | |||
top: 20px; | |||
right: 30px; | |||
font-size: 48px; | |||
font-weight: bold; | |||
color: #ffffff; | |||
cursor: pointer; | |||
z-index: 10016; | |||
line-height: 1; | |||
transition: color 0.2s ease, transform 0.2s ease; | |||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.5); | |||
} | |||
.spark-enlarged-close:hover { | |||
color: #ff6b6b; | |||
transform: scale(1.1); | |||
} | } | ||
| 第110行: | 第317行: | ||
body.card-modal-open { | body.card-modal-open { | ||
overflow: hidden; | overflow: hidden; | ||
} | |||
/* 滚动条样式 */ | |||
.spark-modal-content::-webkit-scrollbar { | |||
width: 8px; | |||
} | |||
.spark-modal-content::-webkit-scrollbar-track { | |||
background: rgba(255, 255, 255, 0.1); | |||
border-radius: 4px; | |||
} | |||
.spark-modal-content::-webkit-scrollbar-thumb { | |||
background: rgba(255, 255, 255, 0.3); | |||
border-radius: 4px; | |||
} | |||
.spark-modal-content::-webkit-scrollbar-thumb:hover { | |||
background: rgba(255, 255, 255, 0.5); | |||
} | } | ||
</style> | </style> | ||
| 第125行: | 第351行: | ||
function initCardWidget() { | function initCardWidget() { | ||
// | // 创建主模态框元素(如果不存在) | ||
var modalOverlay = document.querySelector('.card-modal-overlay'); | |||
if (!modalOverlay) { | if (!modalOverlay) { | ||
| 第135行: | 第361行: | ||
} | } | ||
// 创建闪光卡牌模态框(如果不存在) | |||
var sparkModalOverlay = document.querySelector('.spark-modal-overlay'); | |||
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); | |||
} | |||
// | var modalContent = modalOverlay.querySelector('.card-modal-content'); | ||
var closeButton = modalOverlay.querySelector('.card-modal-close'); | |||
var sparkModalContent = sparkModalOverlay.querySelector('.spark-modal-content'); | |||
var sparkCloseButton = sparkModalOverlay.querySelector('.spark-modal-close'); | |||
// 当前放大的闪光卡牌容器 | |||
var currentEnlargedContainer = null; | |||
// 关闭主模态框函数 | |||
function closeModal() { | function closeModal() { | ||
modalOverlay.classList.add('fade-out'); | modalOverlay.classList.add('fade-out'); | ||
| 第148行: | 第389行: | ||
} | } | ||
// | // 关闭闪光模态框函数 | ||
function openModal(cardElement) { | function closeSparkModal() { | ||
// 先关闭放大的卡牌 | |||
if (currentEnlargedContainer) { | |||
currentEnlargedContainer.remove(); | |||
currentEnlargedContainer = null; | |||
} | |||
sparkModalOverlay.classList.add('fade-out'); | |||
setTimeout(function() { | |||
sparkModalOverlay.classList.remove('active', 'fade-in', 'fade-out'); | |||
sparkModalContent.innerHTML = ''; | |||
}, 300); | |||
} | |||
// 关闭放大的闪光卡牌 | |||
function closeEnlargedCard() { | |||
if (currentEnlargedContainer) { | |||
currentEnlargedContainer.remove(); | |||
currentEnlargedContainer = null; | |||
} | |||
} | |||
// 打开主模态框函数 | |||
function openModal(cardElement, cardId, sparkEnable) { | |||
var cardClone = cardElement.cloneNode(true); | |||
// 移除克隆卡牌的点击事件标记 | |||
delete cardClone.dataset.cardBound; | |||
modalContent.innerHTML = ''; | modalContent.innerHTML = ''; | ||
modalContent.appendChild(cardClone); | modalContent.appendChild(cardClone); | ||
// 如果启用了闪光功能,添加闪光按钮 | |||
if (sparkEnable && sparkEnable.trim() !== '') { | |||
var sparkButton = document.createElement('button'); | |||
sparkButton.className = 'spark-button'; | |||
sparkButton.textContent = '闪光'; | |||
sparkButton.dataset.cardId = cardId; | |||
sparkButton.addEventListener('click', function() { | |||
openSparkModal(cardId); | |||
}); | |||
modalContent.appendChild(sparkButton); | |||
} | |||
modalOverlay.classList.add('active'); | modalOverlay.classList.add('active'); | ||
| 第160行: | 第439行: | ||
requestAnimationFrame(function() { | requestAnimationFrame(function() { | ||
modalOverlay.classList.add('fade-in'); | modalOverlay.classList.add('fade-in'); | ||
}); | |||
} | |||
// 打开闪光卡牌模态框 | |||
function openSparkModal(cardId) { | |||
sparkModalContent.innerHTML = '<div class="spark-modal-title">闪光卡牌列表</div><div class="spark-loading">加载中</div>'; | |||
sparkModalOverlay.classList.add('active'); | |||
requestAnimationFrame(function() { | |||
sparkModalOverlay.classList.add('fade-in'); | |||
}); | |||
// 通过API获取闪光卡牌列表 | |||
var api = new mw.Api(); | |||
api.get({ | |||
action: 'parse', | |||
text: '{{Card/spark/list|' + cardId + '}}', | |||
prop: 'text', | |||
disablelimitreport: true, | |||
format: 'json' | |||
}).done(function(data) { | |||
if (data.parse && data.parse.text) { | |||
var html = data.parse.text['*']; | |||
// 检查是否有卡牌 | |||
var tempDiv = document.createElement('div'); | |||
tempDiv.innerHTML = html; | |||
var cards = tempDiv.querySelectorAll('.card-deck-trans'); | |||
if (cards.length > 0) { | |||
sparkModalContent.innerHTML = '<div class="spark-modal-title">闪光卡牌列表</div>' + html; | |||
// 为闪光列表中的卡牌绑定事件 | |||
bindSparkListCardEvents(); | |||
} else { | |||
sparkModalContent.innerHTML = '<div class="spark-modal-title">闪光卡牌列表</div><div class="spark-empty">暂无闪光卡牌</div>'; | |||
} | |||
} | |||
}).fail(function() { | |||
sparkModalContent.innerHTML = '<div class="spark-modal-title">闪光卡牌列表</div><div class="spark-empty">加载失败,请重试</div>'; | |||
}); | |||
} | |||
// 为闪光列表中的卡牌绑定点击事件 | |||
function bindSparkListCardEvents() { | |||
var sparkCards = sparkModalContent.querySelectorAll('.card-deck-trans'); | |||
sparkCards.forEach(function(card) { | |||
if (card.dataset.sparkBound) return; | |||
card.dataset.sparkBound = 'true'; | |||
card.addEventListener('click', function(e) { | |||
e.preventDefault(); | |||
e.stopPropagation(); | |||
// 关闭之前放大的卡牌 | |||
closeEnlargedCard(); | |||
// 创建放大容器 | |||
var enlargedContainer = document.createElement('div'); | |||
enlargedContainer.className = 'spark-enlarged-container'; | |||
// 添加关闭按钮 | |||
var closeBtn = document.createElement('span'); | |||
closeBtn.className = 'spark-enlarged-close'; | |||
closeBtn.innerHTML = '×'; | |||
closeBtn.addEventListener('click', function(e) { | |||
e.stopPropagation(); | |||
closeEnlargedCard(); | |||
}); | |||
enlargedContainer.appendChild(closeBtn); | |||
// 创建卡牌内容区 | |||
var cardContent = document.createElement('div'); | |||
cardContent.className = 'spark-enlarged-card'; | |||
var cardClone = this.cloneNode(true); | |||
delete cardClone.dataset.sparkBound; | |||
cardContent.appendChild(cardClone); | |||
enlargedContainer.appendChild(cardContent); | |||
// 点击背景关闭 | |||
enlargedContainer.addEventListener('click', function(e) { | |||
if (e.target === enlargedContainer) { | |||
closeEnlargedCard(); | |||
} | |||
}); | |||
document.body.appendChild(enlargedContainer); | |||
currentEnlargedContainer = enlargedContainer; | |||
}); | |||
}); | }); | ||
} | } | ||
| 第167行: | 第536行: | ||
e.stopPropagation(); | e.stopPropagation(); | ||
closeModal(); | closeModal(); | ||
}); | |||
sparkCloseButton.addEventListener('click', function(e) { | |||
e.stopPropagation(); | |||
closeSparkModal(); | |||
}); | }); | ||
| 第173行: | 第547行: | ||
if (e.target === modalOverlay) { | if (e.target === modalOverlay) { | ||
closeModal(); | closeModal(); | ||
} | |||
}); | |||
sparkModalOverlay.addEventListener('click', function(e) { | |||
if (e.target === sparkModalOverlay) { | |||
closeSparkModal(); | |||
} | } | ||
}); | }); | ||
| 第178行: | 第558行: | ||
// ESC键关闭 | // ESC键关闭 | ||
document.addEventListener('keydown', function(e) { | document.addEventListener('keydown', function(e) { | ||
if (e.key === 'Escape' | if (e.key === 'Escape') { | ||
// 优先关闭放大的闪光卡牌 | |||
if (currentEnlargedContainer) { | |||
closeEnlargedCard(); | |||
} else if (sparkModalOverlay.classList.contains('active')) { | |||
closeSparkModal(); | |||
} else if (modalOverlay.classList.contains('active')) { | |||
closeModal(); | |||
} | |||
} | } | ||
}); | }); | ||
| 第185行: | 第572行: | ||
// 为所有卡牌绑定点击事件 | // 为所有卡牌绑定点击事件 | ||
function bindCardEvents() { | function bindCardEvents() { | ||
var cards = document.querySelectorAll('.card-deck-trans'); | |||
cards.forEach(function(card) { | cards.forEach(function(card) { | ||
| 第193行: | 第580行: | ||
// 排除模态框内的卡牌 | // 排除模态框内的卡牌 | ||
if (card.closest('.card-modal-overlay')) return; | if (card.closest('.card-modal-overlay') || card.closest('.spark-modal-overlay') || card.closest('.spark-enlarged-container')) return; | ||
card.addEventListener('click', function(e) { | card.addEventListener('click', function(e) { | ||
e.preventDefault(); | e.preventDefault(); | ||
openModal(this); | |||
// 从card-deck-trans元素获取spark_enable | |||
var sparkEnable = this.dataset.sparkEnable || ''; | |||
// 获取card-id从父级card-wrapper | |||
var wrapper = this.closest('.card-wrapper'); | |||
var cardId = wrapper ? wrapper.dataset.cardId : ''; | |||
openModal(this, cardId, sparkEnable); | |||
}); | }); | ||
}); | }); | ||
| 第206行: | 第601行: | ||
// 监听DOM变化,为动态加载的卡牌绑定事件 | // 监听DOM变化,为动态加载的卡牌绑定事件 | ||
var observer = new MutationObserver(function(mutations) { | |||
var shouldBind = false; | |||
mutations.forEach(function(mutation) { | mutations.forEach(function(mutation) { | ||
if (mutation.addedNodes.length > 0) { | if (mutation.addedNodes.length > 0) { | ||
2026年1月17日 (六) 09:53的版本
此Widget为卡牌显示添加交互效果:
- 鼠标悬停时的动画效果
- 点击后显示放大的模态框
- 自动检测spark_enable属性显示闪光按钮
- 支持闪光卡牌列表显示
使用方法:在页面底部添加