/** * Drawer Component - 抽屉组件 * 支持移动端手势拖拽和PC端右侧滑出 */ class Drawer { constructor() { this.overlay = null; this.container = null; this.handle = null; this.body = null; this.isOpen = false; this.isDragging = false; this.startY = 0; this.currentY = 0; this.scrollTop = 0; // 保存body滚动位置 this.init(); } /** * 初始化抽屉组件 */ init() { this.createDrawerHTML(); this.bindEvents(); } /** * 创建抽屉DOM结构 */ createDrawerHTML() { const drawerHTML = `

PFP全身图

特征详情

`; document.body.insertAdjacentHTML('beforeend', drawerHTML); // 获取DOM引用 this.overlay = document.getElementById('drawer-overlay'); this.container = document.getElementById('drawer-container'); this.handle = document.getElementById('drawer-handle'); this.body = document.getElementById('drawer-body'); } /** * 绑定所有事件 */ bindEvents() { // 点击遮罩关闭 this.overlay.addEventListener('click', (e) => { if (e.target === this.overlay) { this.close(); } }); // 关闭按钮 document.getElementById('drawer-close').addEventListener('click', () => { this.close(); }); // 手势拖拽事件(仅移动端) this.handle.addEventListener('touchstart', this.handleTouchStart.bind(this), { passive: true }); this.handle.addEventListener('touchmove', this.handleTouchMove.bind(this), { passive: false }); this.handle.addEventListener('touchend', this.handleTouchEnd.bind(this), { passive: true }); // ESC键关闭 this.handleEscKey = (e) => { if (e.key === 'Escape' && this.isOpen) { this.close(); } }; document.addEventListener('keydown', this.handleEscKey); } /** * 打开抽屉 * @param {Object} pfpData - PFP数据对象 */ open(pfpData) { if (!pfpData) return; // 填充标题 const title = `#${pfpData.num} ${pfpData.name || '山海 · 精卫'}`; document.getElementById('drawer-title').textContent = title; // 填充图片 const img = document.getElementById('drawer-image'); img.src = pfpData.src; img.alt = `PFP #${pfpData.num}`; // 渲染特征 this.renderFeatures(pfpData.features); // 防止body滚动穿透 this.scrollTop = window.pageYOffset || document.documentElement.scrollTop; document.body.style.overflow = 'hidden'; document.body.style.position = 'fixed'; document.body.style.top = `-${this.scrollTop}px`; document.body.style.width = '100%'; // 显示抽屉 this.overlay.classList.add('active'); this.isOpen = true; } /** * 关闭抽屉 */ close() { if (!this.isOpen) return; this.overlay.classList.remove('active'); this.isOpen = false; // 恢复container的transform(如果被拖拽移动过) this.container.style.transition = ''; this.container.style.transform = ''; // 延迟恢复body滚动,等待动画完成 setTimeout(() => { document.body.style.overflow = ''; document.body.style.position = ''; document.body.style.top = ''; document.body.style.width = ''; window.scrollTo(0, this.scrollTop); }, 350); } /** * 渲染特征列表 * @param {string} featuresStr - 特征字符串 */ renderFeatures(featuresStr) { const grid = document.getElementById('feature-grid'); grid.innerHTML = ''; if (!featuresStr) { grid.innerHTML = '

暂无特征信息

'; return; } // 解析特征字符串:格式 "分类_特征名:特征值,..." const features = featuresStr.split(',').map(item => { const [key, value] = item.split(':'); if (!key || !value) return null; const [category, name] = key.split('_'); return { category: category || '未知', name: name || '未知', value: value || '' }; }).filter(Boolean); if (features.length === 0) { grid.innerHTML = '

暂无特征信息

'; return; } // 渲染特征项 features.forEach(feature => { const item = document.createElement('div'); item.className = 'feature-item'; item.innerHTML = ` ${feature.category} ${feature.name} ${feature.value} `; grid.appendChild(item); }); } /** * 触摸开始 * @param {TouchEvent} e */ handleTouchStart(e) { // 仅在移动端处理 if (window.innerWidth >= 1200) return; this.isDragging = true; this.startY = e.touches[0].clientY; this.currentY = this.startY; // 禁用过渡动画,实现跟手效果 this.container.style.transition = 'none'; } /** * 触摸移动 * @param {TouchEvent} e */ handleTouchMove(e) { if (!this.isDragging || window.innerWidth >= 1200) return; this.currentY = e.touches[0].clientY; const deltaY = this.currentY - this.startY; // 只允许向下拖拽 if (deltaY > 0) { e.preventDefault(); // 阻止默认滚动 this.container.style.transform = `translateY(${deltaY}px)`; // 根据拖拽距离动态调整遮罩透明度 const opacity = Math.max(0, 1 - deltaY / 400); this.overlay.style.background = `rgba(0, 0, 0, ${opacity * 0.6})`; } } /** * 触摸结束 * @param {TouchEvent} e */ handleTouchEnd(e) { if (!this.isDragging || window.innerWidth >= 1200) return; this.isDragging = false; // 恢复过渡动画 this.container.style.transition = ''; const deltaY = this.currentY - this.startY; const threshold = 150; // 关闭阈值 if (deltaY > threshold) { // 超过阈值,关闭抽屉 this.close(); } else { // 未超过阈值,回弹 this.container.style.transform = ''; this.overlay.style.background = ''; } } /** * 销毁抽屉组件 */ destroy() { if (this.isOpen) { this.close(); } // 移除事件监听 document.removeEventListener('keydown', this.handleEscKey); // 移除DOM if (this.overlay && this.overlay.parentNode) { this.overlay.parentNode.removeChild(this.overlay); } // 清空引用 this.overlay = null; this.container = null; this.handle = null; this.body = null; } }