# useLongPress

## 使用方法

检测单指按住不放，并告诉你按在哪里，于是触摸用户也能用到「指针靠右键才有」的那些功能。iOS Safari 在长按后根本不会产生 `contextmenu` 事件，所以它是通往「按坐标定位的浮层」（例如 `ContextMenu`）的唯一触摸途径。手指移动、抬起、多指按下，或者组件卸载，都会取消这次按住。

hook 自己不加任何监听。两种绑法：把返回的 `handlers` 摊到元素上（`<div v-on="longPress.handlers">`）；或者自己驱动——`pointerdown` 时调 `start(point)`，`pointermove` 时调 `moveTo(point)`，`pointerup` 和 `pointercancel` 时调 `cancel`。`ContextMenu` 用的是后一种，并且会跳过鼠标指针，让右键走自己的路。

```ts
import { useLongPress } from '@astryx-vue/core'
```

```vue
<script setup lang="ts">
import { useLongPress } from '@astryx-vue/core'
import { ref } from 'vue'

const isDisabled = ref(false)

const longPress = useLongPress({
  disabled: isDisabled,
  onLongPress: (point) => { openMenuAt(point.x, point.y) },
})
</script>

<template>
  <button type="button" v-on="longPress.handlers">
    按住我
  </button>
</template>
```

## 建议

| 指引 | 做法 |
| --- | --- |
| 推荐 | 用报上来的坐标给打开的浮层定位，让它出现在手指下面。 |
| 推荐 | 和指针的 `contextmenu` 处理一起用，而不是替换它；这个 hook 只管触摸。 |
| 避免 | 把按住不放做成控件的主操作；只能靠长按才能做到的事，用户发现不了。 |

## 参数

| 名称 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `options` | `LongPressOptions` | — | 配置对象。必填。 |
| `options.onLongPress` | `(point: { x: number, y: number }) => void` | — | 按满 `delayMs` 之后触发，参数是按下时的坐标。必填。 |
| `options.disabled` | `MaybeRefOrGetter<boolean>` | `false` | 为 true 时这次不检测，所以被禁用的触发器只要改这个值，不用重新注册。每次按下时读，写普通布尔值、ref 或 getter 都行。 |
| `options.isDisabled` | `MaybeRefOrGetter<boolean>` | — | 同一个开关的另一个名字，任一为真就不检测。 |
| `options.delayMs` | `number` | `500` | 要按住多少毫秒才算长按。 |
| `options.moveCancelPx` | `number` | `10` | 任一轴上移动超过这个距离就取消，当作滚动或拖拽。 |

## 返回值

| 名称 | 类型 | 说明 |
| --- | --- | --- |
| `handlers` | `{ onTouchStart, onTouchMove, onTouchEnd, onTouchCancel }` | 四个触摸监听，直接摊到目标元素上，例如 `<button v-on="press.handlers">`。第二根手指、抬手、取消都会结束这次按住；移动超过 `moveCancelPx` 也一样。 |
| `start` | `(point: { x: number, y: number }) => void` | 从一个坐标开始检测。在 `pointerdown` 里调。它会先清掉上一次没完成的按住，再重新计时。 |
| `moveTo` | `(point: { x: number, y: number }) => boolean` | 报告指针的新位置。还在容差内返回 true；超出去就取消这次按住并返回 false。没有正在进行的按住时也返回 false。 |
| `cancel` | `() => void` | 丢掉还没完成的按住。绑在 `pointerup` 和 `pointercancel` 上。组件卸载时也会执行。 |
