|
|
// 这是自定义JS文件,用于覆盖主题默认JS文件
|
|
|
/**
|
|
|
* AdvancedImageLoader
|
|
|
* 通用图片加载管理器:支持懒加载、预加载、骨架屏控制
|
|
|
*/
|
|
|
class AdvancedImageLoader {
|
|
|
constructor(options = {}) {
|
|
|
this.options = Object.assign({
|
|
|
rootMargin: '200px 0px', // 提前 200px 开始加载
|
|
|
threshold: 0.01, // 只要露出 1% 就加载
|
|
|
selector: '.lazy-target',// 需要懒加载的目标选择器
|
|
|
skeletonClass: 'skeleton-pulse', // 骨架屏动画类名
|
|
|
loadedClass: 'loaded', // 加载完成后的 CSS 类名
|
|
|
}, options);
|
|
|
|
|
|
this.observer = null;
|
|
|
this.init();
|
|
|
}
|
|
|
|
|
|
init() {
|
|
|
if ('IntersectionObserver' in window) {
|
|
|
this.observer = new IntersectionObserver((entries, observer) => {
|
|
|
entries.forEach(entry => {
|
|
|
if (entry.isIntersecting) {
|
|
|
this.loadImage(entry.target);
|
|
|
observer.unobserve(entry.target);
|
|
|
}
|
|
|
});
|
|
|
}, {
|
|
|
rootMargin: this.options.rootMargin,
|
|
|
threshold: this.options.threshold
|
|
|
});
|
|
|
}
|
|
|
this.scan();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 扫描 DOM,寻找未加载的图片并加入观察队列
|
|
|
* 注意:在动态渲染(如搜索、翻页)后必须调用此方法
|
|
|
*/
|
|
|
scan() {
|
|
|
const elements = document.querySelectorAll(`${this.options.selector}:not(.${this.options.loadedClass})`);
|
|
|
elements.forEach(el => {
|
|
|
// 如果浏览器不支持 Observer,直接加载
|
|
|
if (!this.observer) {
|
|
|
this.loadImage(el);
|
|
|
} else {
|
|
|
this.observer.observe(el);
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 核心加载逻辑
|
|
|
* @param {HTMLElement} el 目标 DOM 元素
|
|
|
*/
|
|
|
loadImage(el) {
|
|
|
const bgSrc = el.getAttribute('data-bg');
|
|
|
const imgSrc = el.getAttribute('data-src');
|
|
|
// 寻找外层包裹的骨架屏容器(如果有)
|
|
|
const skeleton = el.closest('.skeleton-wrapper');
|
|
|
|
|
|
if (!bgSrc && !imgSrc) return;
|
|
|
|
|
|
// 1. 创建内存对象进行预加载 (Preload)
|
|
|
const loader = new Image();
|
|
|
|
|
|
loader.onload = () => {
|
|
|
// 2. 图片下载完毕,更新 DOM
|
|
|
if (bgSrc) {
|
|
|
el.style.backgroundImage = `url("${bgSrc}")`;
|
|
|
}
|
|
|
if (imgSrc) {
|
|
|
el.setAttribute('src', imgSrc);
|
|
|
}
|
|
|
|
|
|
// 3. 触发 CSS 动画 (Fade-in)
|
|
|
// 使用 requestAnimationFrame 确保样式重绘顺滑
|
|
|
requestAnimationFrame(() => {
|
|
|
el.classList.add(this.options.loadedClass);
|
|
|
});
|
|
|
|
|
|
// 4. 关闭骨架屏动画
|
|
|
if (skeleton) {
|
|
|
skeleton.classList.remove(this.options.skeletonClass);
|
|
|
// 可选:加载完成后移除背景色,或者保留作为底色
|
|
|
// skeleton.style.backgroundColor = 'transparent';
|
|
|
}
|
|
|
};
|
|
|
|
|
|
loader.onerror = () => {
|
|
|
console.warn('Image load failed:', bgSrc || imgSrc);
|
|
|
// 即使失败也移除动画,避免一直闪烁
|
|
|
if (skeleton) skeleton.classList.remove(this.options.skeletonClass);
|
|
|
};
|
|
|
|
|
|
// 开始下载
|
|
|
loader.src = bgSrc || imgSrc;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 手动强制预加载一组图片(不等待进入视口)
|
|
|
* 适用于:轮播图的首屏图片、模态框弹出前的图片
|
|
|
* @param {Array} urls 图片链接数组
|
|
|
*/
|
|
|
static preloadBatch(urls) {
|
|
|
urls.forEach(url => {
|
|
|
const img = new Image();
|
|
|
img.src = url;
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
|
// 加载图片,支持懒加载、预加载、骨架屏控制
|
|
|
window.ImageLoader = new AdvancedImageLoader();
|
|
|
// 菜单切换
|
|
|
const menuToggle = document.querySelector('.menu-toggle');
|
|
|
const navMenu = document.querySelector('.nav-menu');
|
|
|
|
|
|
if (menuToggle && navMenu) {
|
|
|
menuToggle.addEventListener('click', function() {
|
|
|
navMenu.classList.toggle('active');
|
|
|
|
|
|
// 切换菜单按钮动画
|
|
|
const spans = this.getElementsByTagName('span');
|
|
|
this.classList.toggle('active');
|
|
|
|
|
|
if (this.classList.contains('active')) {
|
|
|
spans[0].style.transform = 'rotate(45deg) translate(5px, 5px)';
|
|
|
spans[1].style.opacity = '0';
|
|
|
spans[2].style.transform = 'rotate(-45deg) translate(7px, -7px)';
|
|
|
} else {
|
|
|
spans[0].style.transform = 'none';
|
|
|
spans[1].style.opacity = '1';
|
|
|
spans[2].style.transform = 'none';
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// 移动端下拉菜单点击切换
|
|
|
function initMobileDropdown() {
|
|
|
const menuItems = document.querySelectorAll('.menu-item-has-children');
|
|
|
|
|
|
// 简单的节流函数
|
|
|
function throttle(func, limit) {
|
|
|
let inThrottle;
|
|
|
return function() {
|
|
|
const args = arguments;
|
|
|
const context = this;
|
|
|
if (!inThrottle) {
|
|
|
func.apply(context, args);
|
|
|
inThrottle = true;
|
|
|
setTimeout(() => inThrottle = false, limit);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 处理菜单切换逻辑
|
|
|
const toggleMenu = function(e, item) {
|
|
|
// 只在移动端处理
|
|
|
if (window.innerWidth > 768) return;
|
|
|
|
|
|
e.preventDefault();
|
|
|
e.stopPropagation();
|
|
|
|
|
|
// 如果正在动画中,则忽略点击(可选,依赖CSS过渡时间)
|
|
|
// 这里使用简单的类切换,由CSS控制动画
|
|
|
|
|
|
const isOpening = !item.classList.contains('mobile-open');
|
|
|
|
|
|
// 关闭同级其他菜单项
|
|
|
const siblings = item.parentNode.children;
|
|
|
for (let sibling of siblings) {
|
|
|
if (sibling !== item && sibling.classList.contains('menu-item-has-children')) {
|
|
|
sibling.classList.remove('mobile-open');
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 切换当前菜单状态
|
|
|
item.classList.toggle('mobile-open');
|
|
|
};
|
|
|
|
|
|
// 创建节流版本的切换函数,300ms内只响应一次
|
|
|
const throttledToggle = throttle(toggleMenu, 300);
|
|
|
|
|
|
menuItems.forEach(function(item) {
|
|
|
const link = item.querySelector('a');
|
|
|
const arrow = item.querySelector('.dropdown-arrow');
|
|
|
|
|
|
if (link) {
|
|
|
// 如果有箭头,优先使用箭头触发
|
|
|
if (arrow) {
|
|
|
arrow.addEventListener('click', function(e) {
|
|
|
throttledToggle(e, item);
|
|
|
});
|
|
|
|
|
|
// 同时也为箭头添加触摸事件监听,提高响应速度
|
|
|
arrow.addEventListener('touchend', function(e) {
|
|
|
// 防止触发 click
|
|
|
e.preventDefault();
|
|
|
throttledToggle(e, item);
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// 链接点击处理
|
|
|
link.addEventListener('click', function(e) {
|
|
|
const href = link.getAttribute('href');
|
|
|
// 如果是在移动端,且链接是空或者#,或者没有箭头(只能点链接),则触发菜单切换
|
|
|
if (window.innerWidth <= 768) {
|
|
|
if (!href || href === '#' || href === 'javascript:void(0);') {
|
|
|
throttledToggle(e, item);
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// 初始化移动端下拉菜单
|
|
|
initMobileDropdown();
|
|
|
|
|
|
// 窗口大小改变时重新初始化
|
|
|
window.addEventListener('resize', function() {
|
|
|
// 如果切换到桌面端,关闭所有移动端展开的菜单
|
|
|
if (window.innerWidth > 768) {
|
|
|
const openItems = document.querySelectorAll('.menu-item-has-children.mobile-open');
|
|
|
openItems.forEach(function(item) {
|
|
|
item.classList.remove('mobile-open');
|
|
|
});
|
|
|
}
|
|
|
});
|
|
|
|
|
|
// 滚动时改变导航栏样式
|
|
|
let lastScroll = 0;
|
|
|
const nav = document.querySelector('.main-nav');
|
|
|
|
|
|
// window.addEventListener('scroll', function() {
|
|
|
// const currentScroll = window.pageYOffset;
|
|
|
|
|
|
// // 到顶部时显示导航栏并清除背景和阴影
|
|
|
// if (currentScroll <= 0) {
|
|
|
// nav.classList.remove('scrolled');
|
|
|
// nav.style.transform = 'translateY(0)';
|
|
|
// nav.style.backgroundColor = '';
|
|
|
// nav.style.boxShadow = '';
|
|
|
// // 设置主菜单项颜色为白色
|
|
|
// const menuLinks = nav.querySelectorAll('.menu-items > li > a');
|
|
|
// menuLinks.forEach(link => {
|
|
|
// link.style.color = '#333';
|
|
|
// });
|
|
|
// // 确保下拉菜单项保持黑色
|
|
|
// const subMenuLinks = nav.querySelectorAll('.sub-menu li a');
|
|
|
// subMenuLinks.forEach(link => {
|
|
|
// link.style.color = '#333';
|
|
|
// });
|
|
|
// } else if (currentScroll > 80) {
|
|
|
// // 滚动超过80px时添加背景和阴影
|
|
|
// nav.classList.add('scrolled');
|
|
|
// nav.style.backgroundColor = '#fff';
|
|
|
// nav.style.boxShadow = '0 2px 10px rgba(0, 0, 0, 1)';
|
|
|
// // 设置主菜单项颜色为黑色
|
|
|
// const menuLinks = nav.querySelectorAll('.menu-items > li > a');
|
|
|
// menuLinks.forEach(link => {
|
|
|
// link.style.color = '#000';
|
|
|
// });
|
|
|
// // 确保下拉菜单项保持黑色
|
|
|
// const subMenuLinks = nav.querySelectorAll('.sub-menu li a');
|
|
|
// subMenuLinks.forEach(link => {
|
|
|
// link.style.color = '#333';
|
|
|
// });
|
|
|
// if (currentScroll > lastScroll) {
|
|
|
// // 向下滚动时显示导航栏
|
|
|
// nav.style.transform = 'translateY(0)';
|
|
|
// } else {
|
|
|
// // 向上滚动时隐藏导航栏
|
|
|
// nav.style.transform = 'translateY(-100%)';
|
|
|
// }
|
|
|
// } else {
|
|
|
// // 滚动在0-80px之间时清除背景和阴影
|
|
|
// nav.classList.remove('scrolled');
|
|
|
// nav.style.backgroundColor = '';
|
|
|
// nav.style.boxShadow = '';
|
|
|
// nav.style.transform = 'translateY(0)';
|
|
|
// // 设置主菜单项颜色为白色
|
|
|
// const menuLinks = nav.querySelectorAll('.menu-items > li > a');
|
|
|
// menuLinks.forEach(link => {
|
|
|
// link.style.color = '#333';
|
|
|
// });
|
|
|
// // 确保下拉菜单项保持黑色
|
|
|
// const subMenuLinks = nav.querySelectorAll('.sub-menu li a');
|
|
|
// subMenuLinks.forEach(link => {
|
|
|
// link.style.color = '#333';
|
|
|
// });
|
|
|
// }
|
|
|
|
|
|
// lastScroll = currentScroll;
|
|
|
// });
|
|
|
|
|
|
// 鼠标移入移出导航栏效果
|
|
|
nav.addEventListener('mouseenter', function() {
|
|
|
nav.classList.add('scrolled');
|
|
|
nav.style.backgroundColor = '#fff';
|
|
|
nav.style.boxShadow = '0 2px 10px rgba(0, 0, 0, 0.1)';
|
|
|
// 设置菜单项颜色为黑色
|
|
|
const menuLinks = nav.querySelectorAll('.menu-items > li > a');
|
|
|
menuLinks.forEach(link => {
|
|
|
link.style.color = '#000';
|
|
|
});
|
|
|
// 确保下拉菜单项保持黑色
|
|
|
const subMenuLinks = nav.querySelectorAll('.sub-menu li a');
|
|
|
subMenuLinks.forEach(link => {
|
|
|
link.style.color = '#333';
|
|
|
});
|
|
|
});
|
|
|
|
|
|
nav.addEventListener('mouseleave', function() {
|
|
|
const currentScroll = window.pageYOffset;
|
|
|
// 根据当前滚动位置决定是否保持背景色
|
|
|
if (currentScroll <= 80) {
|
|
|
nav.classList.remove('scrolled');
|
|
|
nav.style.backgroundColor = '';
|
|
|
nav.style.boxShadow = '';
|
|
|
|
|
|
// 设置菜单项颜色为白色
|
|
|
const menuLinks = nav.querySelectorAll('.menu-items > li > a');
|
|
|
menuLinks.forEach(link => {
|
|
|
link.style.color = '#333';
|
|
|
});
|
|
|
// 确保下拉菜单项保持黑色
|
|
|
const subMenuLinks = nav.querySelectorAll('.sub-menu li a');
|
|
|
subMenuLinks.forEach(link => {
|
|
|
link.style.color = '#333';
|
|
|
});
|
|
|
}
|
|
|
});
|
|
|
|
|
|
// 为有子菜单的项目添加特殊处理
|
|
|
const menuItemsWithChildren = nav.querySelectorAll('.menu-item-has-children');
|
|
|
menuItemsWithChildren.forEach(function(item) {
|
|
|
item.addEventListener('mouseenter', function() {
|
|
|
// 确保导航栏有背景色以便下拉菜单显示
|
|
|
nav.classList.add('scrolled');
|
|
|
nav.style.backgroundColor = '#fff';
|
|
|
nav.style.boxShadow = '0 2px 10px rgba(0, 0, 0, 0.1)';
|
|
|
const menuLinks = nav.querySelectorAll('.menu-items > li > a');
|
|
|
menuLinks.forEach(link => {
|
|
|
link.style.color = '#000';
|
|
|
});
|
|
|
// 确保下拉菜单项保持黑色
|
|
|
const subMenuLinks = nav.querySelectorAll('.sub-menu li a');
|
|
|
subMenuLinks.forEach(link => {
|
|
|
link.style.color = '#333';
|
|
|
});
|
|
|
});
|
|
|
});
|
|
|
|
|
|
// 窗口大小改变时重置菜单状态
|
|
|
window.addEventListener('resize', function() {
|
|
|
if (window.innerWidth > 768) {
|
|
|
// 桌面端时隐藏所有移动端展开的子菜单
|
|
|
document.querySelectorAll('.sub-menu').forEach(menu => {
|
|
|
menu.style.display = '';
|
|
|
});
|
|
|
}
|
|
|
});
|
|
|
});
|
|
|
|
|
|
// Banner轮播现在使用原生JavaScript实现,无需Swiper初始化
|
|
|
// 轮播逻辑已集成在block-banner.php模板中
|