feat: add paper-style mobile navigation
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
import { Plus } from 'lucide-vue-next'
|
||||
import { clampFabPosition, isFabDrag, snapFabPosition } from '../lib/mvp-utils'
|
||||
import { clampFabPosition, fabBottomReserved, isFabDrag, snapFabPosition } from '../lib/mvp-utils'
|
||||
|
||||
const props = withDefaults(defineProps<{ label?: string; show?: boolean }>(), { label: '添加', show: true })
|
||||
const emit = defineEmits<{ activate: [origin: { x: number; y: number }] }>()
|
||||
@@ -35,9 +35,16 @@ function startSnappingPulse() {
|
||||
snappingTimer = undefined
|
||||
}, 220)
|
||||
}
|
||||
function safeAreaBottom() {
|
||||
const value = getComputedStyle(document.documentElement).getPropertyValue('--safe-area-bottom')
|
||||
return Number.parseFloat(value) || 0
|
||||
}
|
||||
function currentBottomReserved() {
|
||||
return window.innerWidth <= 930 ? fabBottomReserved(safeAreaBottom()) : 84
|
||||
}
|
||||
function snapFabToCurrentViewport() {
|
||||
if (!position.value || dragging.value) return
|
||||
position.value = snapFabPosition(position.value.x, position.value.y, window.innerWidth, window.innerHeight, 56, 14, 84)
|
||||
position.value = snapFabPosition(position.value.x, position.value.y, window.innerWidth, window.innerHeight, 56, 14, currentBottomReserved())
|
||||
}
|
||||
function startDrag(event: PointerEvent) {
|
||||
if (!event.isPrimary || (event.pointerType === 'mouse' && event.button !== 0)) return
|
||||
@@ -45,7 +52,7 @@ function startDrag(event: PointerEvent) {
|
||||
snapping.value = false
|
||||
const target = event.currentTarget as HTMLElement
|
||||
const rect = target.getBoundingClientRect()
|
||||
position.value = clampFabPosition(rect.left, rect.top, window.innerWidth, window.innerHeight, 56, 14, 84)
|
||||
position.value = clampFabPosition(rect.left, rect.top, window.innerWidth, window.innerHeight, 56, 14, currentBottomReserved())
|
||||
pointer.value = { id: event.pointerId, startX: event.clientX, startY: event.clientY, originX: rect.left, originY: rect.top }
|
||||
try { target.setPointerCapture?.(event.pointerId) } catch { /* pointer capture unavailable in synthetic events */ }
|
||||
}
|
||||
@@ -56,7 +63,7 @@ function moveDrag(event: PointerEvent) {
|
||||
const deltaY = event.clientY - start.startY
|
||||
if (!dragging.value && !isFabDrag(deltaX, deltaY)) return
|
||||
dragging.value = true
|
||||
position.value = clampFabPosition(start.originX + deltaX, start.originY + deltaY, window.innerWidth, window.innerHeight, 56, 14, 84)
|
||||
position.value = clampFabPosition(start.originX + deltaX, start.originY + deltaY, window.innerWidth, window.innerHeight, 56, 14, currentBottomReserved())
|
||||
}
|
||||
function finishDrag(event: PointerEvent) {
|
||||
const start = pointer.value
|
||||
@@ -64,8 +71,8 @@ function finishDrag(event: PointerEvent) {
|
||||
const dragged = dragging.value || isFabDrag(event.clientX - start.startX, event.clientY - start.startY)
|
||||
pointer.value = null
|
||||
if (dragged) {
|
||||
const current = position.value ?? clampFabPosition(start.originX, start.originY, window.innerWidth, window.innerHeight, 56, 14, 84)
|
||||
position.value = snapFabPosition(current.x, current.y, window.innerWidth, window.innerHeight, 56, 14, 84)
|
||||
const current = position.value ?? clampFabPosition(start.originX, start.originY, window.innerWidth, window.innerHeight, 56, 14, currentBottomReserved())
|
||||
position.value = snapFabPosition(current.x, current.y, window.innerWidth, window.innerHeight, 56, 14, currentBottomReserved())
|
||||
startSnappingPulse()
|
||||
suppressClick = true
|
||||
if (suppressClickTimer) window.clearTimeout(suppressClickTimer)
|
||||
|
||||
Reference in New Issue
Block a user