Skip to content

Commit d87b8a3

Browse files
committed
refactor: move dragging functionality into composable function
1 parent f57515c commit d87b8a3

File tree

2 files changed

+122
-91
lines changed

2 files changed

+122
-91
lines changed

src/components/Carousel/Carousel.ts

Lines changed: 29 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ import {
4949
} from './Carousel.types'
5050
import { carouselProps } from './carouselProps'
5151

52+
import { useDragging } from '@/composables'
53+
5254
export const Carousel = defineComponent({
5355
name: 'VueCarousel',
5456
props: carouselProps,
@@ -331,11 +333,7 @@ export const Carousel = defineComponent({
331333
/**
332334
* Carousel Event listeners
333335
*/
334-
let isTouch = false
335-
const startPosition = { x: 0, y: 0 }
336-
const dragged = reactive({ x: 0, y: 0 })
337336
const isHover = ref(false)
338-
const isDragging = ref(false)
339337

340338
const handleMouseEnter = (): void => {
341339
isHover.value = true
@@ -376,93 +374,6 @@ export const Carousel = defineComponent({
376374
document.removeEventListener('keydown', handleArrowKeys)
377375
}
378376

379-
function handleDragStart(event: MouseEvent | TouchEvent): void {
380-
// Prevent drag initiation on input elements or if already sliding
381-
const targetTagName = (event.target as HTMLElement).tagName
382-
if (['INPUT', 'TEXTAREA', 'SELECT'].includes(targetTagName) || isSliding.value) {
383-
return
384-
}
385-
386-
// Detect if the event is a touchstart or mousedown event
387-
isTouch = event.type === 'touchstart'
388-
389-
// For mouse events, prevent default to avoid text selection
390-
if (!isTouch) {
391-
event.preventDefault()
392-
if ((event as MouseEvent).button !== 0) {
393-
// Ignore non-left-click mouse events
394-
return
395-
}
396-
}
397-
398-
// Initialize start positions for the drag
399-
startPosition.x = 'touches' in event ? event.touches[0].clientX : event.clientX
400-
startPosition.y = 'touches' in event ? event.touches[0].clientY : event.clientY
401-
402-
// Attach event listeners for dragging and drag end
403-
404-
const moveEvent = isTouch ? 'touchmove' : 'mousemove'
405-
const endEvent = isTouch ? 'touchend' : 'mouseup'
406-
document.addEventListener(moveEvent, handleDragging, { passive: false })
407-
document.addEventListener(endEvent, handleDragEnd, { passive: true })
408-
}
409-
410-
const handleDragging = throttle((event: TouchEvent | MouseEvent): void => {
411-
isDragging.value = true
412-
413-
// Get the current position based on the interaction type (touch or mouse)
414-
const currentX = 'touches' in event ? event.touches[0].clientX : event.clientX
415-
const currentY = 'touches' in event ? event.touches[0].clientY : event.clientY
416-
417-
// Calculate deltas for X and Y axes
418-
dragged.x = currentX - startPosition.x
419-
dragged.y = currentY - startPosition.y
420-
421-
const draggedSlides = getDraggedSlidesCount({
422-
isVertical: isVertical.value,
423-
isReversed: isReversed.value,
424-
dragged,
425-
effectiveSlideSize: effectiveSlideSize.value,
426-
threshold: config.dragThreshold,
427-
})
428-
429-
activeSlideIndex.value = config.wrapAround
430-
? currentSlideIndex.value + draggedSlides
431-
: getNumberInRange({
432-
val: currentSlideIndex.value + draggedSlides,
433-
max: maxSlideIndex.value,
434-
min: minSlideIndex.value,
435-
})
436-
437-
// Emit a drag event for further customization if needed
438-
emit('drag', { deltaX: dragged.x, deltaY: dragged.y })
439-
})
440-
441-
function handleDragEnd(): void {
442-
handleDragging.cancel()
443-
444-
// Prevent accidental clicks when there is a slide drag
445-
if (activeSlideIndex.value !== currentSlideIndex.value && !isTouch) {
446-
const preventClick = (e: MouseEvent) => {
447-
e.preventDefault()
448-
window.removeEventListener('click', preventClick)
449-
}
450-
window.addEventListener('click', preventClick)
451-
}
452-
453-
slideTo(activeSlideIndex.value)
454-
455-
// Reset drag state
456-
dragged.x = 0
457-
dragged.y = 0
458-
isDragging.value = false
459-
460-
const moveEvent = isTouch ? 'touchmove' : 'mousemove'
461-
const endEvent = isTouch ? 'touchend' : 'mouseup'
462-
document.removeEventListener(moveEvent, handleDragging)
463-
document.removeEventListener(endEvent, handleDragEnd)
464-
}
465-
466377
/**
467378
* Autoplay
468379
*/
@@ -497,6 +408,33 @@ export const Carousel = defineComponent({
497408
*/
498409
const isSliding = ref(false)
499410

411+
const onDrag = ({ deltaX, deltaY }: { deltaX: number; deltaY: number }) => {
412+
const draggedSlides = getDraggedSlidesCount({
413+
isVertical: isVertical.value,
414+
isReversed: isReversed.value,
415+
dragged: { x: deltaX, y: deltaY },
416+
effectiveSlideSize: effectiveSlideSize.value,
417+
})
418+
419+
activeSlideIndex.value = config.wrapAround
420+
? currentSlideIndex.value + draggedSlides
421+
: getNumberInRange({
422+
val: currentSlideIndex.value + draggedSlides,
423+
max: maxSlideIndex.value,
424+
min: minSlideIndex.value,
425+
})
426+
427+
emit('drag', { deltaX, deltaY })
428+
}
429+
const onDragEnd = () => {
430+
slideTo(activeSlideIndex.value)
431+
}
432+
const { dragged, isDragging, handleDragStart } = useDragging({
433+
isSliding: isSliding.value,
434+
onDrag,
435+
onDragEnd,
436+
})
437+
500438
function slideTo(slideIndex: number, skipTransition = false): void {
501439
if (!skipTransition && isSliding.value) {
502440
return

src/composables/useDragging.ts

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { ref, reactive } from 'vue'
2+
3+
import { throttle } from '@/utils'
4+
5+
export interface UseDraggingOptions {
6+
onDrag?: (data: { deltaX: number; deltaY: number }) => void
7+
onDragStart?: () => void
8+
onDragEnd?: () => void
9+
isSliding?: boolean
10+
}
11+
12+
export function useDragging(options: UseDraggingOptions = {}) {
13+
let isTouch = false
14+
const startPosition = { x: 0, y: 0 }
15+
const dragged = reactive({ x: 0, y: 0 })
16+
const isDragging = ref(false)
17+
18+
const handleDragStart = (event: MouseEvent | TouchEvent): void => {
19+
// Prevent drag initiation on input elements or if already sliding
20+
const targetTagName = (event.target as HTMLElement).tagName
21+
if (['INPUT', 'TEXTAREA', 'SELECT'].includes(targetTagName) || options.isSliding) {
22+
return
23+
}
24+
25+
isTouch = event.type === 'touchstart'
26+
27+
if (!isTouch) {
28+
event.preventDefault()
29+
if ((event as MouseEvent).button !== 0) {
30+
return
31+
}
32+
}
33+
34+
startPosition.x = isTouch
35+
? (event as TouchEvent).touches[0].clientX
36+
: (event as MouseEvent).clientX
37+
startPosition.y = isTouch
38+
? (event as TouchEvent).touches[0].clientY
39+
: (event as MouseEvent).clientY
40+
41+
const moveEvent = isTouch ? 'touchmove' : 'mousemove'
42+
const endEvent = isTouch ? 'touchend' : 'mouseup'
43+
document.addEventListener(moveEvent, handleDragging, { passive: false })
44+
document.addEventListener(endEvent, handleDragEnd, { passive: true })
45+
46+
options.onDragStart?.()
47+
}
48+
49+
const handleDragging = throttle((event: TouchEvent | MouseEvent): void => {
50+
isDragging.value = true
51+
52+
const currentX = isTouch
53+
? (event as TouchEvent).touches[0].clientX
54+
: (event as MouseEvent).clientX
55+
const currentY = isTouch
56+
? (event as TouchEvent).touches[0].clientY
57+
: (event as MouseEvent).clientY
58+
59+
dragged.x = currentX - startPosition.x
60+
dragged.y = currentY - startPosition.y
61+
62+
options.onDrag?.({ deltaX: dragged.x, deltaY: dragged.y })
63+
})
64+
65+
const handleDragEnd = (): void => {
66+
handleDragging.cancel()
67+
68+
if (!isTouch) {
69+
const preventClick = (e: MouseEvent) => {
70+
e.preventDefault()
71+
window.removeEventListener('click', preventClick)
72+
}
73+
window.addEventListener('click', preventClick)
74+
}
75+
76+
options.onDragEnd?.()
77+
78+
dragged.x = 0
79+
dragged.y = 0
80+
isDragging.value = false
81+
82+
const moveEvent = isTouch ? 'touchmove' : 'mousemove'
83+
const endEvent = isTouch ? 'touchend' : 'mouseup'
84+
document.removeEventListener(moveEvent, handleDragging)
85+
document.removeEventListener(endEvent, handleDragEnd)
86+
}
87+
88+
return {
89+
dragged,
90+
isDragging,
91+
handleDragStart,
92+
}
93+
}

0 commit comments

Comments
 (0)