https://github.com/nooooooom/use-draggable
A Vue hook that combines some common logic for dragging.
https://github.com/nooooooom/use-draggable
drag draggable vue-hooks
Last synced: 2 months ago
JSON representation
A Vue hook that combines some common logic for dragging.
- Host: GitHub
- URL: https://github.com/nooooooom/use-draggable
- Owner: nooooooom
- License: mit
- Created: 2022-02-26T14:25:36.000Z (about 3 years ago)
- Default Branch: main
- Last Pushed: 2022-03-11T15:54:05.000Z (about 3 years ago)
- Last Synced: 2024-04-26T13:05:17.892Z (about 1 year ago)
- Topics: drag, draggable, vue-hooks
- Language: TypeScript
- Homepage: https://www.npmjs.com/package/@reasoning/use-draggable
- Size: 90.8 KB
- Stars: 2
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# use-draggable
A Vue hook that combines some common logic for dragging.
[](https://badge.fury.io/js/@reasoning%2Fuse-draggable)
## Install
```sh
yarn add @reasoning/use-draggable --dev
# or
pnpm install @reasoning/use-draggable --save-dev
```## Options
| Option | Type | Description | Default |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | ------------------------------------- |
| target | MaybeRef | `TouchStart` listener region. | undefined |
| initialPostiion | { x: number; y: number; } | Initial position of the pointer. | { x: 0, y: 0 } |
| draggingTarget | HTMLElement | SVGElement | Window | Document | null | Element to attach `TouchMove` and `TouchEnd` events to, If undefined , it will get the window where the `target` is located. | Window |
| contains | Array | (event: MouseEvent | TouchEvent | PointerEvent, target: Element to which the `TouchStart` is bound) => boolean | Determine whether draggable should be started by whom. | undefined |
| pointerTypes | ['mouse' | 'pen' | 'touch'] | Specifies the pointer event type to use. | ['mouse' | 'pen' | 'touch'] |
| wrapper | Wrapper | Use wrapper to control the final output data. | undefined |
| onStart | (event: MouseEvent | TouchEvent | PointerEvent, position: MoveActionPosition, params: unknown) => void | A callback receiving the `TouchStart`. | undefined |
| onMove | (event: MouseEvent | TouchEvent | PointerEvent, position: MoveActionPosition, params: unknown) => void | A callback receiving the `TouchMove`. | undefined |
| onEnd | (event: MouseEvent | TouchEvent | PointerEvent, position: MoveActionPosition, params: unknown) => void | A callback receiving the `TouchEnd`. | undefined |## Response
| Name | Type | Description |
| -------- | ------------- | ----------------------------------- |
| position | Ref | The mouse position during dragging. |
| dragging | Ref | Whether it is dragging. |
| turnOff | () => void | Turn off all event listener. |## Basic Usage
Basic drag.
Without
```html
#box {
position: fixed;
width: 100px;
height: 100px;
background: #116dff;
}
``````ts
import { useDraggable } from '@reasoning/use-draggable'const boxEl = document.querySelector('#box')
let startRect
useDraggable(boxEl, {
onStart: () => {
startRect = boxEl.getBoundingClientRect()
},
onMove: (event, position) => {
// Each dragging, it will return the current mouse position and the position that differ from start.
// type position = { x: number; y: number; diff: { x: number; y: number } }
boxEl.style.top = `${startRect.top + position.diff.y}px`
boxEl.style.left = `${startRect.left + position.diff.x}px`
}
})
```### Use wrapper
`use-draggable` has a very interesting property - `wrapper` , which may be needed when you need to do some kind of calculation on mouse position, It is intended to separate your code logic.
For example, I want to put some constraints on the position of dragging
```ts
import { useDraggable } from '@reasoning/use-draggable'const boxEl = document.querySelector('#box')
let startRect
useDraggable(boxEl, {
wrapper: {
onMove: (event, position) => {
const top = startRect.top + position.diff.y
const left = startRect.left + position.diff.x
// We restrict the `box` to only move in the window.
return {
top: Math.max(0, Math.min(top, window.innerHeight - startRect.height)),
left: Math.max(0, Math.min(left, window.innerWidth - startRect.width))
}
}
},
onStart: () => {
startRect = boxEl.getBoundingClientRect()
},
onMove: (
event,
position,
// The result returned by `wrapper` will be passed in the third argument.
nextPosition
) => {
boxEl.style.top = `${nextPosition.top}px`
boxEl.style.left = `${nextPosition.left}px`
}
})
```So you can extract the core core logic and reuse it elsewhere.
`use-draggable` has some interesting built-in wrappers, such as `rotateWrapper`.
Some options of `rotateWrapper` can be found in the [source code](https://github.com/nooooooom/use-draggable/blob/main/src/wrappers/rotate.ts).
```ts
import { useDraggable } from '@reasoning/use-draggable'
import { rotateWrapper } from '@reasoning/use-draggable/wrappers'const boxEl = document.querySelector('#box')
let currentAngle = 0
useDraggable(boxEl, {
wrapper: rotateWrapper(
// Input in a position to tell `rotateWrapper` which point to use as the center point for angle calculations
{ x: 50, y: 50 }
),
onMove: (event, position, incrementAngle) => {
// Now the `box` will rotate around its own center.
boxEl.style.transform = `rotateZ(${currentAngle + incrementAngle}deg)`
},
onEnd: (event, position, incrementAngle) => {
currentAngle += incrementAngle
}
})
```Separating the logic of operations can help us better troubleshoot problems in our code, because in most cases we are just repeatedly calling some operation functions and then using their operation results.
### Merge wrappers
If you need to combine multiple wrappers, use-draggable provides a utility function called `mergeWrapper` for this purpose, which will pass the return value of the `wrapper` you pass in in turn from left to right, each ` wrapper` can accept the return value from the previous `wrapper`, for example
```ts
import { useDraggable } from '@reasoning/use-draggable'
import { rotateWrapper } from '@reasoning/use-draggable/wrappers'
import { mergeWrappers } from '@reasoning/use-draggable/utils'const boxEl = document.querySelector('#box')
let currentAngle = 0
useDraggable(boxEl, {
wrapper: mergeWrappers(rotateWrapper({ x: 50, y: 50 }), {
onMove: (event, position, incrementAngle) => {
return {
transform: `rotateZ(${currentAngle + incrementAngle}deg)`
}
},
onEnd: (event, position, incrementAngle) => {
currentAngle += incrementAngle
}
}),
onMove: (event, position, style) => {
boxEl.style = style
}
})
```This may have some benefits for code splitting, at least for me they are helpful.
## Feature
I'll keep populating some interesting built-in wrappers, I've enjoyed developing them, and I think they'll help draggable do a lot of interesting things.

## License
MIT