feat: add draggable sidebar resizer (#100)

* feat: add draggable sidebar resizer

- Add resizer handle between map and event panel
- Sidebar width adjustable from 300px to 50% screen width
- Default width remains 400px
- Auto-adjust on window resize

* fix: preserve map zoom when resizing sidebar

Only update viewport screen size without re-fitting the map

* fix: use window size for canvas to prevent map scaling on resize

- Canvas size now based on window instead of container
- Sidebar resize only clips the visible area, map stays same size
- Remove CSS stretch on canvas element

* docs: update sidebar-resizer spec with canvas fix
This commit is contained in:
Zihao Xu
2026-01-25 02:44:54 -08:00
committed by GitHub
parent aaa636a08e
commit c406d2988f
4 changed files with 119 additions and 10 deletions

View File

@@ -27,6 +27,35 @@ const uiStore = useUiStore()
const settingStore = useSettingStore()
const showSplash = ref(true)
// Sidebar resizer 状态。
const sidebarWidth = ref(400)
const isResizing = ref(false)
const MIN_SIDEBAR_WIDTH = 300
function getMaxSidebarWidth() {
return Math.floor(window.innerWidth * 0.5)
}
function onResizerMouseDown(e: MouseEvent) {
e.preventDefault()
isResizing.value = true
document.addEventListener('mousemove', onResizerMouseMove)
document.addEventListener('mouseup', onResizerMouseUp)
}
function onResizerMouseMove(e: MouseEvent) {
if (!isResizing.value) return
const newWidth = window.innerWidth - e.clientX
const maxWidth = getMaxSidebarWidth()
sidebarWidth.value = Math.max(MIN_SIDEBAR_WIDTH, Math.min(newWidth, maxWidth))
}
function onResizerMouseUp() {
isResizing.value = false
document.removeEventListener('mousemove', onResizerMouseMove)
document.removeEventListener('mouseup', onResizerMouseUp)
}
const openedFromSplash = ref(false)
// 1. 游戏初始化逻辑
@@ -146,14 +175,26 @@ async function handleReturnToMain() {
}
}
// 窗口 resize 时,确保 sidebar 宽度不超过最大值。
function onWindowResize() {
const maxWidth = getMaxSidebarWidth()
if (sidebarWidth.value > maxWidth) {
sidebarWidth.value = maxWidth
}
}
onMounted(() => {
window.addEventListener('keydown', onKeydown)
window.addEventListener('resize', onWindowResize)
// Ensure backend language setting matches frontend preference
settingStore.syncBackend()
})
onUnmounted(() => {
window.removeEventListener('keydown', onKeydown)
window.removeEventListener('resize', onWindowResize)
document.removeEventListener('mousemove', onResizerMouseMove)
document.removeEventListener('mouseup', onResizerMouseUp)
})
</script>
@@ -210,7 +251,12 @@ onUnmounted(() => {
/>
<InfoPanelContainer />
</div>
<aside class="sidebar">
<div
class="sidebar-resizer"
:class="{ 'is-resizing': isResizing }"
@mousedown="onResizerMouseDown"
></div>
<aside class="sidebar" :style="{ width: sidebarWidth + 'px' }">
<EventPanel />
</aside>
</div>
@@ -305,12 +351,25 @@ onUnmounted(() => {
backdrop-filter: blur(4px);
}
.sidebar-resizer {
width: 4px;
background: transparent;
cursor: col-resize;
transition: background 0.15s;
flex-shrink: 0;
}
.sidebar-resizer:hover,
.sidebar-resizer.is-resizing {
background: #555;
}
.sidebar {
width: 400px;
background: #181818;
border-left: 1px solid #333;
display: flex;
flex-direction: column;
z-index: 20;
flex-shrink: 0;
}
</style>