Files
n8n_Demo/web_frontend/web_result/js/main.js
Yep_Q 2b1ed656cd feat: 优化会展网站首页用户体验和视觉效果
主要改进:
- 修复星形图标可见性问题,改为黄色高对比度显示
- 移除'立即参展'按钮,清理冗余CTA元素
- 修正背景图片路径并优化透明度(opacity-10)
- 为所有容器区域添加hover提示文字
- 删除'立即参与,共创绿色出行未来'整个CTA区块
- 优化页面加载器和图片错误处理
- 修复展会信息卡片文字对比度问题
- 增强多层背景遮罩效果

影响文件:
- web_frontend/web_result/index.html
- web_frontend/web_result/css/animations.css
- web_frontend/web_result/js/main.js
- 新增多个页面和样式文件

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-08 14:05:26 +08:00

709 lines
22 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 2024长三角国际新能源汽车与智能交通产业博览会 - 交互脚本
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function() {
// 隐藏页面加载器
hidePageLoader();
initNavbar();
initAnimations();
initCounters();
initScrollEffects();
initRippleEffect();
initLazyLoading();
initFormValidation();
initCharts();
initTimeline();
initInteractiveElements();
handleImageErrors();
});
// 隐藏页面加载器
function hidePageLoader() {
const loader = document.getElementById('pageLoader');
if (loader) {
// 添加淡出动画
loader.style.opacity = '0';
loader.style.transition = 'opacity 0.5s ease-out';
// 500ms后完全移除
setTimeout(() => {
loader.style.display = 'none';
// 恢复body滚动
document.body.style.overflow = 'auto';
document.body.classList.remove('loading');
}, 500);
} else {
// 如果没有找到加载器,也要确保恢复滚动
document.body.style.overflow = 'auto';
document.body.classList.remove('loading');
}
}
// 如果页面加载超过3秒强制隐藏加载器
window.addEventListener('load', function() {
setTimeout(() => {
hidePageLoader();
}, 3000);
});
// 导航栏交互
function initNavbar() {
const navbar = document.querySelector('.navbar');
const navLinks = document.querySelectorAll('.nav-link');
const mobileMenuBtn = document.querySelector('.mobile-menu-btn');
const mobileMenu = document.querySelector('.mobile-menu');
// 滚动时改变导航栏样式
window.addEventListener('scroll', () => {
if (window.scrollY > 100) {
navbar?.classList.add('scrolled');
} else {
navbar?.classList.remove('scrolled');
}
});
// 高亮当前页面链接
const currentPath = window.location.pathname;
navLinks.forEach(link => {
if (link.getAttribute('href') === currentPath) {
link.classList.add('active');
}
});
// 移动端菜单切换
mobileMenuBtn?.addEventListener('click', () => {
mobileMenu?.classList.toggle('open');
mobileMenuBtn.classList.toggle('active');
});
// 平滑滚动到锚点
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function(e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
});
}
// 动画初始化
function initAnimations() {
// Intersection Observer for fade-in animations
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('animate-in');
observer.unobserve(entry.target);
}
});
}, observerOptions);
// 观察所有需要动画的元素
document.querySelectorAll('.fade-in, .slide-in-left, .slide-in-right, .grid-item').forEach(el => {
observer.observe(el);
});
}
// 数字计数器动画
function initCounters() {
const counters = document.querySelectorAll('.counter');
const speed = 200; // 动画速度
const countUp = (counter) => {
const target = +counter.getAttribute('data-target');
const increment = target / speed;
const updateCount = () => {
const count = +counter.innerText.replace(/[^0-9]/g, '');
if (count < target) {
counter.innerText = Math.ceil(count + increment).toLocaleString();
setTimeout(updateCount, 1);
} else {
counter.innerText = target.toLocaleString();
// 添加单位
const unit = counter.getAttribute('data-unit');
if (unit) {
counter.innerText += unit;
}
}
};
updateCount();
};
// 使用 Intersection Observer 触发计数
const counterObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
countUp(entry.target);
counterObserver.unobserve(entry.target);
}
});
}, { threshold: 0.5 });
counters.forEach(counter => {
counter.innerText = '0';
counterObserver.observe(counter);
});
}
// 滚动效果
function initScrollEffects() {
let lastScrollTop = 0;
const header = document.querySelector('header');
window.addEventListener('scroll', () => {
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
// 隐藏/显示导航栏
if (scrollTop > lastScrollTop && scrollTop > 300) {
header?.classList.add('hide');
} else {
header?.classList.remove('hide');
}
lastScrollTop = scrollTop <= 0 ? 0 : scrollTop;
// 视差效果
const parallaxElements = document.querySelectorAll('.parallax');
parallaxElements.forEach(el => {
const speed = el.getAttribute('data-speed') || 0.5;
const yPos = -(scrollTop * speed);
el.style.transform = `translateY(${yPos}px)`;
});
// 进度条
const progressBar = document.querySelector('.progress-bar');
if (progressBar) {
const winScroll = document.body.scrollTop || document.documentElement.scrollTop;
const height = document.documentElement.scrollHeight - document.documentElement.clientHeight;
const scrolled = (winScroll / height) * 100;
progressBar.style.width = scrolled + '%';
}
});
}
// 波纹效果
function initRippleEffect() {
document.querySelectorAll('.btn, .card').forEach(element => {
element.addEventListener('click', function(e) {
const ripple = document.createElement('span');
ripple.classList.add('ripple');
const rect = this.getBoundingClientRect();
const size = Math.max(rect.width, rect.height);
const x = e.clientX - rect.left - size / 2;
const y = e.clientY - rect.top - size / 2;
ripple.style.width = ripple.style.height = size + 'px';
ripple.style.left = x + 'px';
ripple.style.top = y + 'px';
this.appendChild(ripple);
setTimeout(() => {
ripple.remove();
}, 600);
});
});
}
// 懒加载
function initLazyLoading() {
const images = document.querySelectorAll('img[data-src]');
const imageObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.removeAttribute('data-src');
imageObserver.unobserve(img);
// 添加加载完成动画
img.addEventListener('load', () => {
img.classList.add('loaded');
});
}
});
}, {
rootMargin: '50px 0px'
});
images.forEach(img => imageObserver.observe(img));
}
// 表单验证
function initFormValidation() {
const forms = document.querySelectorAll('form');
forms.forEach(form => {
form.addEventListener('submit', function(e) {
e.preventDefault();
let isValid = true;
const inputs = form.querySelectorAll('input[required], textarea[required]');
inputs.forEach(input => {
if (!input.value.trim()) {
isValid = false;
input.classList.add('error');
showError(input, '此字段为必填项');
} else {
input.classList.remove('error');
clearError(input);
}
// 邮箱验证
if (input.type === 'email' && input.value) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(input.value)) {
isValid = false;
input.classList.add('error');
showError(input, '请输入有效的邮箱地址');
}
}
// 电话验证
if (input.type === 'tel' && input.value) {
const phoneRegex = /^1[3-9]\d{9}$/;
if (!phoneRegex.test(input.value)) {
isValid = false;
input.classList.add('error');
showError(input, '请输入有效的手机号码');
}
}
});
if (isValid) {
// 显示成功消息
showSuccess('表单提交成功!');
form.reset();
}
});
// 实时验证
form.querySelectorAll('input, textarea').forEach(input => {
input.addEventListener('blur', function() {
validateInput(this);
});
input.addEventListener('input', function() {
if (this.classList.contains('error')) {
validateInput(this);
}
});
});
});
}
// 显示错误信息
function showError(input, message) {
const errorEl = input.nextElementSibling;
if (errorEl && errorEl.classList.contains('error-message')) {
errorEl.textContent = message;
} else {
const error = document.createElement('span');
error.classList.add('error-message');
error.textContent = message;
input.parentNode.insertBefore(error, input.nextSibling);
}
}
// 清除错误信息
function clearError(input) {
const errorEl = input.nextElementSibling;
if (errorEl && errorEl.classList.contains('error-message')) {
errorEl.remove();
}
}
// 验证输入
function validateInput(input) {
if (input.hasAttribute('required') && !input.value.trim()) {
input.classList.add('error');
showError(input, '此字段为必填项');
return false;
}
input.classList.remove('error');
clearError(input);
return true;
}
// 显示成功消息
function showSuccess(message) {
const toast = document.createElement('div');
toast.classList.add('toast', 'success');
toast.textContent = message;
document.body.appendChild(toast);
setTimeout(() => {
toast.classList.add('show');
}, 100);
setTimeout(() => {
toast.classList.remove('show');
setTimeout(() => {
toast.remove();
}, 300);
}, 3000);
}
// 初始化图表
function initCharts() {
// 预算分配饼图
const budgetChart = document.getElementById('budgetChart');
if (budgetChart) {
const data = [
{ label: '场地租赁', value: 35, color: '#10b981' },
{ label: '营销推广', value: 25, color: '#3b82f6' },
{ label: '运营服务', value: 20, color: '#8b5cf6' },
{ label: '人员成本', value: 15, color: '#f59e0b' },
{ label: '其他费用', value: 5, color: '#ef4444' }
];
drawPieChart(budgetChart, data);
}
// 参展商类别分布
const exhibitorChart = document.getElementById('exhibitorChart');
if (exhibitorChart) {
const data = [
{ label: '整车制造', value: 40 },
{ label: '零部件', value: 30 },
{ label: '充电设施', value: 15 },
{ label: '智能驾驶', value: 10 },
{ label: '其他', value: 5 }
];
drawBarChart(exhibitorChart, data);
}
}
// 绘制饼图
function drawPieChart(canvas, data) {
const ctx = canvas.getContext('2d');
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const radius = Math.min(centerX, centerY) - 20;
let currentAngle = -Math.PI / 2;
const total = data.reduce((sum, item) => sum + item.value, 0);
data.forEach(item => {
const sliceAngle = (item.value / total) * 2 * Math.PI;
// 绘制扇形
ctx.beginPath();
ctx.arc(centerX, centerY, radius, currentAngle, currentAngle + sliceAngle);
ctx.lineTo(centerX, centerY);
ctx.fillStyle = item.color;
ctx.fill();
// 绘制标签
const labelX = centerX + Math.cos(currentAngle + sliceAngle / 2) * (radius * 0.7);
const labelY = centerY + Math.sin(currentAngle + sliceAngle / 2) * (radius * 0.7);
ctx.fillStyle = '#fff';
ctx.font = '14px sans-serif';
ctx.textAlign = 'center';
ctx.fillText(item.value + '%', labelX, labelY);
currentAngle += sliceAngle;
});
}
// 绘制柱状图
function drawBarChart(canvas, data) {
const ctx = canvas.getContext('2d');
const barWidth = canvas.width / (data.length * 2);
const maxValue = Math.max(...data.map(item => item.value));
const chartHeight = canvas.height - 40;
data.forEach((item, index) => {
const barHeight = (item.value / maxValue) * chartHeight;
const x = (index * 2 + 0.5) * barWidth;
const y = canvas.height - barHeight - 20;
// 绘制柱子
const gradient = ctx.createLinearGradient(x, y, x, y + barHeight);
gradient.addColorStop(0, '#10b981');
gradient.addColorStop(1, '#3b82f6');
ctx.fillStyle = gradient;
ctx.fillRect(x, y, barWidth, barHeight);
// 绘制数值
ctx.fillStyle = '#333';
ctx.font = '12px sans-serif';
ctx.textAlign = 'center';
ctx.fillText(item.value + '%', x + barWidth / 2, y - 5);
});
}
// 初始化时间线
function initTimeline() {
const timelineItems = document.querySelectorAll('.timeline-item');
const timelineObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('active');
}
});
}, { threshold: 0.5 });
timelineItems.forEach(item => {
timelineObserver.observe(item);
});
}
// 交互元素初始化
function initInteractiveElements() {
// 标签页切换
const tabs = document.querySelectorAll('.tab');
const tabContents = document.querySelectorAll('.tab-content');
tabs.forEach(tab => {
tab.addEventListener('click', () => {
const target = tab.dataset.tab;
// 切换标签状态
tabs.forEach(t => t.classList.remove('active'));
tab.classList.add('active');
// 切换内容显示
tabContents.forEach(content => {
if (content.id === target) {
content.classList.add('active');
content.style.display = 'block';
} else {
content.classList.remove('active');
content.style.display = 'none';
}
});
});
});
// 手风琴效果
const accordionHeaders = document.querySelectorAll('.accordion-header');
accordionHeaders.forEach(header => {
header.addEventListener('click', () => {
const content = header.nextElementSibling;
const isOpen = header.classList.contains('active');
// 关闭其他项
accordionHeaders.forEach(h => {
h.classList.remove('active');
h.nextElementSibling.style.maxHeight = null;
});
// 切换当前项
if (!isOpen) {
header.classList.add('active');
content.style.maxHeight = content.scrollHeight + 'px';
}
});
});
// 模态框
const modalTriggers = document.querySelectorAll('[data-modal]');
const modals = document.querySelectorAll('.modal');
const modalCloses = document.querySelectorAll('.modal-close');
modalTriggers.forEach(trigger => {
trigger.addEventListener('click', () => {
const modalId = trigger.dataset.modal;
const modal = document.getElementById(modalId);
if (modal) {
modal.classList.add('open');
document.body.style.overflow = 'hidden';
}
});
});
modalCloses.forEach(close => {
close.addEventListener('click', () => {
const modal = close.closest('.modal');
modal.classList.remove('open');
document.body.style.overflow = '';
});
});
// 点击背景关闭模态框
modals.forEach(modal => {
modal.addEventListener('click', (e) => {
if (e.target === modal) {
modal.classList.remove('open');
document.body.style.overflow = '';
}
});
});
// 工具提示
const tooltips = document.querySelectorAll('[data-tooltip]');
tooltips.forEach(element => {
const tooltip = document.createElement('div');
tooltip.classList.add('tooltip');
tooltip.textContent = element.dataset.tooltip;
element.addEventListener('mouseenter', () => {
document.body.appendChild(tooltip);
const rect = element.getBoundingClientRect();
tooltip.style.left = rect.left + rect.width / 2 - tooltip.offsetWidth / 2 + 'px';
tooltip.style.top = rect.top - tooltip.offsetHeight - 10 + 'px';
tooltip.classList.add('show');
});
element.addEventListener('mouseleave', () => {
tooltip.classList.remove('show');
setTimeout(() => {
tooltip.remove();
}, 300);
});
});
}
// 复制到剪贴板
function copyToClipboard(text) {
const textarea = document.createElement('textarea');
textarea.value = text;
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
showSuccess('已复制到剪贴板');
}
// 分享功能
function shareContent(platform) {
const url = encodeURIComponent(window.location.href);
const title = encodeURIComponent(document.title);
let shareUrl = '';
switch(platform) {
case 'wechat':
// 生成微信分享二维码
generateQRCode(window.location.href);
break;
case 'weibo':
shareUrl = `https://service.weibo.com/share/share.php?url=${url}&title=${title}`;
break;
case 'linkedin':
shareUrl = `https://www.linkedin.com/sharing/share-offsite/?url=${url}`;
break;
case 'twitter':
shareUrl = `https://twitter.com/intent/tweet?url=${url}&text=${title}`;
break;
}
if (shareUrl) {
window.open(shareUrl, '_blank', 'width=600,height=400');
}
}
// 生成二维码
function generateQRCode(text) {
const modal = document.createElement('div');
modal.classList.add('qr-modal');
modal.innerHTML = `
<div class="qr-content">
<h3>微信扫码分享</h3>
<div id="qrcode"></div>
<button class="btn btn-secondary" onclick="this.parentElement.parentElement.remove()">关闭</button>
</div>
`;
document.body.appendChild(modal);
// 这里可以集成实际的二维码生成库
document.getElementById('qrcode').innerHTML = '二维码生成区域';
}
// 主题切换
function initThemeToggle() {
const themeToggle = document.getElementById('themeToggle');
const currentTheme = localStorage.getItem('theme') || 'light';
document.documentElement.setAttribute('data-theme', currentTheme);
themeToggle?.addEventListener('click', () => {
const theme = document.documentElement.getAttribute('data-theme') === 'light' ? 'dark' : 'light';
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme);
});
}
// 导出为 PDF
function exportToPDF() {
window.print();
}
// 性能监控
function initPerformanceMonitoring() {
// 页面加载时间
window.addEventListener('load', () => {
const loadTime = performance.timing.loadEventEnd - performance.timing.navigationStart;
console.log(`页面加载时间: ${loadTime}ms`);
// 发送到分析服务
if (window.gtag) {
gtag('event', 'page_load_time', {
value: loadTime,
page_path: window.location.pathname
});
}
});
// 监控长任务
if ('PerformanceObserver' in window) {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log('Long Task detected:', entry);
}
});
observer.observe({ entryTypes: ['longtask'] });
}
}
// 处理图片加载错误
function handleImageErrors() {
const images = document.querySelectorAll('img');
const defaultImage = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAwIiBoZWlnaHQ9IjMwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZTJlOGYwIi8+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGZvbnQtZmFtaWx5PSJBcmlhbCIgZm9udC1zaXplPSIxOCIgZmlsbD0iIzk0YTNiOCIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZHk9Ii4zZW0iPuWbvueJh+WKoOi9veS4rTwvdGV4dD48L3N2Zz4=';
images.forEach(img => {
// 添加错误处理
img.addEventListener('error', function() {
console.warn('图片加载失败:', this.src);
// 设置默认占位图
this.src = defaultImage;
this.alt = '图片加载失败';
this.classList.add('image-error');
});
// 添加加载成功处理
img.addEventListener('load', function() {
this.classList.add('image-loaded');
});
});
}
// 初始化性能监控
initPerformanceMonitoring();
initThemeToggle();