优化:百家乐骰子悬浮按钮支持拖拽移动,位置记忆 localStorage
This commit is contained in:
@@ -295,24 +295,131 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- ─── 骰子悬浮入口(游戏开启时常驻) ─── --}}
|
{{-- ─── 骰子悬浮入口(游戏开启时常驻,支持拖拽) ─── --}}
|
||||||
<div id="baccarat-fab" x-data="{ visible: false }" x-show="visible" x-cloak
|
<div id="baccarat-fab" x-data="{ visible: false }" x-show="visible" x-cloak
|
||||||
style="position:fixed; bottom:90px; right:18px; z-index:9900;">
|
style="position:fixed; bottom:90px; right:18px; z-index:9900; touch-action:none;">
|
||||||
<button
|
<button id="baccarat-fab-btn" x-on:click.stop="() => {}"
|
||||||
x-on:click="() => {
|
style="width:52px; height:52px; border-radius:50%; border:none; cursor:grab;
|
||||||
|
background:linear-gradient(135deg,#7c3aed,#4f46e5);
|
||||||
|
box-shadow:0 4px 20px rgba(124,58,237,.5);
|
||||||
|
font-size:22px; display:flex; align-items:center; justify-content:center;
|
||||||
|
animation:pulse-fab 2s infinite; user-select:none;"
|
||||||
|
title="百家乐下注中(可拖动)">🎲</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/**
|
||||||
|
* 百家乐骰子悬浮按钮拖拽逻辑
|
||||||
|
* 需在 DOM 就绪后初始化,因为 x-cloak 元素初始隐藏
|
||||||
|
*/
|
||||||
|
(function initBaccaratFabDrag() {
|
||||||
|
const LS_KEY = 'baccaratFabPos';
|
||||||
|
|
||||||
|
function attachDrag() {
|
||||||
|
const fab = document.getElementById('baccarat-fab');
|
||||||
|
const btn = document.getElementById('baccarat-fab-btn');
|
||||||
|
if (!fab || !btn || fab._dragInited) return;
|
||||||
|
fab._dragInited = true;
|
||||||
|
|
||||||
|
// 恢复上次拖拽位置
|
||||||
|
const saved = (() => {
|
||||||
|
try {
|
||||||
|
return JSON.parse(localStorage.getItem(LS_KEY));
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
if (saved) {
|
||||||
|
fab.style.left = saved.left + 'px';
|
||||||
|
fab.style.top = saved.top + 'px';
|
||||||
|
fab.style.right = 'auto';
|
||||||
|
fab.style.bottom = 'auto';
|
||||||
|
}
|
||||||
|
|
||||||
|
let isDragging = false;
|
||||||
|
let startX, startY, startLeft, startTop;
|
||||||
|
|
||||||
|
function onStart(e) {
|
||||||
|
const rect = fab.getBoundingClientRect();
|
||||||
|
// 转为绝对 left/top
|
||||||
|
fab.style.left = rect.left + 'px';
|
||||||
|
fab.style.top = rect.top + 'px';
|
||||||
|
fab.style.right = 'auto';
|
||||||
|
fab.style.bottom = 'auto';
|
||||||
|
|
||||||
|
isDragging = false;
|
||||||
|
const cx = e.touches ? e.touches[0].clientX : e.clientX;
|
||||||
|
const cy = e.touches ? e.touches[0].clientY : e.clientY;
|
||||||
|
startX = cx;
|
||||||
|
startY = cy;
|
||||||
|
startLeft = rect.left;
|
||||||
|
startTop = rect.top;
|
||||||
|
btn.style.cursor = 'grabbing';
|
||||||
|
|
||||||
|
document.addEventListener('mousemove', onMove, {
|
||||||
|
passive: false
|
||||||
|
});
|
||||||
|
document.addEventListener('mouseup', onEnd);
|
||||||
|
document.addEventListener('touchmove', onMove, {
|
||||||
|
passive: false
|
||||||
|
});
|
||||||
|
document.addEventListener('touchend', onEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMove(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
const cx = e.touches ? e.touches[0].clientX : e.clientX;
|
||||||
|
const cy = e.touches ? e.touches[0].clientY : e.clientY;
|
||||||
|
const dx = cx - startX,
|
||||||
|
dy = cy - startY;
|
||||||
|
if (Math.abs(dx) > 3 || Math.abs(dy) > 3) isDragging = true;
|
||||||
|
if (!isDragging) return;
|
||||||
|
|
||||||
|
const newLeft = Math.max(0, Math.min(window.innerWidth - fab.offsetWidth, startLeft + dx));
|
||||||
|
const newTop = Math.max(0, Math.min(window.innerHeight - fab.offsetHeight, startTop + dy));
|
||||||
|
fab.style.left = newLeft + 'px';
|
||||||
|
fab.style.top = newTop + 'px';
|
||||||
|
}
|
||||||
|
|
||||||
|
function onEnd() {
|
||||||
|
btn.style.cursor = 'grab';
|
||||||
|
document.removeEventListener('mousemove', onMove);
|
||||||
|
document.removeEventListener('mouseup', onEnd);
|
||||||
|
document.removeEventListener('touchmove', onMove);
|
||||||
|
document.removeEventListener('touchend', onEnd);
|
||||||
|
|
||||||
|
if (isDragging) {
|
||||||
|
localStorage.setItem(LS_KEY, JSON.stringify({
|
||||||
|
left: parseInt(fab.style.left),
|
||||||
|
top: parseInt(fab.style.top),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
btn.addEventListener('mousedown', onStart);
|
||||||
|
btn.addEventListener('touchstart', onStart, {
|
||||||
|
passive: true
|
||||||
|
});
|
||||||
|
|
||||||
|
// 非拖拽时才打开面板
|
||||||
|
btn.addEventListener('click', () => {
|
||||||
|
if (isDragging) return;
|
||||||
const p = Alpine.$data(document.getElementById('baccarat-panel'));
|
const p = Alpine.$data(document.getElementById('baccarat-panel'));
|
||||||
|
if (!p) return;
|
||||||
p.show = true;
|
p.show = true;
|
||||||
if (p.phase === 'betting' && p.countdown > 0 && !p.countdownTimer) {
|
if (p.phase === 'betting' && p.countdown > 0 && !p.countdownTimer) {
|
||||||
p.startCountdown();
|
p.startCountdown();
|
||||||
}
|
}
|
||||||
}"
|
});
|
||||||
style="width:52px; height:52px; border-radius:50%; border:none; cursor:pointer;
|
}
|
||||||
background:linear-gradient(135deg,#7c3aed,#4f46e5);
|
|
||||||
box-shadow:0 4px 20px rgba(124,58,237,.5);
|
// 首次尝试(元素可能已在 DOM 中)
|
||||||
font-size:22px; display:flex; align-items:center; justify-content:center;
|
document.addEventListener('DOMContentLoaded', attachDrag);
|
||||||
animation:pulse-fab 2s infinite;"
|
|
||||||
title="百家乐下注中">🎲</button>
|
// Alpine 使 x-cloak 元素出现后再次尝试
|
||||||
</div>
|
document.addEventListener('alpine:initialized', () => setTimeout(attachDrag, 100));
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@keyframes spin {
|
@keyframes spin {
|
||||||
|
|||||||
Reference in New Issue
Block a user