// 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 = `