/** * 悬浮球组件 * 可爱猫咪悬浮窗,支持拖拽,点击打开主界面 */ import { getSettings } from './config.js'; import { requestSave } from './save-manager.js'; // 悬浮球状态 let floatingBallState = { isDragging: false, startX: 0, startY: 0, initialX: 0, initialY: 0, currentX: 0, currentY: 0, hasMoved: false }; // SVG 图标 - 简约猫咪(只有耳朵和胡须) const FLOATING_BALL_SVG = ` `; // 创建悬浮球 export function createFloatingBall() { // 检查是否已存在 if (document.getElementById('wechat-floating-ball')) { return; } const ball = document.createElement('div'); ball.id = 'wechat-floating-ball'; ball.className = 'wechat-floating-ball'; ball.innerHTML = FLOATING_BALL_SVG; document.body.appendChild(ball); // 恢复位置 restorePosition(ball); // 绑定事件 bindFloatingBallEvents(ball); // 根据主界面状态设置悬浮球可见性 updateFloatingBallVisibility(); return ball; } // 恢复保存的位置 function restorePosition(ball) { const settings = getSettings(); const savedPos = settings.floatingBallPosition; if (savedPos && savedPos.x !== undefined && savedPos.y !== undefined) { // 确保位置在视口内 const maxX = window.innerWidth - 30; const maxY = window.innerHeight - 30; floatingBallState.currentX = Math.min(Math.max(0, savedPos.x), maxX); floatingBallState.currentY = Math.min(Math.max(0, savedPos.y), maxY); } else { // 默认位置:右侧中间 floatingBallState.currentX = window.innerWidth - 40; floatingBallState.currentY = (window.innerHeight - 30) / 2; } ball.style.left = floatingBallState.currentX + 'px'; ball.style.top = floatingBallState.currentY + 'px'; } // 保存位置 function savePosition() { const settings = getSettings(); settings.floatingBallPosition = { x: floatingBallState.currentX, y: floatingBallState.currentY }; requestSave(); } // 绑定事件 function bindFloatingBallEvents(ball) { // 鼠标事件 ball.addEventListener('mousedown', onDragStart); document.addEventListener('mousemove', onDragMove); document.addEventListener('mouseup', onDragEnd); // 触摸事件 ball.addEventListener('touchstart', onDragStart, { passive: false }); document.addEventListener('touchmove', onDragMove, { passive: false }); document.addEventListener('touchend', onDragEnd); // 窗口大小变化时调整位置 window.addEventListener('resize', () => { const maxX = window.innerWidth - 30; const maxY = window.innerHeight - 30; if (floatingBallState.currentX > maxX) { floatingBallState.currentX = maxX; ball.style.left = floatingBallState.currentX + 'px'; } if (floatingBallState.currentY > maxY) { floatingBallState.currentY = maxY; ball.style.top = floatingBallState.currentY + 'px'; } }); } // 开始拖拽 function onDragStart(e) { const ball = document.getElementById('wechat-floating-ball'); if (!ball) return; floatingBallState.isDragging = true; floatingBallState.hasMoved = false; // 获取起始位置 if (e.type === 'touchstart') { floatingBallState.startX = e.touches[0].clientX; floatingBallState.startY = e.touches[0].clientY; e.preventDefault(); } else { floatingBallState.startX = e.clientX; floatingBallState.startY = e.clientY; } floatingBallState.initialX = floatingBallState.currentX; floatingBallState.initialY = floatingBallState.currentY; ball.classList.add('dragging'); } // 拖拽移动 function onDragMove(e) { if (!floatingBallState.isDragging) return; const ball = document.getElementById('wechat-floating-ball'); if (!ball) return; let clientX, clientY; if (e.type === 'touchmove') { clientX = e.touches[0].clientX; clientY = e.touches[0].clientY; e.preventDefault(); } else { clientX = e.clientX; clientY = e.clientY; } const deltaX = clientX - floatingBallState.startX; const deltaY = clientY - floatingBallState.startY; // 如果移动距离超过5px,认为是拖拽而非点击 if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) { floatingBallState.hasMoved = true; } // 计算新位置 let newX = floatingBallState.initialX + deltaX; let newY = floatingBallState.initialY + deltaY; // 限制在视口内 const maxX = window.innerWidth - 30; const maxY = window.innerHeight - 30; newX = Math.min(Math.max(0, newX), maxX); newY = Math.min(Math.max(0, newY), maxY); floatingBallState.currentX = newX; floatingBallState.currentY = newY; ball.style.left = newX + 'px'; ball.style.top = newY + 'px'; } // 结束拖拽 function onDragEnd(e) { if (!floatingBallState.isDragging) return; const ball = document.getElementById('wechat-floating-ball'); if (ball) { ball.classList.remove('dragging'); } floatingBallState.isDragging = false; // 如果没有移动,视为点击 if (!floatingBallState.hasMoved) { toggleMainInterface(); } else { // 保存位置 savePosition(); } } // 切换主界面显示 function toggleMainInterface() { const phone = document.getElementById('wechat-phone'); if (!phone) return; const isHidden = phone.classList.contains('hidden'); if (isHidden) { phone.classList.remove('hidden'); } else { phone.classList.add('hidden'); } // 更新设置 const settings = getSettings(); settings.phoneVisible = isHidden; requestSave(); // 更新悬浮球状态 updateFloatingBallVisibility(); } // 更新悬浮球可见性(主界面显示时隐藏悬浮球,反之显示) export function updateFloatingBallVisibility() { const ball = document.getElementById('wechat-floating-ball'); const phone = document.getElementById('wechat-phone'); if (!ball) return; // 主界面隐藏时显示悬浮球,主界面显示时也显示悬浮球(方便用户随时关闭) ball.style.display = 'flex'; } // 显示悬浮球 export function showFloatingBall() { const ball = document.getElementById('wechat-floating-ball'); if (ball) { ball.style.display = 'flex'; } } // 隐藏悬浮球 export function hideFloatingBall() { const ball = document.getElementById('wechat-floating-ball'); if (ball) { ball.style.display = 'none'; } } // 销毁悬浮球 export function destroyFloatingBall() { const ball = document.getElementById('wechat-floating-ball'); if (ball) { ball.remove(); } }