feat: Add 3D animation effect to cover image

This commit is contained in:
levywang 2025-03-20 18:19:18 +08:00
parent 79bdecdc89
commit 76a6259cac
2 changed files with 726 additions and 217 deletions

View File

@ -3,6 +3,12 @@
// 添加全局变量
let currentTab = 'search'; // 默认标签页
// 添加视频URL状态管理
let currentVideoUrl = ''; // 存储当前视频URL
// 添加全局变量来跟踪视频播放状态
let wasPlaying = false; // 记录切换标签页前的播放状态
function switchTab(tabName) {
// 更新当前标签页
currentTab = tabName;
@ -10,6 +16,7 @@ function switchTab(tabName) {
// 获取所有标签页内容和按钮
const tabs = document.querySelectorAll('.tab-content');
const buttons = document.querySelectorAll('.tab-button');
const videoPlayer = document.getElementById('videoPlayer');
// 隐藏所有标签页内容
tabs.forEach(tab => {
@ -27,9 +34,25 @@ function switchTab(tabName) {
// 激活对应的按钮
document.querySelector(`[data-tab="${tabName}"]`).classList.add('active');
// 如果切换到播放器标签页,加载视频
// 处理视频播放状态
if (tabName === 'player') {
loadVideo();
// 切换到播放器标签页
if (!currentVideoUrl) {
loadVideo();
} else if (wasPlaying && videoPlayer) {
// 如果之前是播放状态,恢复播放
videoPlayer.play().catch(e => console.error('Resume play failed:', e));
}
} else {
// 切换到其他标签页
if (videoPlayer) {
// 保存当前播放状态
wasPlaying = !videoPlayer.paused;
// 暂停视频
if (!videoPlayer.paused) {
videoPlayer.pause();
}
}
}
}
@ -193,7 +216,7 @@ function showCoverImage(searchTerm) {
if (!coverImageContainer) {
coverImageContainer = document.createElement('div');
coverImageContainer.id = 'coverImageContainer';
coverImageContainer.className = 'cover-image-container hidden';
coverImageContainer.className = 'cover-image-container hidden card-3d';
// 创建图片元素
image = document.createElement('img');
@ -206,6 +229,11 @@ function showCoverImage(searchTerm) {
// 将容器添加到搜索结果之前
document.getElementById('searchResults').insertAdjacentElement('beforebegin', coverImageContainer);
} else {
// 确保容器有3D卡片类
if (!coverImageContainer.classList.contains('card-3d')) {
coverImageContainer.classList.add('card-3d');
}
}
const modal = document.getElementById('imageModal');
@ -245,6 +273,9 @@ function showCoverImage(searchTerm) {
coverImageContainer.style.transition = 'opacity 0.3s ease';
coverImageContainer.style.opacity = '1';
image.classList.add('loaded');
// 添加3D效果初始化
initCard3DEffect(coverImageContainer);
});
};
@ -254,12 +285,80 @@ function showCoverImage(searchTerm) {
};
// 点击图片显示大图
coverImageContainer.onclick = () => {
modalImage.src = imageUrl;
coverImageContainer.onclick = (e) => {
// 获取点击位置相对于容器的坐标
const rect = coverImageContainer.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
// 将容器分为3x3的网格根据点击位置选择不同的图片
const width = rect.width;
const height = rect.height;
// 水平分为左、中、右三部分
const xSection = Math.floor(x / (width / 3));
// 垂直分为上、中、下三部分
const ySection = Math.floor(y / (height / 3));
// 根据9宫格位置选择不同的图片
const position = ySection * 3 + xSection;
// 预加载图片,确保图片存在再显示
const preloadImage = new Image();
// 所有位置都使用相同的图片URL
const newImageUrl = imageUrl;
// 显示加载指示器
const loadingIndicator = document.createElement('div');
loadingIndicator.className = 'modal-loading';
modal.querySelector('.modal-content').appendChild(loadingIndicator);
// 预加载图片
preloadImage.onload = () => {
// 图片加载成功设置src并显示模态框
modalImage.src = newImageUrl;
modalImage.classList.add('fullwidth-preview');
modal.classList.remove('hidden');
// 移除加载指示器
if (loadingIndicator.parentNode) {
loadingIndicator.parentNode.removeChild(loadingIndicator);
}
setTimeout(() => {
modal.classList.add('active');
}, 10);
};
preloadImage.onerror = () => {
console.log(`预览图 ${newImageUrl} 加载失败`);
// 移除加载指示器
if (loadingIndicator.parentNode) {
loadingIndicator.parentNode.removeChild(loadingIndicator);
}
// 显示错误提示
modalImage.classList.add('error');
modal.classList.remove('hidden');
setTimeout(() => {
modal.classList.add('active');
}, 10);
};
// 开始加载图片
preloadImage.src = newImageUrl;
// 先显示模态框,但图片为空
modalImage.src = '';
modalImage.classList.remove('error');
modalImage.classList.remove('fullwidth-preview');
modal.classList.remove('hidden');
setTimeout(() => {
modal.classList.add('active');
}, 10);
// 显示模态框时初始化事件
initializeModalEvents();
};
} else {
// 如果不是番号格式,隐藏图片容器
@ -267,39 +366,114 @@ function showCoverImage(searchTerm) {
}
}
// 初始化3D卡片效果
function initCard3DEffect(card) {
if (!card) return;
// 移除之前可能添加的事件监听器
card.removeEventListener('mousemove', handleMouseMove);
card.removeEventListener('mouseleave', handleMouseLeave);
card.removeEventListener('mouseenter', handleMouseEnter);
// 添加事件监听器
card.addEventListener('mousemove', handleMouseMove);
card.addEventListener('mouseleave', handleMouseLeave);
card.addEventListener('mouseenter', handleMouseEnter);
}
// 处理鼠标移动事件
function handleMouseMove(e) {
const card = this;
const rect = card.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
// 计算鼠标位置相对于卡片中心的偏移
const centerX = rect.width / 2;
const centerY = rect.height / 2;
const deltaX = (x - centerX) / centerX;
const deltaY = (y - centerY) / centerY;
// 减小旋转角度从15度减小到8度
const rotateX = deltaY * -8;
const rotateY = deltaX * 8;
// 应用3D变换减小缩放比例
card.style.transform = `perspective(1500px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) scale3d(1.02, 1.02, 1.02)`;
// 减小光影效果的强度
card.style.boxShadow = `
0 5px 15px rgba(0,0,0,0.2),
${deltaX * 5}px ${deltaY * 5}px 15px rgba(0,0,0,0.1)
`;
// 减小图片内部的视差效果
const image = card.querySelector('img');
if (image) {
image.style.transform = `translateX(${deltaX * -5}px) translateY(${deltaY * -5}px)`;
// 添加亮度调整,使图片在不同角度下亮度均匀
const brightness = 1 + (deltaX * 0.05);
image.style.filter = `brightness(${brightness})`;
}
}
// 处理鼠标离开事件
function handleMouseLeave() {
const card = this;
// 重置变换
card.style.transform = 'perspective(1500px) rotateX(0) rotateY(0) scale3d(1, 1, 1)';
card.style.boxShadow = '0 4px 10px rgba(0, 0, 0, 0.2)';
// 重置图片位置和滤镜
const image = card.querySelector('img');
if (image) {
image.style.transform = 'translateX(0) translateY(0)';
image.style.filter = 'brightness(1)';
}
}
// 处理鼠标进入事件
function handleMouseEnter() {
const card = this;
// 添加初始变换效果,减小缩放比例
card.style.transition = 'transform 0.3s ease, box-shadow 0.3s ease';
card.style.transform = 'perspective(1500px) scale3d(1.01, 1.01, 1.01)';
card.style.boxShadow = '0 5px 15px rgba(0,0,0,0.2)';
// 短暂延迟后移除过渡效果,使鼠标移动时的变换更加流畅
setTimeout(() => {
card.style.transition = 'none';
}, 300); // 增加延迟时间,使过渡更平滑
}
// 视频播放功能
let hls = null;
let autoplayEnabled = false;
let autoNextEnabled = false;
let autoplayEnabled = localStorage.getItem('autoplay') === 'true'; // 从localStorage读取初始值
let autoNextEnabled = localStorage.getItem('autoNext') === 'true'; // 从localStorage读取初始值
// 初始化自动播放设置
function initializeAutoplaySettings() {
const autoplayToggle = document.getElementById('autoplayToggle');
if (autoplayToggle) {
// 从localStorage读取自动播放设置
autoplayEnabled = localStorage.getItem('autoplayEnabled') === 'true';
autoplayToggle.checked = autoplayEnabled;
// 监听自动播放设置变化
autoplayToggle.addEventListener('change', (e) => {
autoplayEnabled = e.target.checked;
localStorage.setItem('autoplayEnabled', autoplayEnabled);
});
}
}
function initializeAutoNextToggle() {
const autoNextToggle = document.getElementById('autoNextToggle');
if (autoNextToggle) {
// 从localStorage读取状态
autoNextEnabled = localStorage.getItem('autoNextEnabled') === 'true';
autoNextToggle.checked = autoNextEnabled;
// 绑定change事件
autoNextToggle.addEventListener('change', (e) => {
autoNextEnabled = e.target.checked;
localStorage.setItem('autoNextEnabled', autoNextEnabled);
});
}
// 从localStorage读取设置
autoplayToggle.checked = autoplayEnabled;
autoNextToggle.checked = autoNextEnabled;
// 监听自动播放开关变化
autoplayToggle.addEventListener('change', function() {
autoplayEnabled = this.checked;
localStorage.setItem('autoplay', this.checked);
});
// 监听自动下一个开关变化
autoNextToggle.addEventListener('change', function() {
autoNextEnabled = this.checked;
localStorage.setItem('autoNext', this.checked);
});
}
// 初始化封面图开关设置
@ -334,130 +508,136 @@ function initializeCoverToggle() {
});
}
async function loadVideo() {
// 修改 loadVideo 函数
function loadVideo() {
const videoPlayer = document.getElementById('videoPlayer');
const videoSourceUrl = document.getElementById('videoSourceUrl');
const notification = document.getElementById('notification');
const sourceUrlElement = document.getElementById('videoSourceUrl');
const showCover = document.getElementById('coverToggle').checked;
try {
// 添加加载中状态
videoPlayer.classList.add('loading');
// 显示加载中通知
notification.innerHTML = `
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<span>${translations[currentLang].loadingVideo}</span>
`;
notification.classList.add('show');
// 预加载封面图
const response = await fetch(`${API_CONFIG.BASE_URL}${API_CONFIG.ENDPOINTS.VIDEO}`);
const data = await response.json();
// 创建一个Image对象来预加载封面图
const img = new Image();
img.src = data.img_url;
// 等待封面图加载完成
await new Promise((resolve) => {
img.onload = resolve;
img.onerror = resolve; // 如果加载失败也继续
});
// 销毁之前的HLS实例
if (hls) {
hls.destroy();
hls = null;
// 如果已经有视频URL则不重新请求
if (currentVideoUrl) {
if (videoSourceUrl) {
videoSourceUrl.textContent = currentVideoUrl;
}
// 设置视频封面(如果开启了封面图显示)
if (showCover && data.img_url) {
videoPlayer.poster = data.img_url;
} else {
videoPlayer.poster = ''; // 清除封面图
}
// 更新视频源地址显示
sourceUrlElement.textContent = data.url;
// 根据视频URL类型选择播放方式
if (data.url.endsWith('.m3u8')) {
if (Hls.isSupported()) {
hls = new Hls();
hls.loadSource(data.url);
hls.attachMedia(videoPlayer);
hls.on(Hls.Events.MANIFEST_PARSED, () => {
if (autoplayEnabled) {
videoPlayer.play().catch(e => console.error('Auto-play failed:', e));
}
});
} else if (videoPlayer.canPlayType('application/vnd.apple.mpegurl')) {
videoPlayer.src = data.url;
if (autoplayEnabled) {
videoPlayer.play().catch(e => console.error('Auto-play failed:', e));
}
}
} else {
videoPlayer.src = data.url;
if (videoPlayer && videoPlayer.src !== currentVideoUrl) {
videoPlayer.src = currentVideoUrl;
// 根据自动播放设置决定是否播放
if (autoplayEnabled) {
videoPlayer.play().catch(e => console.error('Auto-play failed:', e));
}
}
// 确保播放器控件可见
videoPlayer.addEventListener('webkitfullscreenchange', () => {
if (document.webkitFullscreenElement) {
videoPlayer.style.display = 'block';
videoPlayer.controls = true;
}
});
videoPlayer.addEventListener('fullscreenchange', () => {
if (document.fullscreenElement) {
videoPlayer.style.display = 'block';
videoPlayer.controls = true;
}
});
videoPlayer.addEventListener('ended', ()=> {
if (autoNextEnabled) {
loadVideo()}
;
});
videoPlayer.classList.remove('loading');
notification.classList.remove('show');
} catch (error) {
console.error('加载视频出错:', error);
videoPlayer.classList.remove('loading');
sourceUrlElement.textContent = '';
notification.innerHTML = `
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<span>${translations[currentLang].videoError}</span>
`;
notification.style.background = '#dc2626';
notification.classList.add('show');
setTimeout(() => {
notification.classList.remove('show');
notification.style.background = '';
}, 3000);
return;
}
// 显示加载中通知
notification.innerHTML = `
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<span>${translations[currentLang].loadingVideo}</span>
`;
notification.classList.add('show');
// 如果没有视频URL则请求新的
fetch(`${API_CONFIG.BASE_URL}${API_CONFIG.ENDPOINTS.VIDEO}`)
.then(response => response.json())
.then(data => {
if (data && data.url) {
currentVideoUrl = data.url; // 保存视频URL
if (videoSourceUrl) {
videoSourceUrl.textContent = data.url;
}
if (videoPlayer) {
// 设置视频封面(如果开启了封面图显示)
if (showCover && data.img_url) {
videoPlayer.poster = data.img_url;
} else {
videoPlayer.poster = ''; // 清除封面图
}
videoPlayer.src = data.url;
// 根据自动播放设置决定是否播放
if (autoplayEnabled) {
videoPlayer.play().catch(e => console.error('Auto-play failed:', e));
}
// 添加视频结束事件监听
videoPlayer.onended = () => {
if (autoNextEnabled) {
clearVideoUrl(); // 清除当前URL
loadVideo(); // 加载下一个视频
}
};
}
// 隐藏加载通知
notification.classList.remove('show');
}
})
.catch(error => {
console.error('加载视频失败:', error);
notification.innerHTML = `
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
<span>${translations[currentLang].videoError}</span>
`;
notification.style.background = '#dc2626';
setTimeout(() => {
notification.classList.remove('show');
notification.style.background = '';
}, 3000);
});
}
// 初始化视频播放器
document.addEventListener('DOMContentLoaded', () => {
initializeAutoplaySettings();
initializeAutoplaySettings(); // 这个函数现在已经包含了自动播放和自动下一个的初始化
initializeCopyButton();
initializeCoverToggle();
initializeAutoNextToggle(); // 新增初始化
// 修改下一个按钮的事件监听
const nextVideoButton = document.getElementById('nextVideo');
if (nextVideoButton) {
nextVideoButton.addEventListener('click', loadVideo);
nextVideoButton.addEventListener('click', () => {
clearVideoUrl(); // 使用clearVideoUrl函数来处理
});
}
// 初始化模态框关闭功能
const imageModal = document.getElementById('imageModal');
const closeModal = document.getElementById('closeModal');
if (imageModal && closeModal) {
// 点击关闭按钮关闭模态框
closeModal.addEventListener('click', () => {
imageModal.classList.remove('active');
setTimeout(() => {
imageModal.classList.add('hidden');
}, 300);
});
// 点击模态框背景关闭模态框
imageModal.addEventListener('click', (e) => {
if (e.target === imageModal) {
imageModal.classList.remove('active');
setTimeout(() => {
imageModal.classList.add('hidden');
}, 300);
}
});
// ESC键关闭模态框
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && !imageModal.classList.contains('hidden')) {
imageModal.classList.remove('active');
setTimeout(() => {
imageModal.classList.add('hidden');
}, 300);
}
});
}
});
@ -802,7 +982,9 @@ document.addEventListener('DOMContentLoaded', () => {
// 下一个视频按钮
const nextVideoButton = document.getElementById('nextVideo');
if (nextVideoButton) {
nextVideoButton.addEventListener('click', loadVideo);
nextVideoButton.addEventListener('click', () => {
clearVideoUrl(); // 使用clearVideoUrl函数来处理
});
}
});
@ -1067,6 +1249,14 @@ function setLanguage(lang) {
el.textContent = translations[lang][messageKey];
}
});
// 更新模态框关闭提示文本
const modal = document.getElementById('imageModal');
if (modal) {
modal.setAttribute('data-close-text',
lang === 'en' ? 'Click anywhere to close' : '点击任意位置关闭'
);
}
}
// 更新分页控件文本
@ -1538,15 +1728,52 @@ function showThemeMenu(button) {
document.addEventListener('keydown', handleEscape);
}
// 注册 Service Worker
if ('serviceWorker' in navigator) {
// 修改 Service Worker 注册代码
if ('serviceWorker' in navigator && window.location.protocol === 'https:') {
window.addEventListener('load', () => {
navigator.serviceWorker.register('/sw.js')
navigator.serviceWorker.register('./sw.js') // 使用相对路径
.then(registration => {
console.log('ServiceWorker registration successful');
})
.catch(err => {
console.log('ServiceWorker registration failed: ', err);
console.error('ServiceWorker registration failed: ', err);
});
});
} else {
console.log('ServiceWorker is not supported or requires HTTPS');
}
// 修改模态框点击事件处理
function initializeModalEvents() {
const modal = document.getElementById('imageModal');
const modalImage = document.getElementById('modalImage');
// 设置关闭提示文本
modal.setAttribute('data-close-text',
currentLang === 'en' ? 'Click anywhere to close' : '点击任意位置关闭'
);
// 点击模态框任意位置关闭
modal.addEventListener('click', (e) => {
// 添加关闭动画
modal.classList.remove('active');
modalImage.style.transform = 'scale(0.9)';
modalImage.style.opacity = '0';
// 延迟隐藏模态框,等待动画完成
setTimeout(() => {
modal.classList.add('hidden');
// 重置图片样式
modalImage.style.transform = '';
modalImage.style.opacity = '';
}, 300);
});
}
// 添加清除视频URL的函数可以在需要重新加载视频时调用
function clearVideoUrl() {
if (currentVideoUrl) { // 只有在有当前URL时才清除并重新加载
currentVideoUrl = '';
loadVideo();
}
}

View File

@ -835,53 +835,66 @@ body.light-theme select option {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 16px;
padding: 32px;
width: 100%;
position: static;
background: var(--background-dark);
border-radius: 8px;
margin: 20px auto;
max-width: 400px;
}
/* 加载动画 */
/* 加载动画圆圈 */
.loading-spinner {
width: 40px;
height: 40px;
border: 3px solid var(--border-color);
border-top-color: var(--primary-color);
border-radius: 50%;
animation: spin 1s linear infinite;
transform-origin: center center;
animation: loading-spin 1s linear infinite;
margin: 0 auto;
display: block;
}
/* 加载动画旋转效果 */
@keyframes loading-spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* 加载文本样式 */
.loading-text {
color: var(--text-secondary);
font-size: 14px;
position: relative;
display: inline-block;
text-align: center;
margin: 0 auto;
}
/* 加载文本点动画 */
.loading-text::after {
content: '';
animation: dots 1.5s steps(4, end) infinite;
animation: loading-dots 1.5s steps(4, end) infinite;
}
@keyframes dots {
/* 点动画关键帧 */
@keyframes loading-dots {
0%, 20% { content: ''; }
40% { content: '.'; }
60% { content: '..'; }
80%, 100% { content: '...'; }
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
/* 搜索结果容器 */
#searchResults {
position: relative;
z-index: 1;
}
/* 亮色主题下的标签页容器样式 */
[data-theme="light"] .tab-container {
background: #ffffff;
border-color: #e5e7eb;
/* 搜索结果中的加载容器 */
#searchResults .loading-container {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
/* Logo 样式 */
@ -995,31 +1008,37 @@ body.light-theme select option {
height: 20px;
}
/* 视频源地址样式 */
/* 视频源地址容器样式 */
.video-source {
background: var(--bg-color);
border: 1px solid var(--border-color);
margin-bottom: 1rem;
display: flex;
align-items: center;
flex-wrap: nowrap; /* 防止换行 */
gap: 0.5rem;
padding: 0.75rem 1rem;
}
.source-url {
color: var(--text-color);
padding: 0.25rem 0;
word-break: break-all;
/* 视频源URL容器 */
.video-source .flex-grow {
min-width: 0; /* 允许容器缩小 */
flex-shrink: 1; /* 允许收缩 */
}
/* 复制按钮样式 */
.copy-button {
background: var(--primary-color);
color: var(--bg-color);
transition: all 0.2s;
flex-shrink: 0; /* 防止按钮缩小 */
white-space: nowrap; /* 防止文字换行 */
padding: 0.5rem 1rem;
min-width: fit-content; /* 确保按钮宽度适应内容 */
}
.copy-button:hover {
opacity: 0.9;
}
.copy-button.copied {
background: var(--success-color);
/* 源地址URL样式 */
.source-url {
text-overflow: ellipsis; /* 超出显示省略号 */
overflow: hidden;
white-space: nowrap;
}
/* 深色主题适配 */
@ -1039,38 +1058,114 @@ body.light-theme select option {
margin: 20px auto;
text-align: center;
transition: all 0.3s ease;
border-radius: 8px;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
cursor: pointer;
min-height: 300px; /* 添加最小高度 */
min-height: 300px;
background-color: rgba(0, 0, 0, 0.05);
}
.cover-image-container::before {
/* 添加提示文本,告诉用户可以点击不同区域 */
.cover-image-container::after {
content: '点击不同区域查看更多图片';
position: absolute;
bottom: 10px;
left: 50%;
transform: translateX(-50%);
background-color: rgba(0, 0, 0, 0.7);
color: white;
padding: 5px 10px;
border-radius: 4px;
font-size: 12px;
opacity: 0;
transition: opacity 0.3s ease;
z-index: 4;
pointer-events: none;
}
.cover-image-container:hover::after {
opacity: 1;
}
/* 增强模态框中的图片效果 */
.modal-image {
max-width: 100%;
max-height: 90vh;
object-fit: contain;
border-radius: 8px;
box-shadow: 0 5px 25px rgba(0, 0, 0, 0.5);
transition: opacity 0.3s ease, transform 0.3s ease;
opacity: 0;
}
.image-modal.active .modal-image {
opacity: 1;
animation: zoomIn 0.3s forwards;
}
@keyframes zoomIn {
from {
transform: scale(0.9);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
/* 修改3D卡片效果样式减小抖动幅度 */
.card-3d {
transform-style: preserve-3d;
transform: perspective(1500px); /* 增加透视距离,减小变形效果 */
transition: transform 0.3s ease, box-shadow 0.3s ease;
will-change: transform, box-shadow;
border-radius: 12px;
overflow: hidden;
position: relative;
cursor: pointer;
}
/* 修改光影效果,使光线更均匀 */
.card-3d::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(90deg,
rgba(255,255,255,0.1) 25%,
rgba(255,255,255,0.2) 50%,
rgba(255,255,255,0.1) 75%
background: linear-gradient(
135deg,
rgba(255, 255, 255, 0.1) 0%,
rgba(255, 255, 255, 0.05) 50%,
rgba(255, 255, 255, 0.1) 100%
);
animation: shimmer 1.5s infinite;
z-index: 1;
z-index: 3;
pointer-events: none;
transition: opacity 0.3s ease;
}
@keyframes shimmer {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(100%);
}
.card-3d:hover::before {
opacity: 0.6;
}
/* 移除辅助线 */
.card-3d:hover::after {
display: none;
}
/* 修改图片过渡效果,使其更平滑 */
.card-3d img {
transition: transform 0.3s ease;
transform-origin: center center;
backface-visibility: hidden;
position: relative;
z-index: 2;
filter: brightness(1.02); /* 轻微提高亮度 */
}
/* 确保图片加载后显示正常 */
.cover-image {
opacity: 0;
transition: opacity 0.3s ease, transform 0.3s ease;
@ -1089,7 +1184,7 @@ body.light-theme select option {
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.9);
background-color: rgba(0, 0, 0, 0.95);
display: flex;
justify-content: center;
align-items: center;
@ -1097,6 +1192,7 @@ body.light-theme select option {
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
cursor: pointer;
}
.image-modal.active {
@ -1110,26 +1206,16 @@ body.light-theme select option {
.modal-content {
position: relative;
max-width: 90%;
max-width: 96vw;
max-height: 90vh;
}
.modal-image {
max-width: 100%;
max-height: 90vh;
object-fit: contain;
display: flex;
justify-content: center;
align-items: center;
transition: transform 0.3s ease;
}
.modal-close {
position: absolute;
top: -40px;
right: 0;
background: none;
border: none;
color: white;
cursor: pointer;
padding: 8px;
transition: transform 0.2s ease;
display: none;
}
.modal-close:hover {
@ -1141,15 +1227,19 @@ body.light-theme select option {
height: 24px;
}
/* 添加键盘操作提示 */
.modal-content::after {
content: 'ESC 关闭';
position: absolute;
bottom: -30px;
/* 修改点击关闭提示,支持中英文 */
.image-modal::after {
content: attr(data-close-text);
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
color: rgba(255, 255, 255, 0.6);
font-size: 14px;
padding: 8px 16px;
background: rgba(0, 0, 0, 0.5);
border-radius: 4px;
pointer-events: none;
}
/* 下一个按钮样式 */
@ -1157,8 +1247,11 @@ body.light-theme select option {
background-color: var(--primary-color);
color: var(--text-color);
border-radius: 0.375rem;
font-weight: 500;
font-weight: 700; /* 加粗文字 */
transition: all 0.2s;
padding: 0.5rem 1rem;
white-space: nowrap; /* 防止文字换行 */
flex-shrink: 0; /* 防止缩小 */
}
.next-button:hover {
@ -1244,6 +1337,46 @@ body.light-theme select option {
background: rgba(0, 0, 0, 0.05);
padding: 0.75rem 1rem;
border-radius: 0.5rem;
display: flex;
align-items: center;
flex-wrap: nowrap; /* 防止换行 */
gap: 1rem; /* 添加间距 */
}
/* 自动播放开关容器 */
.autoplay-toggle {
display: flex;
align-items: center;
white-space: nowrap; /* 防止文字换行 */
flex-shrink: 0; /* 防止缩小 */
}
/* 确保开关文字不换行 */
.autoplay-toggle label {
white-space: nowrap;
}
/* 调整开关间距 */
.video-controls .autoplay-toggle + .autoplay-toggle {
margin-left: 1rem;
}
/* 确保移动端布局正确 */
@media (max-width: 640px) {
.video-controls {
flex-direction: row;
justify-content: space-between;
padding: 0.5rem;
gap: 0.5rem;
}
.autoplay-toggle label {
font-size: 0.875rem; /* 稍微减小字体大小 */
}
.next-button {
padding: 0.5rem 0.75rem;
}
}
/* 自动播放开关样式 */
@ -1498,3 +1631,152 @@ main {
z-index: 50;
margin-bottom: 60px;
}
/* 添加模态框加载指示器 */
.modal-loading {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 50px;
height: 50px;
border: 5px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
border-top-color: var(--primary-color);
animation: spin 1s ease-in-out infinite;
z-index: 10;
}
@keyframes spin {
to { transform: translate(-50%, -50%) rotate(360deg); }
}
/* 确保模态框内容区域有相对定位 */
.modal-content {
position: relative;
max-width: 90%;
max-height: 90vh;
min-height: 200px;
min-width: 200px;
display: flex;
justify-content: center;
align-items: center;
}
/* 改进模态框图片加载过程 */
.modal-image {
max-width: 100%;
max-height: 90vh;
object-fit: contain;
border-radius: 8px;
box-shadow: 0 5px 25px rgba(0, 0, 0, 0.5);
transition: opacity 0.3s ease, transform 0.3s ease;
opacity: 0;
}
.image-modal.active .modal-image {
opacity: 1;
animation: zoomIn 0.3s forwards;
}
/* 添加键盘事件处理支持ESC关闭和方向键切换图片 */
.image-modal.active {
opacity: 1;
visibility: visible;
}
/* 添加图片加载失败时的提示 */
.modal-image.error::before {
content: '图片加载失败';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
background: rgba(0, 0, 0, 0.7);
padding: 10px 20px;
border-radius: 4px;
font-size: 14px;
}
/* 全宽预览图样式 */
.fullwidth-preview {
width: 96vw !important;
max-width: 96vw !important;
height: auto !important;
max-height: 90vh !important;
object-fit: contain !important;
}
/* 改进模态框样式以适应全宽图片 */
.modal-content {
position: relative;
max-width: 96vw;
max-height: 90vh;
min-height: 200px;
min-width: 200px;
display: flex;
justify-content: center;
align-items: center;
}
/* 确保模态框内容居中 */
.image-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.95);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
opacity: 0;
visibility: hidden;
transition: all 0.3s ease;
}
/* 改进模态框图片加载过程 */
.modal-image {
max-width: 100%;
max-height: 90vh;
object-fit: contain;
border-radius: 8px;
box-shadow: 0 5px 25px rgba(0, 0, 0, 0.5);
transition: opacity 0.3s ease, transform 0.3s ease;
opacity: 0;
}
/* 添加图片加载错误状态 */
.modal-image.error {
min-width: 300px;
min-height: 200px;
background-color: rgba(0, 0, 0, 0.3);
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
.modal-image.error::after {
content: '图片加载失败';
position: absolute;
color: white;
font-size: 16px;
}
/* 添加提示文本 */
.image-modal::after {
content: '点击任意位置关闭';
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
color: rgba(255, 255, 255, 0.6);
font-size: 14px;
padding: 8px 16px;
background: rgba(0, 0, 0, 0.5);
border-radius: 4px;
pointer-events: none;
}