feat: turn floating add button into sleeping cat
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
<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'
|
||||
|
||||
const props = withDefaults(defineProps<{ label?: string }>(), { label: '添加' })
|
||||
const emit = defineEmits<{ activate: [origin: { x: number; y: number }] }>()
|
||||
const dragging = ref(false)
|
||||
const snapping = ref(false)
|
||||
const edge = ref<'left' | 'right'>('right')
|
||||
const position = ref<{ x: number; y: number } | null>(null)
|
||||
const pointer = ref<{ id: number; startX: number; startY: number; originX: number; originY: number } | null>(null)
|
||||
let suppressClick = false
|
||||
@@ -30,11 +30,17 @@ function startSnappingPulse() {
|
||||
snappingTimer = undefined
|
||||
}, 220)
|
||||
}
|
||||
function updateEdge() {
|
||||
if (!position.value) return
|
||||
edge.value = position.value.x + 28 <= window.innerWidth / 2 ? 'left' : 'right'
|
||||
}
|
||||
function snapFabToCurrentViewport() {
|
||||
if (!position.value || dragging.value) return
|
||||
position.value = snapFabPosition(position.value.x, position.value.y, window.innerWidth, window.innerHeight, 56, 14, 84)
|
||||
updateEdge()
|
||||
}
|
||||
function startDrag(event: PointerEvent) {
|
||||
if (!event.isPrimary || (event.pointerType === 'mouse' && event.button !== 0)) return
|
||||
clearSnappingTimer()
|
||||
snapping.value = false
|
||||
const target = event.currentTarget as HTMLElement
|
||||
@@ -60,6 +66,7 @@ function finishDrag(event: PointerEvent) {
|
||||
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)
|
||||
updateEdge()
|
||||
startSnappingPulse()
|
||||
suppressClick = true
|
||||
window.setTimeout(() => { suppressClick = false }, 180)
|
||||
@@ -80,7 +87,18 @@ onUnmounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<button class="unified-fab" :class="{ dragging, snapping }" :style="buttonStyle" :aria-label="props.label" @pointerdown="startDrag" @pointermove="moveDrag" @pointerup="finishDrag" @pointercancel="finishDrag" @click="activate">
|
||||
<Plus />
|
||||
<button class="unified-fab" :class="{ dragging, snapping, awake: dragging, sleeping: !dragging, 'edge-left': edge === 'left', 'edge-right': edge === 'right' }" :style="buttonStyle" :aria-label="props.label" @pointerdown="startDrag" @pointermove="moveDrag" @pointerup="finishDrag" @pointercancel="finishDrag" @click="activate">
|
||||
<svg class="fab-cat" viewBox="0 0 56 56" aria-hidden="true">
|
||||
<g class="fab-cat-body">
|
||||
<path class="fab-cat-tail" d="M41 38c7 2 8 9 3 11-4 2-8-1-7-4 1-2 4-2 5 0"/>
|
||||
<path class="fab-cat-back" d="M13 41c1-10 7-17 17-17 8 0 14 5 15 13 1 6-3 10-10 10H19c-5 0-7-2-6-6Z"/>
|
||||
<path class="fab-cat-head" d="M12 24 15 12l8 6c4-2 9-2 13 0l8-6 2 13c0 9-7 15-17 15S12 33 12 24Z"/>
|
||||
<path class="fab-cat-ear" d="m17 18-1-4 4 3m20 1 2-4-5 3"/>
|
||||
<path class="fab-cat-eye fab-cat-eye--sleeping" d="M19 27c2 2 4 2 6 0m8 0c2 2 4 2 6 0"/>
|
||||
<g class="fab-cat-eye fab-cat-eye--awake"><circle cx="22" cy="27" r="2"/><circle cx="36" cy="27" r="2"/></g>
|
||||
<path class="fab-cat-face" d="m27 31 2 1 2-1m-2 1v2m-13-2-6-1m6 4-6 2m32-5 6-1m-6 4 6 2"/>
|
||||
</g>
|
||||
<g class="fab-cat-sleep"><text x="39" y="13">z</text><text x="45" y="8">z</text></g>
|
||||
</svg>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user