| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 |
- /**
- * 图片轮播组件
- */
- class Carousel {
- constructor(container, images) {
- this.container = container;
- this.images = images || [];
- this.currentIndex = 0;
- this.init();
- }
- init() {
- if (this.images.length === 0) {
- this.container.innerHTML = '<div class="carousel-empty">暂无图片</div>';
- return;
- }
- this.render();
- this.bindEvents();
- }
- render() {
- const html = `
- <div class="carousel-images" data-carousel-track>
- ${this.images.map((img, index) => `
- <img
- class="carousel-image"
- src="${img}"
- alt="图片 ${index + 1}"
- loading="lazy"
- data-index="${index}"
- />
- `).join('')}
- </div>
- ${this.images.length > 1 ? `
- <div class="carousel-controls">
- <button class="carousel-btn carousel-prev" data-carousel-prev>‹</button>
- <button class="carousel-btn carousel-next" data-carousel-next>›</button>
- </div>
- <div class="carousel-indicators">
- ${this.images.map((_, index) => `
- <span
- class="carousel-indicator ${index === 0 ? 'active' : ''}"
- data-carousel-indicator="${index}"
- ></span>
- `).join('')}
- </div>
- ` : ''}
- `;
- this.container.innerHTML = html;
- }
- bindEvents() {
- if (this.images.length <= 1) return;
- // 上一张
- const prevBtn = this.container.querySelector('[data-carousel-prev]');
- if (prevBtn) {
- prevBtn.addEventListener('click', (e) => {
- e.stopPropagation();
- this.prev();
- });
- }
- // 下一张
- const nextBtn = this.container.querySelector('[data-carousel-next]');
- if (nextBtn) {
- nextBtn.addEventListener('click', (e) => {
- e.stopPropagation();
- this.next();
- });
- }
- // 指示器点击
- const indicators = this.container.querySelectorAll('[data-carousel-indicator]');
- indicators.forEach(indicator => {
- indicator.addEventListener('click', (e) => {
- e.stopPropagation();
- const index = parseInt(indicator.getAttribute('data-carousel-indicator'));
- this.goTo(index);
- });
- });
- // 图片点击打开模态框
- const imgElements = this.container.querySelectorAll('.carousel-image');
- imgElements.forEach(img => {
- img.addEventListener('click', () => {
- this.openModal(this.currentIndex);
- });
- });
- // 键盘快捷键
- document.addEventListener('keydown', (e) => {
- if (e.key === 'ArrowLeft') this.prev();
- if (e.key === 'ArrowRight') this.next();
- });
- }
- prev() {
- this.currentIndex = (this.currentIndex - 1 + this.images.length) % this.images.length;
- this.update();
- }
- next() {
- this.currentIndex = (this.currentIndex + 1) % this.images.length;
- this.update();
- }
- goTo(index) {
- this.currentIndex = index;
- this.update();
- }
- update() {
- const track = this.container.querySelector('[data-carousel-track]');
- if (track) {
- track.style.transform = `translateX(-${this.currentIndex * 100}%)`;
- }
- // 更新指示器
- const indicators = this.container.querySelectorAll('[data-carousel-indicator]');
- indicators.forEach((indicator, index) => {
- indicator.classList.toggle('active', index === this.currentIndex);
- });
- }
- openModal(startIndex) {
- ImageModal.open(this.images, startIndex);
- }
- }
- /**
- * 图片查看器模态框
- */
- class ImageModal {
- static modal = null;
- static images = [];
- static currentIndex = 0;
- static init() {
- this.modal = document.getElementById('image-modal');
- if (!this.modal) return;
- const overlay = document.getElementById('modal-overlay');
- const closeBtn = document.getElementById('modal-close');
- const prevBtn = document.getElementById('modal-prev');
- const nextBtn = document.getElementById('modal-next');
- // 关闭
- overlay?.addEventListener('click', () => this.close());
- closeBtn?.addEventListener('click', () => this.close());
- // 切换
- prevBtn?.addEventListener('click', () => this.prev());
- nextBtn?.addEventListener('click', () => this.next());
- // 键盘
- document.addEventListener('keydown', (e) => {
- if (!this.modal?.classList.contains('active')) return;
- if (e.key === 'Escape') this.close();
- if (e.key === 'ArrowLeft') this.prev();
- if (e.key === 'ArrowRight') this.next();
- });
- }
- static open(images, startIndex = 0) {
- this.images = images;
- this.currentIndex = startIndex;
- this.show();
- }
- static show() {
- if (!this.modal) return;
- this.modal.classList.add('active');
- this.updateImage();
- document.body.style.overflow = 'hidden';
- }
- static close() {
- if (!this.modal) return;
- this.modal.classList.remove('active');
- document.body.style.overflow = '';
- }
- static prev() {
- this.currentIndex = (this.currentIndex - 1 + this.images.length) % this.images.length;
- this.updateImage();
- }
- static next() {
- this.currentIndex = (this.currentIndex + 1) % this.images.length;
- this.updateImage();
- }
- static updateImage() {
- const img = document.getElementById('modal-image');
- const caption = document.getElementById('modal-caption');
- if (img) {
- img.src = this.images[this.currentIndex];
- }
- if (caption) {
- caption.textContent = `${this.currentIndex + 1} / ${this.images.length}`;
- }
- }
- }
- // 初始化模态框
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', () => ImageModal.init());
- } else {
- ImageModal.init();
- }
|