- 更新settings.local.json,移除冗余的权限设置,添加新的Playwright相关权限。 - 删除exhibition_demo_project_2025.md文档,清理不再使用的文件。 - 更新多个HTML页面,统一viewport设置,添加页面加载动画、错误处理和性能优化脚本。 - 统一使用Tailwind CSS的引入方式,提升页面加载性能。 - 增强导航组件,支持移动端菜单和返回顶部功能,改善用户体验。
387 lines
15 KiB
JavaScript
387 lines
15 KiB
JavaScript
// 页面加载动画组件
|
||
(function() {
|
||
// 创建加载器HTML结构
|
||
function createLoader() {
|
||
const loaderHTML = `
|
||
<div id="page-loader" class="fixed inset-0 bg-white z-[9999] flex items-center justify-center">
|
||
<!-- 背景动画 -->
|
||
<div class="absolute inset-0 overflow-hidden">
|
||
<!-- 渐变背景 -->
|
||
<div class="absolute inset-0 bg-gradient-to-br from-blue-50 via-purple-50 to-emerald-50"></div>
|
||
|
||
<!-- 浮动粒子 -->
|
||
<div class="floating-particles">
|
||
${Array.from({length: 12}).map((_, i) => `
|
||
<div class="particle particle-${i + 1}"></div>
|
||
`).join('')}
|
||
</div>
|
||
|
||
<!-- 几何图形 -->
|
||
<div class="geometric-shapes">
|
||
<div class="shape shape-1"></div>
|
||
<div class="shape shape-2"></div>
|
||
<div class="shape shape-3"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 主加载内容 -->
|
||
<div class="relative z-10 text-center">
|
||
<!-- Logo区域 -->
|
||
<div class="mb-8">
|
||
<div class="w-20 h-20 mx-auto mb-4 relative">
|
||
<!-- 旋转的环 -->
|
||
<div class="absolute inset-0 border-4 border-transparent border-t-emerald-500 border-r-blue-500 rounded-full animate-spin"></div>
|
||
<div class="absolute inset-2 border-4 border-transparent border-b-purple-500 border-l-pink-500 rounded-full animate-spin-reverse"></div>
|
||
|
||
<!-- 中心图标 -->
|
||
<div class="absolute inset-0 flex items-center justify-center">
|
||
<div class="w-8 h-8 bg-gradient-to-br from-emerald-400 to-blue-500 rounded-lg flex items-center justify-center text-white font-bold text-sm animate-pulse">
|
||
新
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 品牌名称 -->
|
||
<h1 class="text-2xl font-bold text-gray-800 mb-2 animate-fade-in">
|
||
长三角国际新能源汽车博览会
|
||
</h1>
|
||
<p class="text-gray-600 animate-fade-in-delay">
|
||
智行未来,绿动长三角
|
||
</p>
|
||
</div>
|
||
|
||
<!-- 进度条 -->
|
||
<div class="w-64 mx-auto mb-6">
|
||
<div class="bg-gray-200 rounded-full h-2 overflow-hidden">
|
||
<div id="loading-progress" class="h-full bg-gradient-to-r from-emerald-400 to-blue-500 rounded-full transition-all duration-300 ease-out" style="width: 0%"></div>
|
||
</div>
|
||
<div class="flex justify-between text-xs text-gray-500 mt-2">
|
||
<span>加载中</span>
|
||
<span id="loading-percentage">0%</span>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 加载状态文字 -->
|
||
<div class="text-sm text-gray-500">
|
||
<span id="loading-text">正在准备展会信息</span>
|
||
<span class="loading-dots">
|
||
<span class="dot">.</span>
|
||
<span class="dot">.</span>
|
||
<span class="dot">.</span>
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
// 将加载器插入到 body 的最前面,确保body存在
|
||
if (document.body) {
|
||
document.body.insertAdjacentHTML('afterbegin', loaderHTML);
|
||
} else {
|
||
// 如果body不存在,等待DOM加载完成
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
if (document.body && !document.getElementById('page-loader')) {
|
||
document.body.insertAdjacentHTML('afterbegin', loaderHTML);
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
// 添加样式
|
||
function addStyles() {
|
||
const style = document.createElement('style');
|
||
style.textContent = `
|
||
/* 基础动画定义 */
|
||
@keyframes spin-reverse {
|
||
from { transform: rotate(0deg); }
|
||
to { transform: rotate(-360deg); }
|
||
}
|
||
|
||
@keyframes fade-in {
|
||
from { opacity: 0; transform: translateY(20px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
|
||
@keyframes fade-in-delay {
|
||
from { opacity: 0; transform: translateY(15px); }
|
||
to { opacity: 1; transform: translateY(0); }
|
||
}
|
||
|
||
@keyframes float {
|
||
0%, 100% { transform: translateY(0px) rotate(0deg); }
|
||
25% { transform: translateY(-10px) rotate(90deg); }
|
||
50% { transform: translateY(-5px) rotate(180deg); }
|
||
75% { transform: translateY(-15px) rotate(270deg); }
|
||
}
|
||
|
||
@keyframes dot-blink {
|
||
0%, 20% { opacity: 0; }
|
||
50% { opacity: 1; }
|
||
100% { opacity: 0; }
|
||
}
|
||
|
||
@keyframes shape-float {
|
||
0%, 100% { transform: translateY(0px) translateX(0px) rotate(0deg); }
|
||
33% { transform: translateY(-20px) translateX(10px) rotate(120deg); }
|
||
66% { transform: translateY(10px) translateX(-10px) rotate(240deg); }
|
||
}
|
||
|
||
@keyframes particle-drift {
|
||
0% { transform: translateY(100vh) translateX(0px) rotate(0deg); opacity: 0; }
|
||
10% { opacity: 1; }
|
||
90% { opacity: 1; }
|
||
100% { transform: translateY(-100px) translateX(50px) rotate(360deg); opacity: 0; }
|
||
}
|
||
|
||
/* 应用动画类 */
|
||
.animate-spin-reverse {
|
||
animation: spin-reverse 2s linear infinite;
|
||
}
|
||
|
||
.animate-fade-in {
|
||
animation: fade-in 1s ease-out forwards;
|
||
}
|
||
|
||
.animate-fade-in-delay {
|
||
animation: fade-in-delay 1s ease-out 0.3s forwards;
|
||
opacity: 0;
|
||
}
|
||
|
||
/* 加载点动画 */
|
||
.loading-dots {
|
||
display: inline-block;
|
||
}
|
||
|
||
.loading-dots .dot {
|
||
animation: dot-blink 1.4s infinite;
|
||
}
|
||
|
||
.loading-dots .dot:nth-child(2) {
|
||
animation-delay: 0.2s;
|
||
}
|
||
|
||
.loading-dots .dot:nth-child(3) {
|
||
animation-delay: 0.4s;
|
||
}
|
||
|
||
/* 浮动粒子 */
|
||
.floating-particles {
|
||
position: absolute;
|
||
inset: 0;
|
||
overflow: hidden;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.particle {
|
||
position: absolute;
|
||
width: 6px;
|
||
height: 6px;
|
||
border-radius: 50%;
|
||
animation: particle-drift 8s infinite linear;
|
||
}
|
||
|
||
.particle-1 { background: rgba(16, 185, 129, 0.6); left: 10%; animation-delay: 0s; animation-duration: 8s; }
|
||
.particle-2 { background: rgba(59, 130, 246, 0.6); left: 20%; animation-delay: 1s; animation-duration: 10s; }
|
||
.particle-3 { background: rgba(168, 85, 247, 0.6); left: 30%; animation-delay: 2s; animation-duration: 9s; }
|
||
.particle-4 { background: rgba(236, 72, 153, 0.6); left: 40%; animation-delay: 3s; animation-duration: 11s; }
|
||
.particle-5 { background: rgba(245, 158, 11, 0.6); left: 50%; animation-delay: 4s; animation-duration: 7s; }
|
||
.particle-6 { background: rgba(239, 68, 68, 0.6); left: 60%; animation-delay: 5s; animation-duration: 9s; }
|
||
.particle-7 { background: rgba(16, 185, 129, 0.6); left: 70%; animation-delay: 6s; animation-duration: 8s; }
|
||
.particle-8 { background: rgba(59, 130, 246, 0.6); left: 80%; animation-delay: 7s; animation-duration: 10s; }
|
||
.particle-9 { background: rgba(168, 85, 247, 0.6); left: 90%; animation-delay: 1.5s; animation-duration: 9s; }
|
||
.particle-10 { background: rgba(236, 72, 153, 0.6); left: 15%; animation-delay: 2.5s; animation-duration: 11s; }
|
||
.particle-11 { background: rgba(245, 158, 11, 0.6); left: 25%; animation-delay: 3.5s; animation-duration: 7s; }
|
||
.particle-12 { background: rgba(239, 68, 68, 0.6); left: 85%; animation-delay: 4.5s; animation-duration: 8s; }
|
||
|
||
/* 几何图形 */
|
||
.geometric-shapes {
|
||
position: absolute;
|
||
inset: 0;
|
||
overflow: hidden;
|
||
pointer-events: none;
|
||
}
|
||
|
||
.shape {
|
||
position: absolute;
|
||
border-radius: 12px;
|
||
animation: shape-float 6s ease-in-out infinite;
|
||
}
|
||
|
||
.shape-1 {
|
||
top: 20%;
|
||
left: 10%;
|
||
width: 40px;
|
||
height: 40px;
|
||
background: linear-gradient(45deg, rgba(16, 185, 129, 0.1), rgba(59, 130, 246, 0.1));
|
||
animation-delay: 0s;
|
||
}
|
||
|
||
.shape-2 {
|
||
top: 60%;
|
||
right: 15%;
|
||
width: 60px;
|
||
height: 60px;
|
||
background: linear-gradient(135deg, rgba(168, 85, 247, 0.1), rgba(236, 72, 153, 0.1));
|
||
border-radius: 50%;
|
||
animation-delay: 2s;
|
||
}
|
||
|
||
.shape-3 {
|
||
bottom: 30%;
|
||
left: 20%;
|
||
width: 32px;
|
||
height: 32px;
|
||
background: linear-gradient(225deg, rgba(245, 158, 11, 0.1), rgba(239, 68, 68, 0.1));
|
||
animation-delay: 4s;
|
||
}
|
||
|
||
/* 加载器隐藏动画 */
|
||
#page-loader.fade-out {
|
||
animation: fadeOutLoader 0.8s ease-in-out forwards;
|
||
}
|
||
|
||
@keyframes fadeOutLoader {
|
||
0% {
|
||
opacity: 1;
|
||
transform: scale(1);
|
||
}
|
||
100% {
|
||
opacity: 0;
|
||
transform: scale(1.05);
|
||
}
|
||
}
|
||
|
||
/* 防止页面滚动 */
|
||
body.loading {
|
||
overflow: hidden;
|
||
}
|
||
`;
|
||
document.head.appendChild(style);
|
||
}
|
||
|
||
// 更新加载进度
|
||
function updateProgress(percentage, text) {
|
||
const progressBar = document.getElementById('loading-progress');
|
||
const percentageText = document.getElementById('loading-percentage');
|
||
const loadingText = document.getElementById('loading-text');
|
||
|
||
if (progressBar) {
|
||
progressBar.style.width = percentage + '%';
|
||
}
|
||
if (percentageText) {
|
||
percentageText.textContent = percentage + '%';
|
||
}
|
||
if (loadingText && text) {
|
||
loadingText.textContent = text;
|
||
}
|
||
}
|
||
|
||
// 模拟加载过程
|
||
function simulateLoading() {
|
||
const loadingSteps = [
|
||
{ progress: 0, text: '正在准备展会信息', delay: 100 },
|
||
{ progress: 15, text: '加载导航组件', delay: 200 },
|
||
{ progress: 30, text: '获取展会数据', delay: 300 },
|
||
{ progress: 45, text: '渲染页面布局', delay: 250 },
|
||
{ progress: 60, text: '加载图片资源', delay: 400 },
|
||
{ progress: 75, text: '初始化交互功能', delay: 200 },
|
||
{ progress: 85, text: '优化页面性能', delay: 150 },
|
||
{ progress: 95, text: '准备完成', delay: 200 },
|
||
{ progress: 100, text: '加载完成', delay: 300 }
|
||
];
|
||
|
||
let currentStep = 0;
|
||
|
||
function nextStep() {
|
||
if (currentStep < loadingSteps.length) {
|
||
const step = loadingSteps[currentStep];
|
||
updateProgress(step.progress, step.text);
|
||
currentStep++;
|
||
setTimeout(nextStep, step.delay);
|
||
} else {
|
||
// 加载完成,延迟一点时间后隐藏加载器
|
||
setTimeout(hideLoader, 500);
|
||
}
|
||
}
|
||
|
||
// 开始加载过程
|
||
setTimeout(nextStep, 300);
|
||
}
|
||
|
||
// 隐藏加载器
|
||
function hideLoader() {
|
||
const loader = document.getElementById('page-loader');
|
||
if (loader) {
|
||
loader.classList.add('fade-out');
|
||
|
||
// 动画结束后移除元素并恢复页面滚动
|
||
setTimeout(() => {
|
||
loader.remove();
|
||
document.body.classList.remove('loading');
|
||
|
||
// 触发页面加载完成事件
|
||
document.dispatchEvent(new CustomEvent('pageLoaded'));
|
||
}, 800);
|
||
}
|
||
}
|
||
|
||
// 初始化加载器
|
||
function init() {
|
||
// 确保DOM已加载
|
||
if (!document.body) {
|
||
// 如果body还不存在,等待DOM加载完成
|
||
document.addEventListener('DOMContentLoaded', init);
|
||
return;
|
||
}
|
||
|
||
// 添加样式
|
||
addStyles();
|
||
|
||
// 创建加载器
|
||
createLoader();
|
||
|
||
// 禁用页面滚动
|
||
if (document.body) {
|
||
document.body.classList.add('loading');
|
||
}
|
||
|
||
// 等待页面资源加载完成
|
||
if (document.readyState === 'loading') {
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
setTimeout(simulateLoading, 200);
|
||
});
|
||
} else {
|
||
setTimeout(simulateLoading, 200);
|
||
}
|
||
|
||
// 监听 window load 事件(所有资源包括图片都加载完成)
|
||
window.addEventListener('load', () => {
|
||
// 如果加载器仍然存在,加速完成过程
|
||
if (document.getElementById('page-loader')) {
|
||
updateProgress(100, '加载完成');
|
||
setTimeout(hideLoader, 300);
|
||
}
|
||
});
|
||
|
||
// 监听页面可见性变化(用户切换标签页时暂停动画)
|
||
document.addEventListener('visibilitychange', function() {
|
||
const loader = document.getElementById('page-loader');
|
||
if (loader) {
|
||
if (document.hidden) {
|
||
loader.style.animationPlayState = 'paused';
|
||
} else {
|
||
loader.style.animationPlayState = 'running';
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
// 提供外部接口
|
||
window.PageLoader = {
|
||
updateProgress,
|
||
hideLoader
|
||
};
|
||
|
||
// 初始化
|
||
init();
|
||
})(); |