kashkool ver 1
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||
|
||||
const DRAG_THRESHOLD = 6
|
||||
|
||||
type FabPosition = { x: number; y: number }
|
||||
|
||||
type DraggableFloatingOptions = {
|
||||
width?: number
|
||||
height?: number
|
||||
defaultBottomOffset?: number
|
||||
defaultRightOffset?: number
|
||||
}
|
||||
|
||||
export function useDraggableFloatingButton(
|
||||
storageKey: string,
|
||||
options: DraggableFloatingOptions = {}
|
||||
) {
|
||||
const width = options.width ?? 96
|
||||
const height = options.height ?? 44
|
||||
const defaultBottomOffset = options.defaultBottomOffset ?? 120
|
||||
const defaultRightOffset = options.defaultRightOffset ?? 20
|
||||
|
||||
const pos = ref<FabPosition>({ x: 0, y: 0 })
|
||||
const dragging = ref(false)
|
||||
|
||||
let pointerId: number | null = null
|
||||
let startX = 0
|
||||
let startY = 0
|
||||
let originX = 0
|
||||
let originY = 0
|
||||
let hasMoved = false
|
||||
|
||||
const fabStyle = computed(() => ({
|
||||
left: `${pos.value.x}px`,
|
||||
top: `${pos.value.y}px`,
|
||||
width: `${width}px`,
|
||||
touchAction: 'none'
|
||||
}))
|
||||
|
||||
function getViewportWidth() {
|
||||
return Math.min(window.innerWidth, 426)
|
||||
}
|
||||
|
||||
function getHorizontalOffset() {
|
||||
const vw = window.innerWidth
|
||||
const contentWidth = getViewportWidth()
|
||||
return Math.max(0, (vw - contentWidth) / 2)
|
||||
}
|
||||
|
||||
function resetToDefault() {
|
||||
const vh = window.innerHeight
|
||||
const offsetX = getHorizontalOffset()
|
||||
const contentWidth = getViewportWidth()
|
||||
|
||||
pos.value = {
|
||||
x: offsetX + contentWidth - width - defaultRightOffset,
|
||||
y: vh - defaultBottomOffset - height
|
||||
}
|
||||
}
|
||||
|
||||
function clampToViewport() {
|
||||
const vw = window.innerWidth
|
||||
const vh = window.innerHeight
|
||||
|
||||
pos.value = {
|
||||
x: Math.max(0, Math.min(vw - width, pos.value.x)),
|
||||
y: Math.max(0, Math.min(vh - height, pos.value.y))
|
||||
}
|
||||
}
|
||||
|
||||
function loadPosition() {
|
||||
try {
|
||||
const raw = localStorage.getItem(storageKey)
|
||||
if (!raw) return false
|
||||
const parsed = JSON.parse(raw) as FabPosition
|
||||
if (typeof parsed?.x === 'number' && typeof parsed?.y === 'number') {
|
||||
pos.value = parsed
|
||||
clampToViewport()
|
||||
return true
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function savePosition() {
|
||||
localStorage.setItem(storageKey, JSON.stringify(pos.value))
|
||||
}
|
||||
|
||||
function handleResize() {
|
||||
clampToViewport()
|
||||
savePosition()
|
||||
}
|
||||
|
||||
function handlePointerDown(event: PointerEvent) {
|
||||
dragging.value = true
|
||||
hasMoved = false
|
||||
pointerId = event.pointerId
|
||||
startX = event.clientX
|
||||
startY = event.clientY
|
||||
originX = pos.value.x
|
||||
originY = pos.value.y
|
||||
;(event.currentTarget as HTMLElement)?.setPointerCapture?.(event.pointerId)
|
||||
}
|
||||
|
||||
function handlePointerMove(event: PointerEvent) {
|
||||
if (!dragging.value || pointerId !== event.pointerId) return
|
||||
|
||||
const dx = event.clientX - startX
|
||||
const dy = event.clientY - startY
|
||||
|
||||
if (Math.abs(dx) > DRAG_THRESHOLD || Math.abs(dy) > DRAG_THRESHOLD) {
|
||||
hasMoved = true
|
||||
}
|
||||
|
||||
pos.value = {
|
||||
x: originX + dx,
|
||||
y: originY + dy
|
||||
}
|
||||
clampToViewport()
|
||||
}
|
||||
|
||||
function finishPointer(event: PointerEvent) {
|
||||
if (!dragging.value || pointerId !== event.pointerId) return false
|
||||
|
||||
dragging.value = false
|
||||
pointerId = null
|
||||
savePosition()
|
||||
|
||||
const clicked = !hasMoved
|
||||
hasMoved = false
|
||||
return clicked
|
||||
}
|
||||
|
||||
function handlePointerUp(event: PointerEvent) {
|
||||
return finishPointer(event)
|
||||
}
|
||||
|
||||
function handlePointerCancel(event: PointerEvent) {
|
||||
finishPointer(event)
|
||||
return false
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (!loadPosition()) {
|
||||
resetToDefault()
|
||||
}
|
||||
window.addEventListener('resize', handleResize)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener('resize', handleResize)
|
||||
})
|
||||
|
||||
return {
|
||||
dragging,
|
||||
fabStyle,
|
||||
handlePointerDown,
|
||||
handlePointerMove,
|
||||
handlePointerUp,
|
||||
handlePointerCancel,
|
||||
resetToDefault
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user