Skip to content

Commit f76cd7f

Browse files
authored
Merge pull request #489 from tbranger/feat/threshold-option
feat: add threshold option for slide transition customization
2 parents b64f27a + 4c38717 commit f76cd7f

File tree

8 files changed

+114
-2
lines changed

8 files changed

+114
-2
lines changed

docs/config.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
| `pauseAutoplayOnHover` | `boolean` | false | When true, autoplay pauses while the mouse cursor is over the carousel. |
2222
| `preventExcessiveDragging` | `boolean` | false | Limits dragging behavior at carousel boundaries for better UX. <Badge text="0.13.0" /> |
2323
| `snapAlign` | 'start', 'end', 'center-odd', 'center-even' | 'center' | Determines how slides are aligned within the viewport. |
24+
| `threshold` | `number` | 0.5 | Define a threshold for the drag distance required to trigger a slide transition. |
2425
| `touchDrag` | `boolean` | true | Enables/disables touch navigation on touch-enabled devices. |
2526
| `transition` | `number` | 300 | Duration of the slide transition animation in milliseconds. |
2627
| `wrapAround` | `boolean` | false | When true, creates an infinite loop effect by connecting the last slide to the first. |

playground/App.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ const defaultConfig = {
4646
gap: 10,
4747
pauseAutoplayOnHover: true,
4848
useBreakpoints: false,
49+
threshold: 0.5,
4950
}
5051
5152
const config = reactive({ ...defaultConfig })

src/components/Carousel/Carousel.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,7 @@ export const Carousel = defineComponent({
422422
isReversed: isReversed.value,
423423
dragged,
424424
effectiveSlideSize: effectiveSlideSize.value,
425+
threshold: config.threshold,
425426
})
426427

427428
activeSlideIndex.value = config.wrapAround

src/components/Carousel/carouselProps.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,11 @@ export const carouselProps = {
117117
return SLIDE_EFFECTS.includes(value)
118118
},
119119
},
120+
// control the threshold to trigger slide change
121+
threshold: {
122+
default: DEFAULT_CONFIG.threshold,
123+
type: Number
124+
},
120125
// sliding transition time in ms
121126
transition: {
122127
default: DEFAULT_CONFIG.transition,

src/shared/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export const DEFAULT_CONFIG: CarouselConfig = {
6363
preventExcessiveDragging: false,
6464
slideEffect: SLIDE_EFFECTS[0],
6565
snapAlign: SNAP_ALIGN_OPTIONS[0],
66+
threshold: 0.5,
6667
touchDrag: true,
6768
transition: 300,
6869
wrapAround: false,

src/shared/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export type CarouselConfig = {
4646
preventExcessiveDragging: boolean
4747
slideEffect: SlideEffect
4848
snapAlign: SnapAlign
49+
threshold: number
4950
touchDrag?: boolean
5051
transition?: number
5152
wrapAround?: boolean

src/utils/getDraggedSlidesCount.spec.ts

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ describe('getDraggedSlidesCount', () => {
99
isReversed: false,
1010
dragged: { x: 150, y: 0 },
1111
effectiveSlideSize: 100,
12+
threshold: 0.5,
1213
}
1314
expect(getDraggedSlidesCount(params)).toBe(-2)
1415
})
@@ -19,6 +20,7 @@ describe('getDraggedSlidesCount', () => {
1920
isReversed: true,
2021
dragged: { x: 150, y: 0 },
2122
effectiveSlideSize: 100,
23+
threshold: 0.5,
2224
}
2325
expect(getDraggedSlidesCount(params)).toBe(2)
2426
})
@@ -29,6 +31,7 @@ describe('getDraggedSlidesCount', () => {
2931
isReversed: true,
3032
dragged: { x: 0, y: 150 },
3133
effectiveSlideSize: 100,
34+
threshold: 0.5,
3235
}
3336
expect(getDraggedSlidesCount(params)).toBe(2)
3437
})
@@ -39,16 +42,106 @@ describe('getDraggedSlidesCount', () => {
3942
isReversed: false,
4043
dragged: { x: 0, y: 150 },
4144
effectiveSlideSize: 100,
45+
threshold: 0.5,
4246
}
4347
expect(getDraggedSlidesCount(params)).toBe(-2)
4448
})
4549

50+
it('should handle drag equal to the threshold', () => {
51+
const params = {
52+
isVertical: false,
53+
isReversed: false,
54+
dragged: { x: 50, y: 0 },
55+
effectiveSlideSize: 100,
56+
threshold: 0.5,
57+
};
58+
expect(getDraggedSlidesCount(params)).toBe(-1);
59+
});
60+
61+
it('should handle reversed drag equal to the threshold', () => {
62+
const params = {
63+
isVertical: false,
64+
isReversed: true,
65+
dragged: { x: 50, y: 0 },
66+
effectiveSlideSize: 100,
67+
threshold: 0.5,
68+
};
69+
expect(getDraggedSlidesCount(params)).toBe(1);
70+
});
71+
72+
it('should handle vertical drag equal to the threshold', () => {
73+
const params = {
74+
isVertical: true,
75+
isReversed: false,
76+
dragged: { x: 0, y: 50 },
77+
effectiveSlideSize: 100,
78+
threshold: 0.5,
79+
};
80+
expect(getDraggedSlidesCount(params)).toBe(-1);
81+
});
82+
83+
it('should handle reversed vertical drag equal to the threshold', () => {
84+
const params = {
85+
isVertical: true,
86+
isReversed: true,
87+
dragged: { x: 0, y: 50 },
88+
effectiveSlideSize: 100,
89+
threshold: 0.5,
90+
};
91+
expect(getDraggedSlidesCount(params)).toBe(1);
92+
});
93+
94+
it('should handle drag less than the threshold', () => {
95+
const params = {
96+
isVertical: false,
97+
isReversed: false,
98+
dragged: { x: 49, y: 0 },
99+
effectiveSlideSize: 100,
100+
threshold: 0.5,
101+
};
102+
expect(getDraggedSlidesCount(params)).toBe(0);
103+
});
104+
105+
it('should handle reversed drag less than the threshold', () => {
106+
const params = {
107+
isVertical: false,
108+
isReversed: true,
109+
dragged: { x: 49, y: 0 },
110+
effectiveSlideSize: 100,
111+
threshold: 0.5,
112+
};
113+
expect(getDraggedSlidesCount(params)).toBe(0);
114+
});
115+
116+
it('should handle vertical drag less than the threshold', () => {
117+
const params = {
118+
isVertical: true,
119+
isReversed: false,
120+
dragged: { x: 0, y: 49 },
121+
effectiveSlideSize: 100,
122+
threshold: 0.5,
123+
};
124+
expect(getDraggedSlidesCount(params)).toBe(0);
125+
});
126+
127+
it('should handle reversed vertical drag less than the threshold', () => {
128+
const params = {
129+
isVertical: true,
130+
isReversed: true,
131+
dragged: { x: 0, y: 49 },
132+
effectiveSlideSize: 100,
133+
threshold: 0.5,
134+
};
135+
expect(getDraggedSlidesCount(params)).toBe(0);
136+
});
137+
46138
it('should handle zero drag', () => {
47139
const params = {
48140
isVertical: false,
49141
isReversed: false,
50142
dragged: { x: 0, y: 0 },
51143
effectiveSlideSize: 100,
144+
threshold: 0.5,
52145
}
53146
expect(getDraggedSlidesCount(params)).toBe(0)
54147
})

src/utils/getDraggedSlidesCount.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ type DragParams = {
33
isReversed: boolean
44
dragged: { x: number; y: number }
55
effectiveSlideSize: number
6+
threshold: number
67
}
78

89
/**
@@ -11,15 +12,23 @@ type DragParams = {
1112
* @returns Number of slides to move (positive or negative)
1213
*/
1314
export function getDraggedSlidesCount(params: DragParams): number {
14-
const { isVertical, isReversed, dragged, effectiveSlideSize } = params
15+
const { isVertical, isReversed, dragged, effectiveSlideSize, threshold } = params
1516

1617
// Get drag value based on direction
1718
const dragValue = isVertical ? dragged.y : dragged.x
1819

1920
// If no drag, return +0 explicitly
2021
if (dragValue === 0) return 0
2122

22-
const slidesDragged = Math.round(dragValue / effectiveSlideSize)
23+
const dragRatio = dragValue / effectiveSlideSize
24+
const absRatio = Math.abs(dragRatio)
25+
26+
// If below the threshold, consider it no movement
27+
if (absRatio < threshold) return 0
28+
29+
// For drags less than a full slide, move one slide in the drag direction
30+
// For drags of a full slide or more, move the corresponding number of slides
31+
const slidesDragged = absRatio < 1 ? Math.sign(dragRatio) : Math.round(dragRatio)
2332

2433
return isReversed ? slidesDragged : -slidesDragged
2534
}

0 commit comments

Comments
 (0)