Skip to content

Commit c6f16cf

Browse files
committed
refactor: move dragging functionality into composable function
1 parent f02122c commit c6f16cf

File tree

2 files changed

+121
-90
lines changed

2 files changed

+121
-90
lines changed

src/components/Carousel/Carousel.ts

Lines changed: 28 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
} from 'vue'
1717

1818
import { ARIA as ARIAComponent } from '@/components/ARIA'
19+
import { useDragging } from '@/composables'
1920
import {
2021
CarouselConfig,
2122
createSlideRegistry,
@@ -330,11 +331,7 @@ export const Carousel = defineComponent({
330331
/**
331332
* Carousel Event listeners
332333
*/
333-
let isTouch = false
334-
const startPosition = { x: 0, y: 0 }
335-
const dragged = reactive({ x: 0, y: 0 })
336334
const isHover = ref(false)
337-
const isDragging = ref(false)
338335

339336
const handleMouseEnter = (): void => {
340337
isHover.value = true
@@ -375,92 +372,6 @@ export const Carousel = defineComponent({
375372
document.removeEventListener('keydown', handleArrowKeys)
376373
}
377374

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

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