// 全局变量
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 `
${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;