Skip to content

Commit 7dd304e

Browse files
committed
feat: add wheel event support to Carousel and enhance drag functionality with new useDrag composable
1 parent 8633407 commit 7dd304e

File tree

5 files changed

+56
-54
lines changed

5 files changed

+56
-54
lines changed

playground/App.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ const events = [
211211
'slide-end',
212212
'loop',
213213
'drag',
214+
'wheel',
214215
'slide-registered',
215216
'slide-unregistered',
216217
]

src/components/Carousel/Carousel.ts

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
} from 'vue'
1818

1919
import { ARIA as ARIAComponent } from '@/components/ARIA'
20-
import { useDragging, useHover, useWheel } from '@/composables'
20+
import { DragEventData, useDrag, useHover, useWheel, WheelEventData } from '@/composables'
2121
import {
2222
CarouselConfig,
2323
createSlideRegistry,
@@ -64,6 +64,7 @@ export const Carousel = defineComponent({
6464
'slide-start',
6565
'slide-unregistered',
6666
'update:modelValue',
67+
'wheel',
6768
],
6869
setup(props: CarouselConfig, { slots, emit, expose }: SetupContext) {
6970
const slideRegistry = createSlideRegistry(emit)
@@ -401,13 +402,9 @@ export const Carousel = defineComponent({
401402
*/
402403
const isSliding = ref(false)
403404

404-
const onDrag = ({
405-
delta,
406-
isTouch,
407-
}: {
408-
delta: { x: number; y: number }
409-
isTouch: boolean
410-
}) => {
405+
const onDrag = ({ deltaX, deltaY, isTouch }: DragEventData) => {
406+
emit('drag', { deltaX, deltaY })
407+
411408
const threshold = isTouch
412409
? typeof config.touchDrag === 'object'
413410
? (config.touchDrag?.threshold ?? DEFAULT_DRAG_THRESHOLD)
@@ -419,7 +416,7 @@ export const Carousel = defineComponent({
419416
const draggedSlides = getDraggedSlidesCount({
420417
isVertical: isVertical.value,
421418
isReversed: isReversed.value,
422-
dragged: delta,
419+
dragged: { x: deltaX, y: deltaY },
423420
effectiveSlideSize: effectiveSlideSize.value,
424421
threshold,
425422
})
@@ -431,25 +428,41 @@ export const Carousel = defineComponent({
431428
max: maxSlideIndex.value,
432429
min: minSlideIndex.value,
433430
})
434-
435-
emit('drag', delta)
436431
}
437432

438433
const onDragEnd = () => slideTo(activeSlideIndex.value)
439434

440-
const { dragged, isDragging, handleDragStart } = useDragging({
435+
const { dragged, isDragging, handleDragStart } = useDrag({
441436
isSliding,
442437
onDrag,
443438
onDragEnd,
444439
})
445440

441+
const onWheel = ({ deltaX, deltaY, isScrollingForward }: WheelEventData) => {
442+
emit('wheel', { deltaX, deltaY })
443+
444+
if (isScrollingForward) {
445+
// Scrolling down/right
446+
if (isReversed.value) {
447+
prev()
448+
} else {
449+
next()
450+
}
451+
} else {
452+
// Scrolling up/left
453+
if (isReversed.value) {
454+
next()
455+
} else {
456+
prev()
457+
}
458+
}
459+
}
460+
446461
const { handleScroll } = useWheel({
447462
isVertical,
448-
isReversed,
449463
isSliding,
450464
config,
451-
next,
452-
prev,
465+
onWheel,
453466
})
454467

455468
function slideTo(slideIndex: number, skipTransition = false): void {

src/composables/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
export * from './useDragging'
1+
export * from './useDrag'
22
export * from './useHover'
33
export * from './useWheel'

src/composables/useDragging.ts renamed to src/composables/useDrag.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,19 @@ import { ref, reactive, computed, Ref } from 'vue'
22

33
import { throttle } from '@/utils'
44

5-
export interface UseDraggingOptions {
5+
export type DragEventData = {
6+
deltaX: number
7+
deltaY: number
8+
isTouch: boolean
9+
}
10+
export interface UseDragOptions {
611
isSliding: boolean | Ref<boolean>
7-
onDrag?: (data: { delta: { x: number; y: number }; isTouch: boolean }) => void
12+
onDrag?: ({ deltaX, deltaY, isTouch }: DragEventData) => void
813
onDragStart?: () => void
914
onDragEnd?: () => void
1015
}
1116

12-
export function useDragging(options: UseDraggingOptions) {
17+
export function useDrag(options: UseDragOptions) {
1318
let isTouch = false
1419
const startPosition = { x: 0, y: 0 }
1520
const dragged = reactive({ x: 0, y: 0 })
@@ -46,13 +51,13 @@ export function useDragging(options: UseDraggingOptions) {
4651

4752
const moveEvent = isTouch ? 'touchmove' : 'mousemove'
4853
const endEvent = isTouch ? 'touchend' : 'mouseup'
49-
document.addEventListener(moveEvent, handleDragging, { passive: false })
54+
document.addEventListener(moveEvent, handleDrag, { passive: false })
5055
document.addEventListener(endEvent, handleDragEnd, { passive: true })
5156

5257
options.onDragStart?.()
5358
}
5459

55-
const handleDragging = throttle((event: TouchEvent | MouseEvent): void => {
60+
const handleDrag = throttle((event: TouchEvent | MouseEvent): void => {
5661
isDragging.value = true
5762

5863
const currentX = isTouch
@@ -65,11 +70,11 @@ export function useDragging(options: UseDraggingOptions) {
6570
dragged.x = currentX - startPosition.x
6671
dragged.y = currentY - startPosition.y
6772

68-
options.onDrag?.({ delta: { x: dragged.x, y: dragged.y }, isTouch })
73+
options.onDrag?.({ deltaX: dragged.x, deltaY: dragged.y, isTouch })
6974
})
7075

7176
const handleDragEnd = (): void => {
72-
handleDragging.cancel()
77+
handleDrag.cancel()
7378

7479
if (!isTouch) {
7580
const preventClick = (e: MouseEvent) => {
@@ -87,7 +92,7 @@ export function useDragging(options: UseDraggingOptions) {
8792

8893
const moveEvent = isTouch ? 'touchmove' : 'mousemove'
8994
const endEvent = isTouch ? 'touchend' : 'mouseup'
90-
document.removeEventListener(moveEvent, handleDragging)
95+
document.removeEventListener(moveEvent, handleDrag)
9196
document.removeEventListener(endEvent, handleDragEnd)
9297
}
9398

src/composables/useWheel.ts

Lines changed: 13 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -3,49 +3,47 @@ import { ComputedRef, Ref, computed } from 'vue'
33
import { CarouselConfig } from '@/shared'
44
import { DEFAULT_MOUSE_WHEEL_THRESHOLD } from '@/shared/constants'
55

6-
interface UseWheelOptions {
6+
export type WheelEventData = {
7+
deltaX: number
8+
deltaY: number
9+
isScrollingForward: boolean
10+
}
11+
12+
export type UseWheelOptions = {
713
isVertical: boolean | ComputedRef<boolean>
8-
isReversed: boolean | ComputedRef<boolean>
914
isSliding: boolean | Ref<boolean>
1015
config: CarouselConfig
11-
next: (skipTransition?: boolean) => void
12-
prev: (skipTransition?: boolean) => void
16+
onWheel?: (data: WheelEventData) => void
1317
}
1418

1519
export function useWheel(options: UseWheelOptions) {
16-
const { isVertical, isReversed, isSliding, config, next, prev } = options
20+
const { isVertical, isSliding, config } = options
1721

1822
// Create computed values to handle both reactive and non-reactive inputs
1923
const vertical = computed(() => {
2024
return typeof isVertical === 'boolean' ? isVertical : isVertical.value
2125
})
2226

23-
const reversed = computed(() => {
24-
return typeof isReversed === 'boolean' ? isReversed : isReversed.value
25-
})
26-
2727
const sliding = computed(() => {
2828
return typeof isSliding === 'boolean' ? isSliding : isSliding.value
2929
})
3030

31-
const handleScroll = (event: Event): void => {
31+
const handleScroll = (event: WheelEvent): void => {
3232
event.preventDefault()
3333

3434
if (!config.mouseWheel || sliding.value) {
3535
return
3636
}
3737

38-
const wheelEvent = event as WheelEvent
39-
4038
// Add sensitivity threshold to prevent small movements from triggering navigation
4139
const threshold =
4240
typeof config.mouseWheel === 'object'
4341
? (config.mouseWheel.threshold ?? DEFAULT_MOUSE_WHEEL_THRESHOLD)
4442
: DEFAULT_MOUSE_WHEEL_THRESHOLD
4543

4644
// Determine scroll direction
47-
const deltaY = Math.abs(wheelEvent.deltaY) > threshold ? wheelEvent.deltaY : 0
48-
const deltaX = Math.abs(wheelEvent.deltaX) > threshold ? wheelEvent.deltaX : 0
45+
const deltaY = Math.abs(event.deltaY) > threshold ? event.deltaY : 0
46+
const deltaX = Math.abs(event.deltaX) > threshold ? event.deltaX : 0
4947

5048
// If neither delta exceeds the threshold, don't navigate
5149
if (deltaY === 0 && deltaX === 0) {
@@ -62,22 +60,7 @@ export function useWheel(options: UseWheelOptions) {
6260
// Positive delta means scrolling down/right
6361
const isScrollingForward = effectiveDelta > 0
6462

65-
// Apply navigation based on scroll direction and carousel configuration
66-
if (isScrollingForward) {
67-
// Scrolling down/right
68-
if (reversed.value) {
69-
prev()
70-
} else {
71-
next()
72-
}
73-
} else {
74-
// Scrolling up/left
75-
if (reversed.value) {
76-
next()
77-
} else {
78-
prev()
79-
}
80-
}
63+
options.onWheel?.({ deltaX, deltaY, isScrollingForward })
8164
}
8265

8366
return {

0 commit comments

Comments
 (0)