514 lines
15 KiB
JavaScript
514 lines
15 KiB
JavaScript
// 主JavaScript文件 - 爱鉴花官方网站
|
||
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
// 初始化函数
|
||
initWebsite();
|
||
});
|
||
|
||
function initWebsite() {
|
||
// 初始化导航栏滚动效果
|
||
initNavbarScroll();
|
||
|
||
// 初始化滚动动画
|
||
initScrollAnimation();
|
||
|
||
// 初始化表单验证
|
||
initFormValidation();
|
||
|
||
// 初始化计数器动画
|
||
initCounterAnimation();
|
||
|
||
// 初始化图片懒加载
|
||
initLazyLoading();
|
||
|
||
// 初始化移动端菜单
|
||
initMobileMenu();
|
||
|
||
// 初始化平滑滚动
|
||
initSmoothScroll();
|
||
|
||
// 初始化悬停动画
|
||
initHoverEffects();
|
||
|
||
// 初始化加载状态
|
||
initLoadingStates();
|
||
|
||
// 初始化页面过渡效果
|
||
initPageTransitions();
|
||
}
|
||
|
||
// 导航栏滚动效果
|
||
function initNavbarScroll() {
|
||
const navbar = document.querySelector('.navbar');
|
||
|
||
window.addEventListener('scroll', function() {
|
||
if (window.scrollY > 100) {
|
||
navbar.classList.add('navbar-scrolled');
|
||
} else {
|
||
navbar.classList.remove('navbar-scrolled');
|
||
}
|
||
});
|
||
}
|
||
|
||
// 滚动动画效果
|
||
function initScrollAnimation() {
|
||
const animatedElements = document.querySelectorAll('.card, .feature-icon');
|
||
|
||
const observerOptions = {
|
||
threshold: 0.1,
|
||
rootMargin: '0px 0px -50px 0px'
|
||
};
|
||
|
||
const observer = new IntersectionObserver(function(entries) {
|
||
entries.forEach(function(entry) {
|
||
if (entry.isIntersecting) {
|
||
entry.target.classList.add('fade-in-up');
|
||
observer.unobserve(entry.target);
|
||
}
|
||
});
|
||
}, observerOptions);
|
||
|
||
animatedElements.forEach(function(element) {
|
||
observer.observe(element);
|
||
});
|
||
}
|
||
|
||
// 表单验证
|
||
function initFormValidation() {
|
||
const forms = document.querySelectorAll('form');
|
||
|
||
forms.forEach(function(form) {
|
||
form.addEventListener('submit', function(e) {
|
||
if (!validateForm(form)) {
|
||
e.preventDefault();
|
||
}
|
||
});
|
||
});
|
||
}
|
||
|
||
function validateForm(form) {
|
||
let isValid = true;
|
||
const inputs = form.querySelectorAll('input[required], textarea[required]');
|
||
|
||
inputs.forEach(function(input) {
|
||
if (!input.value.trim()) {
|
||
showError(input, '此字段为必填项');
|
||
isValid = false;
|
||
} else if (input.type === 'email' && !isValidEmail(input.value)) {
|
||
showError(input, '请输入有效的邮箱地址');
|
||
isValid = false;
|
||
} else {
|
||
clearError(input);
|
||
}
|
||
});
|
||
|
||
return isValid;
|
||
}
|
||
|
||
function isValidEmail(email) {
|
||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||
return emailRegex.test(email);
|
||
}
|
||
|
||
function showError(input, message) {
|
||
clearError(input);
|
||
|
||
const errorDiv = document.createElement('div');
|
||
errorDiv.className = 'invalid-feedback';
|
||
errorDiv.textContent = message;
|
||
|
||
input.classList.add('is-invalid');
|
||
input.parentNode.appendChild(errorDiv);
|
||
}
|
||
|
||
function clearError(input) {
|
||
input.classList.remove('is-invalid');
|
||
const errorDiv = input.parentNode.querySelector('.invalid-feedback');
|
||
if (errorDiv) {
|
||
errorDiv.remove();
|
||
}
|
||
}
|
||
|
||
// 计数器动画
|
||
function initCounterAnimation() {
|
||
const counters = document.querySelectorAll('.counter');
|
||
|
||
if (counters.length > 0) {
|
||
const observer = new IntersectionObserver(function(entries) {
|
||
entries.forEach(function(entry) {
|
||
if (entry.isIntersecting) {
|
||
animateCounters();
|
||
observer.unobserve(entry.target);
|
||
}
|
||
});
|
||
});
|
||
|
||
observer.observe(counters[0]);
|
||
}
|
||
}
|
||
|
||
function animateCounters() {
|
||
const counters = document.querySelectorAll('.counter');
|
||
|
||
counters.forEach(function(counter) {
|
||
const target = parseInt(counter.getAttribute('data-target'));
|
||
const duration = 2000; // 2 seconds
|
||
const frameDuration = 1000 / 60; // 60fps
|
||
const totalFrames = Math.round(duration / frameDuration);
|
||
let currentFrame = 0;
|
||
|
||
const updateCounter = function() {
|
||
currentFrame++;
|
||
const progress = currentFrame / totalFrames;
|
||
const currentValue = Math.round(target * progress);
|
||
|
||
counter.textContent = currentValue.toLocaleString();
|
||
|
||
if (currentFrame < totalFrames) {
|
||
requestAnimationFrame(updateCounter);
|
||
} else {
|
||
counter.textContent = target.toLocaleString();
|
||
}
|
||
};
|
||
|
||
updateCounter();
|
||
});
|
||
}
|
||
|
||
// 图片懒加载
|
||
function initLazyLoading() {
|
||
if ('IntersectionObserver' in window) {
|
||
const lazyImages = document.querySelectorAll('img.lazy-load');
|
||
|
||
const imageObserver = new IntersectionObserver(function(entries) {
|
||
entries.forEach(function(entry) {
|
||
if (entry.isIntersecting) {
|
||
const img = entry.target;
|
||
// 确保图片完全加载后显示
|
||
img.onload = function() {
|
||
img.classList.add('loaded');
|
||
};
|
||
// 处理data-src属性或直接使用src
|
||
if (img.hasAttribute('data-src')) {
|
||
img.src = img.getAttribute('data-src');
|
||
img.removeAttribute('data-src');
|
||
}
|
||
imageObserver.unobserve(img);
|
||
}
|
||
});
|
||
}, {
|
||
threshold: 0.1,
|
||
rootMargin: '50px 0px'
|
||
});
|
||
|
||
lazyImages.forEach(function(img) {
|
||
imageObserver.observe(img);
|
||
});
|
||
} else {
|
||
// 浏览器不支持IntersectionObserver时的降级方案
|
||
const lazyImages = document.querySelectorAll('img.lazy-load');
|
||
lazyImages.forEach(function(img) {
|
||
img.classList.add('loaded');
|
||
});
|
||
}
|
||
}
|
||
|
||
// 移动端菜单处理
|
||
function initMobileMenu() {
|
||
const navbarToggler = document.querySelector('.navbar-toggler');
|
||
const navbarCollapse = document.querySelector('.navbar-collapse');
|
||
|
||
if (navbarToggler && navbarCollapse) {
|
||
navbarToggler.addEventListener('click', function() {
|
||
navbarCollapse.classList.toggle('show');
|
||
// 添加菜单动画效果
|
||
navbarToggler.classList.toggle('active');
|
||
});
|
||
|
||
// 点击菜单项后自动关闭菜单(移动端)
|
||
const navLinks = document.querySelectorAll('.navbar-nav .nav-link');
|
||
navLinks.forEach(function(link) {
|
||
link.addEventListener('click', function() {
|
||
if (window.innerWidth < 992) {
|
||
navbarCollapse.classList.remove('show');
|
||
navbarToggler.classList.remove('active');
|
||
}
|
||
});
|
||
});
|
||
}
|
||
}
|
||
|
||
// 平滑滚动效果
|
||
function initSmoothScroll() {
|
||
// 内部链接平滑滚动
|
||
const internalLinks = document.querySelectorAll('a[href^="#"]');
|
||
|
||
internalLinks.forEach(function(link) {
|
||
link.addEventListener('click', function(e) {
|
||
e.preventDefault();
|
||
|
||
const targetId = this.getAttribute('href').substring(1);
|
||
const targetElement = document.getElementById(targetId);
|
||
|
||
if (targetElement) {
|
||
const offsetTop = targetElement.offsetTop - 80; // 考虑导航栏高度
|
||
|
||
window.scrollTo({
|
||
top: offsetTop,
|
||
behavior: 'smooth'
|
||
});
|
||
}
|
||
});
|
||
});
|
||
|
||
// 返回顶部按钮
|
||
const backToTopBtn = document.createElement('button');
|
||
backToTopBtn.className = 'back-to-top';
|
||
backToTopBtn.innerHTML = '↑';
|
||
backToTopBtn.setAttribute('aria-label', '返回顶部');
|
||
|
||
backToTopBtn.addEventListener('click', function() {
|
||
window.scrollTo({
|
||
top: 0,
|
||
behavior: 'smooth'
|
||
});
|
||
});
|
||
|
||
// 滚动时显示/隐藏返回顶部按钮
|
||
window.addEventListener('scroll', debounce(function() {
|
||
if (window.scrollY > 500) {
|
||
backToTopBtn.classList.add('show');
|
||
} else {
|
||
backToTopBtn.classList.remove('show');
|
||
}
|
||
}, 100));
|
||
|
||
document.body.appendChild(backToTopBtn);
|
||
}
|
||
|
||
// 悬停动画效果
|
||
function initHoverEffects() {
|
||
// 卡片悬停效果
|
||
const cards = document.querySelectorAll('.card, .product-card, .news-card');
|
||
|
||
cards.forEach(function(card) {
|
||
card.addEventListener('mouseenter', function() {
|
||
this.style.transform = 'translateY(-5px) scale(1.02)';
|
||
this.style.boxShadow = '0 10px 25px rgba(0, 0, 0, 0.15)';
|
||
});
|
||
|
||
card.addEventListener('mouseleave', function() {
|
||
this.style.transform = 'translateY(0) scale(1)';
|
||
this.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)';
|
||
});
|
||
});
|
||
|
||
// 按钮悬停效果
|
||
const buttons = document.querySelectorAll('.btn, .nav-link');
|
||
|
||
buttons.forEach(function(button) {
|
||
button.addEventListener('mouseenter', function() {
|
||
this.style.transform = 'translateY(-2px)';
|
||
});
|
||
|
||
button.addEventListener('mouseleave', function() {
|
||
this.style.transform = 'translateY(0)';
|
||
});
|
||
});
|
||
}
|
||
|
||
// 加载状态指示器
|
||
function initLoadingStates() {
|
||
const forms = document.querySelectorAll('form');
|
||
|
||
forms.forEach(function(form) {
|
||
form.addEventListener('submit', function() {
|
||
const submitBtn = this.querySelector('button[type="submit"]');
|
||
if (submitBtn) {
|
||
submitBtn.disabled = true;
|
||
submitBtn.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> 处理中...';
|
||
|
||
// 3秒后恢复按钮状态(防止无限加载)
|
||
setTimeout(function() {
|
||
submitBtn.disabled = false;
|
||
submitBtn.innerHTML = submitBtn.getAttribute('data-original-text') || '提交';
|
||
}, 3000);
|
||
}
|
||
});
|
||
});
|
||
|
||
// 保存按钮原始文本
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
const submitButtons = document.querySelectorAll('button[type="submit"]');
|
||
submitButtons.forEach(function(btn) {
|
||
btn.setAttribute('data-original-text', btn.textContent);
|
||
});
|
||
});
|
||
}
|
||
|
||
// 页面过渡效果
|
||
function initPageTransitions() {
|
||
// 链接点击时的过渡效果
|
||
const links = document.querySelectorAll('a:not([href^="#"]):not([href^="javascript"])');
|
||
|
||
links.forEach(function(link) {
|
||
link.addEventListener('click', function(e) {
|
||
if (this.href && !this.target && !this.hasAttribute('download')) {
|
||
e.preventDefault();
|
||
|
||
// 添加页面离开动画
|
||
document.body.classList.add('page-leaving');
|
||
|
||
setTimeout(function() {
|
||
window.location.href = link.href;
|
||
}, 300);
|
||
}
|
||
});
|
||
});
|
||
|
||
// 页面进入动画
|
||
document.body.classList.add('page-entering');
|
||
|
||
setTimeout(function() {
|
||
document.body.classList.remove('page-entering');
|
||
}, 300);
|
||
}
|
||
|
||
// 工具函数:防抖
|
||
function debounce(func, wait) {
|
||
let timeout;
|
||
return function() {
|
||
const context = this;
|
||
const args = arguments;
|
||
clearTimeout(timeout);
|
||
timeout = setTimeout(function() {
|
||
func.apply(context, args);
|
||
}, wait);
|
||
};
|
||
}
|
||
|
||
// 工具函数:节流
|
||
function throttle(func, limit) {
|
||
let inThrottle;
|
||
return function() {
|
||
const args = arguments;
|
||
const context = this;
|
||
if (!inThrottle) {
|
||
func.apply(context, args);
|
||
inThrottle = true;
|
||
setTimeout(function() {
|
||
inThrottle = false;
|
||
}, limit);
|
||
}
|
||
};
|
||
}
|
||
|
||
// 页面性能监控
|
||
function monitorPerformance() {
|
||
// 页面加载时间
|
||
window.addEventListener('load', function() {
|
||
const loadTime = performance.timing.loadEventEnd - performance.timing.navigationStart;
|
||
console.log('页面加载时间:', loadTime + 'ms');
|
||
});
|
||
|
||
// 最大内容绘制(LCP)
|
||
new PerformanceObserver(function(entryList) {
|
||
const entries = entryList.getEntries();
|
||
const lastEntry = entries[entries.length - 1];
|
||
console.log('LCP:', lastEntry.startTime);
|
||
}).observe({type: 'largest-contentful-paint', buffered: true});
|
||
}
|
||
|
||
// 错误监控
|
||
function monitorErrors() {
|
||
window.addEventListener('error', function(e) {
|
||
console.error('JavaScript错误:', e.error);
|
||
});
|
||
|
||
window.addEventListener('unhandledrejection', function(e) {
|
||
console.error('未处理的Promise拒绝:', e.reason);
|
||
});
|
||
}
|
||
|
||
// 初始化性能和错误监控
|
||
monitorPerformance();
|
||
monitorErrors();
|
||
|
||
// 通用JavaScript功能
|
||
|
||
// 导航栏滚动效果
|
||
window.addEventListener('scroll', function() {
|
||
const navbar = document.querySelector('.navbar');
|
||
if (window.scrollY > 50) {
|
||
navbar.classList.add('scrolled');
|
||
} else {
|
||
navbar.classList.remove('scrolled');
|
||
}
|
||
});
|
||
|
||
// 懒加载图片
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
const lazyImages = [].slice.call(document.querySelectorAll('img.lazy-load'));
|
||
|
||
if ('IntersectionObserver' in window) {
|
||
let lazyImageObserver = new IntersectionObserver(function(entries, observer) {
|
||
entries.forEach(function(entry) {
|
||
if (entry.isIntersecting) {
|
||
let lazyImage = entry.target;
|
||
lazyImage.src = lazyImage.dataset.src || lazyImage.src;
|
||
lazyImage.classList.remove('lazy-load');
|
||
lazyImageObserver.unobserve(lazyImage);
|
||
}
|
||
});
|
||
});
|
||
|
||
lazyImages.forEach(function(lazyImage) {
|
||
lazyImageObserver.observe(lazyImage);
|
||
});
|
||
}
|
||
});
|
||
|
||
// 平滑滚动
|
||
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
|
||
anchor.addEventListener('click', function (e) {
|
||
e.preventDefault();
|
||
|
||
const target = document.querySelector(this.getAttribute('href'));
|
||
if (target) {
|
||
window.scrollTo({
|
||
top: target.offsetTop - 76,
|
||
behavior: 'smooth'
|
||
});
|
||
}
|
||
});
|
||
});
|
||
|
||
// 表单验证增强
|
||
(function() {
|
||
'use strict';
|
||
|
||
window.addEventListener('load', function() {
|
||
// 获取所有表单并阻止默认提交行为
|
||
var forms = document.getElementsByClassName('needs-validation');
|
||
var validation = Array.prototype.filter.call(forms, function(form) {
|
||
form.addEventListener('submit', function(event) {
|
||
if (form.checkValidity() === false) {
|
||
event.preventDefault();
|
||
event.stopPropagation();
|
||
}
|
||
form.classList.add('was-validated');
|
||
}, false);
|
||
});
|
||
}, false);
|
||
})();
|
||
|
||
// 动态年份更新
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
const yearElements = document.querySelectorAll('.current-year');
|
||
const currentYear = new Date().getFullYear();
|
||
|
||
yearElements.forEach(element => {
|
||
element.textContent = currentYear;
|
||
});
|
||
});
|