/* =================================== UI工具函数 =================================== */ import { CONFIG } from '../config.js'; export class UIUtils { /** * 切换界面显示/隐藏 * @param {HTMLElement} hideElement - 要隐藏的元素 * @param {HTMLElement} showElement - 要显示的元素 * @param {Function} onComplete - 完成回调 */ static switchInterface(hideElement, showElement, onComplete) { const duration = CONFIG.animation.ui.fadeDuration; gsap.to(hideElement, { opacity: 0, duration: duration, onComplete: () => { hideElement.style.display = 'none'; showElement.style.display = 'block'; gsap.to(showElement, { opacity: 1, duration: duration, onComplete: onComplete }); } }); } /** * 批量显示元素(带渐入动画) * @param {string} selector - CSS选择器 * @param {object} options - 动画选项 */ static staggerShow(selector, options = {}) { const defaults = { opacity: 1, y: 0, duration: 0.6, stagger: CONFIG.animation.ui.cardStagger, delay: CONFIG.animation.ui.cardDelay, ease: "power2.out" }; gsap.to(selector, { ...defaults, ...options }); } /** * 移动端触摸事件适配 * @param {HTMLElement} element - 目标元素 * @param {object} handlers - 事件处理器 { onTap, onSwipe } */ static bindTouchEvents(element, handlers) { let startX = 0; let startY = 0; let startTime = 0; element.addEventListener('touchstart', (e) => { startX = e.touches[0].clientX; startY = e.touches[0].clientY; startTime = Date.now(); }); element.addEventListener('touchend', (e) => { const endX = e.changedTouches[0].clientX; const endY = e.changedTouches[0].clientY; const endTime = Date.now(); const deltaX = endX - startX; const deltaY = endY - startY; const deltaTime = endTime - startTime; // 判断是点击还是滑动 if (Math.abs(deltaX) < 10 && Math.abs(deltaY) < 10 && deltaTime < 300) { // 点击 if (handlers.onTap) handlers.onTap(e); } else { // 滑动 if (handlers.onSwipe) { const direction = Math.abs(deltaX) > Math.abs(deltaY) ? (deltaX > 0 ? 'right' : 'left') : (deltaY > 0 ? 'down' : 'up'); handlers.onSwipe(direction, e); } } }); } /** * 防抖函数 * @param {Function} func - 要防抖的函数 * @param {number} wait - 等待时间(毫秒) * @returns {Function} */ static debounce(func, wait = 300) { let timeout; return function executedFunction(...args) { const later = () => { clearTimeout(timeout); func(...args); }; clearTimeout(timeout); timeout = setTimeout(later, wait); }; } /** * 节流函数 * @param {Function} func - 要节流的函数 * @param {number} limit - 限制间隔(毫秒) * @returns {Function} */ static throttle(func, limit = 100) { let inThrottle; return function executedFunction(...args) { if (!inThrottle) { func(...args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; } /** * 检测是否为移动设备 * @returns {boolean} */ static isMobile() { return window.innerWidth <= CONFIG.mobile.breakpoint; } /** * 获取安全区域内边距 * @returns {object} { top, bottom, left, right } */ static getSafeAreaInsets() { const computed = getComputedStyle(document.documentElement); return { top: parseInt(computed.getPropertyValue('env(safe-area-inset-top)')) || 0, bottom: parseInt(computed.getPropertyValue('env(safe-area-inset-bottom)')) || 0, left: parseInt(computed.getPropertyValue('env(safe-area-inset-left)')) || 0, right: parseInt(computed.getPropertyValue('env(safe-area-inset-right)')) || 0 }; } /** * 初始化陀螺仪控制(移动端视差) * @param {Function} callback - 回调函数,接收 (beta, gamma) 参数 */ static initGyroscope(callback) { if (!this.isMobile()) return; if (window.DeviceOrientationEvent) { window.addEventListener('deviceorientation', (e) => { const beta = e.beta || 0; // 前后倾斜(-180 ~ 180) const gamma = e.gamma || 0; // 左右倾斜(-90 ~ 90) callback(beta, gamma); }); } } /** * 显示加载提示 * @param {string} text - 提示文本 */ static showLoading(text = '加载中...') { const existing = document.getElementById('loading-overlay'); if (existing) return; const overlay = document.createElement('div'); overlay.id = 'loading-overlay'; overlay.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.8); display: flex; align-items: center; justify-content: center; z-index: 9999; color: #38bdf8; font-size: 1rem; `; overlay.innerText = text; document.body.appendChild(overlay); } /** * 隐藏加载提示 */ static hideLoading() { const overlay = document.getElementById('loading-overlay'); if (overlay) { overlay.remove(); } } }