You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

328 lines
12 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**
* Simple Carousel Plugin - Swiper替代方案
* 轻量级轮播插件,避免与平滑滚动库冲突
*/
(function() {
'use strict';
class SimpleCarousel {
constructor(container, options = {}) {
this.container = container;
this.options = {
autoplay: true,
autoplayDelay: 5000,
loop: false,
navigation: true,
pagination: true,
touchEnabled: true,
...options
};
this.currentIndex = 0;
this.slides = [];
this.isAnimating = false;
this.autoplayTimer = null;
this.touchStartX = 0;
this.touchEndX = 0;
this.init();
}
init() {
this.setupSlides();
this.setupNavigation();
this.setupPagination();
this.setupTouch();
this.setupAutoplay();
this.updateSlides();
// 触发初始化完成回调
if (this.options.onInit) {
this.options.onInit();
}
}
setupSlides() {
const wrapper = this.container.querySelector('.carousel-wrapper');
if (!wrapper) return;
this.wrapper = wrapper; // 保存wrapper引用
this.slides = Array.from(wrapper.children);
this.slidesPerView = this.options.slidesPerView || 5;
this.totalGroups = Math.ceil(this.slides.length / this.slidesPerView);
// 设置轮播容器样式
wrapper.style.position = 'relative';
wrapper.style.overflow = 'hidden';
wrapper.style.display = 'flex';
wrapper.style.alignItems = 'center';
wrapper.style.transition = 'transform 0.5s ease';
// 为每个幻灯片设置样式
this.slides.forEach((slide, index) => {
if (this.slidesPerView === 1) {
// 单个幻灯片模式每个幻灯片占100%宽度
slide.style.flex = '0 0 100%';
} else {
// 多个幻灯片模式根据slidesPerView计算宽度
const slideWidth = 100 / this.slidesPerView;
slide.style.flex = `0 0 ${slideWidth}%`;
}
slide.style.transition = 'all 0.3s ease';
});
// 初始化显示第一组
this.updateSlides();
}
setupNavigation() {
if (!this.options.navigation) return;
const nextBtn = this.container.querySelector('.carousel-button-next');
const prevBtn = this.container.querySelector('.carousel-button-prev');
if (nextBtn) {
nextBtn.addEventListener('click', (e) => {
e.preventDefault();
this.next();
});
}
if (prevBtn) {
prevBtn.addEventListener('click', (e) => {
e.preventDefault();
this.prev();
});
}
}
setupPagination() {
if (!this.options.pagination) return;
const pagination = this.container.querySelector('.carousel-pagination');
if (!pagination) return;
// 使用现有的分页器按钮,只需要绑定事件
const bullets = pagination.querySelectorAll('.carousel-pagination-bullet');
bullets.forEach((bullet, index) => {
bullet.addEventListener('click', () => this.goTo(index));
});
}
setupTouch() {
if (!this.options.touchEnabled) return;
const wrapper = this.container.querySelector('.carousel-wrapper');
if (!wrapper) return;
wrapper.addEventListener('touchstart', (e) => {
this.touchStartX = e.touches[0].clientX;
this.pauseAutoplay();
}, { passive: true });
wrapper.addEventListener('touchend', (e) => {
this.touchEndX = e.changedTouches[0].clientX;
this.handleSwipe();
this.resumeAutoplay();
}, { passive: true });
}
setupAutoplay() {
if (!this.options.autoplay) return;
this.autoplayTimer = setInterval(() => {
this.next();
}, this.options.autoplayDelay);
}
handleSwipe() {
const swipeThreshold = 50;
const diff = this.touchStartX - this.touchEndX;
if (Math.abs(diff) > swipeThreshold) {
if (diff > 0) {
this.next();
} else {
this.prev();
}
}
}
next() {
if (this.isAnimating) return;
if (this.slidesPerView === 1) {
// 单个幻灯片模式:直接切换到下一个
let nextIndex = this.currentIndex + 1;
if (nextIndex >= this.slides.length) {
nextIndex = this.options.loop ? 0 : this.currentIndex;
}
if (nextIndex !== this.currentIndex) {
this.goTo(nextIndex);
}
} else {
// 多个幻灯片模式:按组切换
const currentGroup = Math.floor(this.currentIndex / this.slidesPerView);
let nextGroup = currentGroup + 1;
if (nextGroup >= this.totalGroups) {
nextGroup = this.options.loop ? 0 : currentGroup;
}
const nextIndex = nextGroup * this.slidesPerView;
if (nextIndex !== this.currentIndex) {
this.goTo(nextIndex);
}
}
}
prev() {
if (this.isAnimating) return;
if (this.slidesPerView === 1) {
// 单个幻灯片模式:直接切换到上一个
let prevIndex = this.currentIndex - 1;
if (prevIndex < 0) {
prevIndex = this.options.loop ? this.slides.length - 1 : this.currentIndex;
}
if (prevIndex !== this.currentIndex) {
this.goTo(prevIndex);
}
} else {
// 多个幻灯片模式:按组切换
const currentGroup = Math.floor(this.currentIndex / this.slidesPerView);
let prevGroup = currentGroup - 1;
if (prevGroup < 0) {
prevGroup = this.options.loop ? this.totalGroups - 1 : currentGroup;
}
const prevIndex = prevGroup * this.slidesPerView;
if (prevIndex !== this.currentIndex) {
this.goTo(prevIndex);
}
}
}
goTo(index) {
console.log('SimpleCarousel: goTo 被调用,目标索引:', index, '当前索引:', this.currentIndex);
if (this.isAnimating || index === this.currentIndex || index < 0 || index >= this.slides.length) {
console.log('SimpleCarousel: goTo 被阻止 - 动画中:', this.isAnimating, '相同索引:', index === this.currentIndex, '索引范围:', index < 0 || index >= this.slides.length);
return;
}
this.isAnimating = true;
const previousIndex = this.currentIndex;
this.currentIndex = index;
console.log('SimpleCarousel: 切换从索引', previousIndex, '到', this.currentIndex);
// 更新幻灯片
this.updateSlides();
// 触发回调
if (this.options.onSlideChange) {
console.log('SimpleCarousel: 触发 onSlideChange 回调');
this.options.onSlideChange(this.currentIndex, previousIndex);
}
setTimeout(() => {
this.isAnimating = false;
}, 500);
}
updateSlides() {
let translateX;
if (this.slidesPerView === 1) {
// 单个幻灯片模式:直接按索引移动
translateX = -this.currentIndex * 100;
} else {
// 多个幻灯片模式:按组移动
const currentGroup = Math.floor(this.currentIndex / this.slidesPerView);
translateX = -currentGroup * 100;
}
// 移动整个轮播容器
this.wrapper.style.transform = `translateX(${translateX}%)`;
// 更新分页器(如果存在)
const bullets = this.container.querySelectorAll('.carousel-pagination-bullet');
bullets.forEach((bullet, index) => {
if (this.slidesPerView === 1) {
// 单个幻灯片模式:直接按索引激活
bullet.classList.toggle('active', index === this.currentIndex);
} else {
// 多个幻灯片模式:激活当前可见范围内的幻灯片
const currentGroup = Math.floor(this.currentIndex / this.slidesPerView);
const slideGroup = Math.floor(index / this.slidesPerView);
bullet.classList.toggle('active', slideGroup === currentGroup);
}
});
}
pauseAutoplay() {
if (this.autoplayTimer) {
clearInterval(this.autoplayTimer);
this.autoplayTimer = null;
}
}
resumeAutoplay() {
if (this.options.autoplay && !this.autoplayTimer) {
this.autoplayTimer = setInterval(() => {
this.next();
}, this.options.autoplayDelay);
}
}
destroy() {
this.pauseAutoplay();
// 移除事件监听器
const nextBtn = this.container.querySelector('.carousel-button-next');
const prevBtn = this.container.querySelector('.carousel-button-prev');
const bullets = this.container.querySelectorAll('.carousel-pagination-bullet');
if (nextBtn) nextBtn.replaceWith(nextBtn.cloneNode(true));
if (prevBtn) prevBtn.replaceWith(prevBtn.cloneNode(true));
bullets.forEach(bullet => bullet.replaceWith(bullet.cloneNode(true)));
}
// 获取当前索引
get activeIndex() {
return this.currentIndex;
}
// 获取幻灯片总数
get slidesLength() {
return this.slides.length;
}
// 销毁实例
destroy() {
// 停止自动播放
this.pauseAutoplay();
// 清除事件监听器
this.removeEventListeners();
// 重置样式
if (this.wrapper) {
this.wrapper.style.transform = '';
}
// 清除引用
this.container = null;
this.wrapper = null;
this.slides = [];
this.autoplayTimer = null;
}
}
// 导出到全局
window.SimpleCarousel = SimpleCarousel;
})();