// 全局变量 let currentAlbum = null; let currentImageIndex = 0; let images = []; // 设备检测 const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent); const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0; // API基础URL const API_BASE = '/api'; // 图片加载器类(与app.js中的保持一致) class ImageLoader { constructor() { this.cache = new Map(); } // 加载并解压图片 async loadImage(imageId, element) { // 检查缓存 if (this.cache.has(imageId)) { const cachedData = this.cache.get(imageId); this.setImageSrc(element, cachedData.blobUrl, cachedData.mimeType); return; } try { const response = await fetch(`${API_BASE}/image/${imageId}`); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const contentType = response.headers.get('content-type'); const contentEncoding = response.headers.get('content-encoding'); const originalContentType = response.headers.get('x-original-content-type'); let imageData; if (contentEncoding === 'br') { // Brotli压缩数据,需要解压 const compressedData = await response.arrayBuffer(); imageData = await this.decompressBrotli(compressedData); } else { // 未压缩数据 imageData = await response.blob(); } // 创建Blob URL const mimeType = originalContentType || contentType || 'image/jpeg'; const blob = new Blob([imageData], { type: mimeType }); const blobUrl = URL.createObjectURL(blob); // 缓存结果 this.cache.set(imageId, { blobUrl, mimeType }); // 设置图片源 this.setImageSrc(element, blobUrl, mimeType); } catch (error) { console.error('图片加载失败:', error); // 加载失败时使用占位图 element.src = '/assets/placeholder.svg'; } } // 设置图片源 setImageSrc(element, blobUrl, mimeType) { // 创建新的Image对象进行预加载 const img = new Image(); img.onload = () => { element.src = blobUrl; element.classList.add('loaded'); }; img.onerror = () => { console.error('图片解码失败'); element.src = '/assets/placeholder.svg'; }; img.src = blobUrl; } // Brotli解压 async decompressBrotli(compressedData) { // 检查浏览器是否支持Brotli解压 if (window.TextDecoder && window.TextEncoder) { try { // 使用浏览器的Brotli解压(如果支持) if (window.DecompressionStream && 'br' in window) { const decompressionStream = new DecompressionStream('br'); const decompressedStream = new Response(compressedData).body.pipeThrough(decompressionStream); return new Response(decompressedStream).arrayBuffer(); } } catch (error) { console.warn('浏览器Brotli解压失败,使用JavaScript解压:', error); } } // 使用JavaScript实现的Brotli解压(备用方案) return await this.decompressBrotliJS(compressedData); } // JavaScript实现的Brotli解压(备用方案) async decompressBrotliJS(compressedData) { // 这里可以集成一个轻量级的Brotli解压库 // 由于Brotli解压比较复杂,这里先返回原始数据 // 在实际项目中,可以使用第三方库如brotli-wasm console.warn('Brotli解压未实现,返回原始数据'); return compressedData; } // 清理缓存 clearCache() { // 清理所有Blob URL for (const [imageId, cached] of this.cache.entries()) { URL.revokeObjectURL(cached.blobUrl); } this.cache.clear(); } } // 创建全局图片加载器实例 const imageLoader = new ImageLoader(); // 页面加载完成后初始化 document.addEventListener('DOMContentLoaded', function() { // 添加设备类到body if (isMobile) { document.body.classList.add('mobile-device'); } if (isTouchDevice) { document.body.classList.add('touch-device'); } const urlParams = new URLSearchParams(window.location.search); const albumId = urlParams.get('id'); if (albumId) { loadAlbum(albumId); } else { showError('缺少相册ID参数'); } // 初始化键盘导航和触摸手势 initLightboxControls(); // 初始化图片懒加载 initAlbumLazyLoading(); }); // 初始化相册图片懒加载 function initAlbumLazyLoading() { // 使用Intersection Observer实现懒加载 if ('IntersectionObserver' in window) { const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; const imageId = img.getAttribute('data-image-id'); if (imageId && !img.classList.contains('loaded')) { imageLoader.loadImage(imageId, img); } observer.unobserve(img); } }); }, { rootMargin: '200px 0px', // 提前200px开始加载 threshold: 0.1 }); // 观察所有带有data-image-id的图片 document.querySelectorAll('img[data-image-id]').forEach(img => { if (!img.classList.contains('loaded')) { observer.observe(img); } }); } else { // 不支持Intersection Observer的回退方案 console.warn('浏览器不支持Intersection Observer,使用回退懒加载方案'); initAlbumFallbackLazyLoading(); } } // 相册回退懒加载方案 function initAlbumFallbackLazyLoading() { let scrollTimeout; function checkImagesInView() { const images = document.querySelectorAll('img[data-image-id]:not(.loaded)'); const viewportHeight = window.innerHeight; const scrollTop = window.scrollY; images.forEach(img => { const rect = img.getBoundingClientRect(); const isInView = rect.top < viewportHeight + 300 && rect.bottom > -300; if (isInView) { const imageId = img.getAttribute('data-image-id'); if (imageId) { imageLoader.loadImage(imageId, img); } } }); } window.addEventListener('scroll', () => { clearTimeout(scrollTimeout); scrollTimeout = setTimeout(checkImagesInView, 100); }); // 初始检查 checkImagesInView(); } // 显示图片骨架屏 function showImageSkeleton(count = 8) { const container = document.getElementById('images-container'); container.innerHTML = Array(count).fill(0).map(() => `
加载中...
`).join(''); } // 加载相册详情 async function loadAlbum(albumId) { try { // 显示骨架屏 showImageSkeleton(); const response = await fetch(`${API_BASE}/album/${albumId}`); if (!response.ok) { if (response.status === 404) { throw new Error('相册不存在'); } throw new Error(`HTTP error! status: ${response.status}`); } const albumData = await response.json(); currentAlbum = albumData.album; images = albumData.images; updateAlbumInfo(); displayImages(); // 初始化图片懒加载 initLazyLoading(); } catch (error) { console.error('加载相册失败:', error); showError('加载相册失败: ' + error.message); } } // 更新相册信息 function updateAlbumInfo() { document.getElementById('album-title').textContent = currentAlbum.title; let infoHtml = `${images.length} 张图片`; document.getElementById('album-info').innerHTML = infoHtml; // 显示相册元数据(人名 + 标签) updateAlbumMeta(); document.title = `${currentAlbum.title} - 图片展示服务`; } // 标签分类映射(与后端 tag_extractor.rs 保持一致) const TAG_CATEGORIES = { style: ['日系', '韩系', '森系', '复古', '甜美', '性感', '清新', '冷艳', '优雅', '知性', '可爱', '俏皮', '温柔', '妩媚', '野性', '清纯', '时尚', '潮流', '街头', '学院', '文艺', '古典', '民族', '异域', 'japanese', 'korean', 'retro', 'vintage', 'cute', 'sexy', 'elegant', 'fresh', 'sweet', 'wild', 'pure', 'fashion'], outfit: ['比基尼', '泳装', '泳衣', '旗袍', '制服', '丝袜', '内衣', '文胸', '包臀裙', '紧身裙', '超短裙', '连衣裙', '半身裙', '短裙', '长裙', '牛仔裤', '热裤', '短裤', '休闲裤', '西装裤', 'T恤', '衬衫', '毛衣', '针织衫', '外套', '风衣', '大衣', '西装', '吊带', '抹胸', '露背', '透视', '蕾丝', '镂空', '开叉', '高跟鞋', '运动鞋', '凉鞋', '拖鞋', '靴子', 'bikini', 'swimsuit', 'stockings', 'lingerie', 'dress', 'skirt', 'jeans', 'shirt', 'sweater', 'coat', 'heels'], scene: ['室内', '户外', '街拍', '浴室', '浴缸', '床上', '床照', '沙发', '厨房', '阳台', '窗边', '楼梯', '走廊', '电梯', '车内', '驾驶座', '海边', '沙滩', '泳池', '温泉', '森林', '草地', '花园', '公园', '街道', '巷弄', '建筑', '天台', '屋顶', '咖啡馆', '餐厅', '酒店', 'indoor', 'outdoor', 'street', 'beach', 'pool', 'bathroom', 'bedroom', 'kitchen', 'balcony', 'car', 'cafe', 'hotel'], body: ['美腿', '美胸', '翘臀', '玉足', '美背', '香肩', '蛮腰', '细腰', '长腿', '酥胸', '丰臀', '白皙', '肌肤', '纹身', 'legs', 'breasts', 'ass', 'feet', 'back', 'shoulders', 'waist', 'skin', 'tattoo'], mood: ['清纯', '诱惑', '唯美', '清新', '冷艳', '妩媚', '娇羞', '俏皮', '温柔', '野性', '冷傲', '热情', '慵懒', '迷离', '甜美', '知性', 'pure', 'seductive', 'beautiful', 'fresh', 'cool', 'charming', 'shy', 'cute', 'gentle', 'wild', 'lazy', 'sweet'], topic: ['写真', '私房', '街拍', '旅拍', '婚纱照', '艺术照', '生活照', '自拍', '跟拍', '约拍', '商拍', 'photoshoot', 'portrait', 'selfie', 'travel', 'wedding'], }; // 获取标签的分类 function getTagCategory(tag) { const tagLower = tag.toLowerCase(); for (const [category, tags] of Object.entries(TAG_CATEGORIES)) { if (tags.some(t => t.toLowerCase() === tagLower)) { return category; } } return 'default'; } // 更新相册元数据(人名 + 标签)简洁版:只用颜色区分,不写分类名 function updateAlbumMeta() { const metaContainer = document.getElementById('album-meta-tags'); if (!metaContainer) return; let chips = []; // 添加人名标签 if (currentAlbum.person_name_cleaned) { chips.push({ text: currentAlbum.person_name_cleaned, type: 'person', category: 'person', query: currentAlbum.person_name_cleaned }); } // 添加标签(支持多种格式) if (currentAlbum.tags) { let tagList = []; try { const parsed = JSON.parse(currentAlbum.tags); if (Array.isArray(parsed)) { parsed.forEach(item => { if (Array.isArray(item)) { tagList.push({ text: item[0], category: item[1] || 'default' }); } else if (typeof item === 'string') { tagList.push({ text: item, category: getTagCategory(item) }); } else if (item.tag || item.name) { const t = item.tag || item.name; const c = item.category || item.type || getTagCategory(t); tagList.push({ text: t, category: c }); } }); } } catch (e) { currentAlbum.tags.split(',').forEach(tag => { const t = tag.trim(); if (t) tagList.push({ text: t, category: getTagCategory(t) }); }); } tagList.forEach(t => { chips.push({ text: t.text, type: 'tag', category: t.category, query: t.text }); }); } if (chips.length === 0) { metaContainer.style.display = 'none'; return; } metaContainer.style.display = 'flex'; metaContainer.innerHTML = chips.map(chip => { const icon = chip.type === 'person' ? 'fa-user' : 'fa-tag'; const cssClass = chip.type === 'person' ? 'meta-chip-person' : `meta-chip-tag-${chip.category || 'default'}`; return ` ${chip.text} `; }).join(''); } // 事件委托:点击元数据标签 document.addEventListener('click', function(e) { const chip = e.target.closest('.meta-chip'); if (!chip) return; const query = chip.dataset.metaQuery; if (query) { searchByMeta(query); } }); // 点击元数据标签搜索 function searchByMeta(encodedQuery) { window.location.href = `/?search=${encodedQuery}`; } // 智能判断图片方向 - 更精确的判断逻辑 function getImageOrientation(width, height) { if (!width || !height) return 'square'; const ratio = width / height; // 更精确的阈值判断 if (ratio > 1.5) return 'landscape'; // 宽幅图片 if (ratio < 0.67) return 'portrait'; // 竖幅图片 if (ratio > 1.2) return 'wide'; // 较宽图片 if (ratio < 0.83) return 'tall'; // 较高图片 return 'square'; // 方形或接近方形的图片 } // 显示图片列表 function displayImages() { const container = document.getElementById('images-container'); if (images.length === 0) { container.innerHTML = '

此相册暂无图片
'; return; } container.innerHTML = images.map((image, index) => { // 智能判断图片方向 const orientation = getImageOrientation(image.width, image.height); // 使用懒加载技术 let imageUrl = '/assets/placeholder.svg'; let dataSrc = ''; if (image.id) { dataSrc = `${API_BASE}/image/${image.id}`; imageUrl = '/assets/placeholder.svg'; // 初始使用占位图 } else if (image.original_url) { dataSrc = image.original_url; imageUrl = '/assets/placeholder.svg'; } const title = image.title || `图片 ${index + 1}`; // 检查图片是否已被收藏 isImageFavorited(image.id || image.original_url); return `
${title}
${image.width ? `${image.width}×${image.height}` : ''} ${image.file_size ? `${(image.file_size / 1024).toFixed(2)} KB` : ''}
`; }).join(''); } // 打开图片查看器 - 使用previewModal相同逻辑 function openLightbox(index) { currentImageIndex = index; const lightbox = document.getElementById('lightbox'); // 更新lightbox内容 updateLightboxContent(index); // 显示lightbox lightbox.classList.add('active'); document.body.style.overflow = 'hidden'; } // 关闭图片查看器 function closeLightbox() { const lightbox = document.getElementById('lightbox'); lightbox.classList.remove('active'); document.body.style.overflow = 'auto'; } // 下一张图片 function nextImage() { const newIndex = (currentImageIndex + 1) % images.length; updateLightboxContent(newIndex); } // 上一张图片 function prevImage() { const newIndex = (currentImageIndex - 1 + images.length) % images.length; updateLightboxContent(newIndex); } // 更新lightbox内容(不关闭面板) function updateLightboxContent(newIndex) { currentImageIndex = newIndex; const image = images[newIndex]; // 更新标题和相册信息 const titleElement = document.getElementById('lightbox-title'); const albumElement = document.getElementById('lightbox-album'); if (titleElement) titleElement.textContent = image.title || `图片 ${newIndex + 1}`; if (albumElement) albumElement.textContent = `来自: ${currentAlbum.title}`; // 更新计数器 const counterElement = document.getElementById('lightbox-counter'); if (counterElement) counterElement.textContent = `${newIndex + 1} / ${images.length}`; // 更新导航按钮状态 const prevButton = document.getElementById('lightbox-prev'); const nextButton = document.getElementById('lightbox-next'); if (prevButton) prevButton.disabled = newIndex === 0; if (nextButton) nextButton.disabled = newIndex === images.length - 1; // 检查收藏状态 checkLightboxFavoriteStatus(); // 更新元数据 const dimensionsElement = document.getElementById('lightbox-dimensions'); const sizeElement = document.getElementById('lightbox-size'); if (dimensionsElement) { dimensionsElement.innerHTML = ` ${image.width || 0} × ${image.height || 0}`; } if (sizeElement) { const fileSizeKB = image.file_size ? (image.file_size / 1024).toFixed(2) : '0'; sizeElement.innerHTML = ` ${fileSizeKB} KB`; } // 更新图片 const imageElement = document.getElementById('lightbox-img'); const loadingElement = document.querySelector('#lightbox .preview-loading'); if (imageElement && loadingElement) { // 显示加载状态 imageElement.style.opacity = '0'; loadingElement.style.display = 'flex'; // 加载新图片 const newImage = new Image(); // 在lightbox中优先使用原始图片以获得更好的显示效果 let imageUrl = '/assets/placeholder.svg'; if (image.id) { imageUrl = `${API_BASE}/image/${image.id}`; } else if (image.original_url) { imageUrl = image.original_url; } newImage.src = imageUrl; newImage.alt = image.title || `图片 ${newIndex + 1}`; newImage.className = 'preview-image'; newImage.onload = function() { // 替换图片 if (imageElement.parentNode) { imageElement.parentNode.replaceChild(newImage, imageElement); // 重新设置ID newImage.id = 'lightbox-img'; } // 隐藏加载状态 loadingElement.style.display = 'none'; newImage.style.opacity = '1'; }; newImage.onerror = function() { // 图片加载失败 loadingElement.innerHTML = '图片加载失败'; }; } } // 初始化图片懒加载 function initLazyLoading() { // 使用Intersection Observer实现懒加载 if ('IntersectionObserver' in window) { const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; const imageId = img.getAttribute('data-image-id'); if (imageId && !img.classList.contains('loaded')) { imageLoader.loadImage(imageId, img); } observer.unobserve(img); } }); }, { rootMargin: '200px 0px', // 提前200px开始加载 threshold: 0.1 }); // 观察所有带有data-image-id的图片 document.querySelectorAll('img[data-image-id]').forEach(img => { if (!img.classList.contains('loaded')) { observer.observe(img); } }); } else { // 不支持Intersection Observer的回退方案 console.warn('浏览器不支持Intersection Observer,使用回退懒加载方案'); initAlbumFallbackLazyLoading(); } } // 初始化lightbox控制 function initLightboxControls() { // 键盘导航 document.addEventListener('keydown', function(event) { const lightbox = document.getElementById('lightbox'); if (lightbox.classList.contains('active')) { switch(event.key) { case 'Escape': closeLightbox(); break; case 'ArrowLeft': prevImage(); break; case 'ArrowRight': nextImage(); break; } } }); // 点击背景关闭 document.getElementById('lightbox').addEventListener('click', function(event) { if (event.target === this) closeLightbox(); }); // 触摸手势支持 if (isTouchDevice) { let touchStartX = 0; let touchStartY = 0; document.addEventListener('touchstart', function(event) { touchStartX = event.touches[0].clientX; touchStartY = event.touches[0].clientY; }); document.addEventListener('touchend', function(event) { const lightbox = document.getElementById('lightbox'); if (!lightbox.classList.contains('active')) return; // 检查点击目标是否是导航按钮或关闭按钮 const target = event.target; const isNavButton = target.closest('.preview-nav') || target.closest('.preview-close'); if (isNavButton) { // 如果是按钮点击,不处理轻点关闭逻辑 return; } const touchEndX = event.changedTouches[0].clientX; const touchEndY = event.changedTouches[0].clientY; const deltaX = touchEndX - touchStartX; const deltaY = touchEndY - touchStartY; // 水平滑动距离大于垂直滑动距离,且水平滑动距离足够大 if (Math.abs(deltaX) > Math.abs(deltaY) && Math.abs(deltaX) > 50) { if (deltaX > 0) { prevImage(); } else { nextImage(); } // 滑动操作后阻止轻点关闭判断 return; } // 轻点关闭(如果移动距离很小) if (Math.abs(deltaX) < 10 && Math.abs(deltaY) < 10) { closeLightbox(); } }); } } // 从lightbox添加收藏 function addToFavoritesFromLightbox() { const image = images[currentImageIndex]; const imageId = image.id; if (!imageId) { alert('无法收藏此图片:缺少图片ID'); return; } // 检查是否已经收藏 const favoriteBtn = document.getElementById('lightbox-favorite-btn'); if (favoriteBtn.classList.contains('favorited')) { // 取消收藏 fetch(`${API_BASE}/favorites/image/${imageId}`, { method: 'DELETE' }) .then(response => { if (!response.ok) { throw new Error('取消收藏失败'); } return response.json(); }) .then(() => { favoriteBtn.classList.remove('favorited'); favoriteBtn.innerHTML = ' 加入收藏'; showFavoriteToast('已取消收藏', false); }) .catch(error => { console.error('取消收藏失败:', error); alert('取消收藏失败,请稍后重试'); }); } else { // 添加收藏 const favoriteData = { image_id: imageId, album_id: currentAlbum.id }; fetch(`${API_BASE}/favorites`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(favoriteData) }) .then(response => { if (!response.ok) { throw new Error('添加收藏失败'); } return response.json(); }) .then(() => { favoriteBtn.classList.add('favorited'); favoriteBtn.innerHTML = ' 已收藏'; showFavoriteToast('已添加到收藏夹', true); }) .catch(error => { console.error('添加收藏失败:', error); alert('添加收藏失败,请稍后重试'); }); } // 添加动画效果 const heartIcon = favoriteBtn.querySelector('i'); if (heartIcon) { heartIcon.style.transform = 'scale(1.3)'; setTimeout(() => { heartIcon.style.transform = 'scale(1)'; }, 300); } } // 检查图片收藏状态并更新lightbox按钮 function checkLightboxFavoriteStatus() { const image = images[currentImageIndex]; const imageId = image.id; const favoriteBtn = document.getElementById('lightbox-favorite-btn'); if (!imageId || !favoriteBtn) return; // 检查收藏状态 fetch(`${API_BASE}/favorites/status/${imageId}`) .then(response => { if (!response.ok) { throw new Error('检查收藏状态失败'); } return response.json(); }) .then(status => { if (status.is_favorited) { favoriteBtn.classList.add('favorited'); favoriteBtn.innerHTML = ' 已收藏'; } else { favoriteBtn.classList.remove('favorited'); favoriteBtn.innerHTML = ' 加入收藏'; } }) .catch(error => { console.error('检查收藏状态失败:', error); // 失败时默认显示"加入收藏" favoriteBtn.classList.remove('favorited'); favoriteBtn.innerHTML = ' 加入收藏'; }); } // 显示错误信息 function showError(message) { const container = document.getElementById('images-container'); container.innerHTML = `

${message}
`; document.getElementById('album-title').textContent = '加载失败'; document.getElementById('album-info').innerHTML = ''; } // 移动端优化:防止双击缩放 function preventDoubleTapZoom() { if (isTouchDevice) { let lastTouchEnd = 0; document.addEventListener('touchend', function(event) { const now = (new Date()).getTime(); if (now - lastTouchEnd <= 300) { event.preventDefault(); } lastTouchEnd = now; }, false); } } // 性能监控 function logPerformance() { if ('performance' in window) { window.addEventListener('load', () => { setTimeout(() => { const navigationTiming = performance.getEntriesByType('navigation')[0]; console.log('相册页面加载时间:', navigationTiming.loadEventEnd - navigationTiming.navigationStart, 'ms'); }, 0); }); } } // 检查图片是否已收藏 function isImageFavorited(imageId) { // 这里需要从后端API获取收藏状态 // 暂时返回false,实际实现中需要调用API return false; } // 切换收藏状态 function toggleFavorite(event, imageIndex) { event.stopPropagation(); // 阻止事件冒泡,避免打开lightbox const image = images[imageIndex]; const imageId = image.id; if (!imageId) { alert('无法收藏此图片:缺少图片ID'); return; } // 找到按钮元素(event.target 可能是 i 图标) const btn = event.target.closest('.favorite-btn'); if (!btn) return; // 检查当前收藏状态 const isFavorited = btn.classList.contains('favorited'); if (isFavorited) { // 取消收藏 fetch(`${API_BASE}/favorites/image/${imageId}`, { method: 'DELETE' }) .then(response => { if (!response.ok) { throw new Error('取消收藏失败'); } return response.json(); }) .then(() => { btn.classList.remove('favorited'); showFavoriteToast('已取消收藏', false); }) .catch(error => { console.error('取消收藏失败:', error); alert('取消收藏失败,请稍后重试'); }); } else { // 添加收藏 const favoriteData = { image_id: imageId, album_id: currentAlbum.id }; fetch(`${API_BASE}/favorites`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(favoriteData) }) .then(response => { if (!response.ok) { throw new Error('添加收藏失败'); } return response.json(); }) .then(() => { btn.classList.add('favorited'); showFavoriteToast('已添加到收藏夹', true); }) .catch(error => { console.error('添加收藏失败:', error); alert('添加收藏失败,请稍后重试'); }); } // 添加动画效果 let heartIcon; // 确保找到正确的图标元素 if (event.target.tagName === 'I') { heartIcon = event.target; } else if (event.target.tagName === 'BUTTON') { heartIcon = event.target.querySelector('i'); } else { heartIcon = event.target.closest('.favorite-btn')?.querySelector('i'); } if (heartIcon) { heartIcon.style.transform = 'scale(1.3)'; setTimeout(() => { heartIcon.style.transform = 'scale(1)'; }, 300); } } // 显示收藏提示 function showFavoriteToast(message, isAdd) { // 创建提示元素 let toast = document.getElementById('favorite-toast'); if (!toast) { toast = document.createElement('div'); toast.id = 'favorite-toast'; toast.style.cssText = ` position: fixed; top: 20px; right: 20px; background: ${isAdd ? '#10b981' : '#ef4444'}; color: white; padding: 12px 20px; border-radius: 25px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 1000; opacity: 0; transform: translateX(100px); transition: all 0.3s ease; display: flex; align-items: center; gap: 8px; `; document.body.appendChild(toast); } toast.innerHTML = ` ${message}`; toast.style.background = isAdd ? '#10b981' : '#ef4444'; // 显示动画 setTimeout(() => { toast.style.opacity = '1'; toast.style.transform = 'translateX(0)'; }, 10); // 隐藏动画 setTimeout(() => { toast.style.opacity = '0'; toast.style.transform = 'translateX(100px)'; setTimeout(() => { if (toast.parentNode) { toast.parentNode.removeChild(toast); } }, 300); }, 2000); } // 初始化性能监控和移动端优化 logPerformance(); preventDoubleTapZoom(); // 导出函数供全局使用 window.openLightbox = openLightbox; window.closeLightbox = closeLightbox; window.prevImage = prevImage; window.nextImage = nextImage; window.toggleFavorite = toggleFavorite; window.addToFavoritesFromLightbox = addToFavoritesFromLightbox;