Skip to content

Commit 6a8cd87

Browse files
committed
feat: add mouse wheel scrolling support to carousel component
1 parent a0cd533 commit 6a8cd87

File tree

11 files changed

+234
-10
lines changed

11 files changed

+234
-10
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Modern lightweight Vue 3 carousel component
1919
- 📱 **Responsive** - Breakpoints support
2020
- 🔄 **Infinite Scroll** - Wrap around sliding
2121
- 🖱️ **Mouse/Touch** - Dragging support
22+
- 🖲️ **Mouse Wheel** - Scroll navigation support
2223
-**Auto Play** - Automatic sliding
2324
- 🎯 **Slide Classes** - Active & visible states
2425
- 🌐 **RTL** - Right-to-left support

docs/config.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
| `breakpoints` | `object` | null | Responsive breakpoint configurations. Each breakpoint can override any carousel prop. |
1010
| `clamp` | `boolean` | false | If true will clamp itemsToShow to the number of available slides |
1111
| `dir` | 'ltr', 'rtl', 'ttb', 'btt' | 'ltr' | Carousel sliding direction. Supports horizontal (ltr/rtl) and vertical (ttb/btt) orientations. |
12-
| `dragThreshold` | `number` | 0.3 | Define a threshold for the drag distance required to trigger a slide transition. |
1312
| `enabled` | `boolean` | true | Controls whether the carousel is interactive. When false, all interactions are disabled. |
1413
| `gap` | `number` | 0 | Space (in pixels) between carousel slides. |
1514
| `height` | `number` \| `string` | 'auto' | Sets the carousel track height. Required for vertical orientation. |
@@ -19,6 +18,9 @@
1918
| `itemsToShow` | `number` \| 'auto' | 1 | Number of slides visible simultaneously. Use 'auto' for variable width slides. |
2019
| `modelValue` | `number` | 0 | Controls the active slide index. Can be used with v-model for two-way binding. |
2120
| `mouseDrag` | `boolean` | true | Enables/disables mouse drag navigation. |
21+
| `mouseDragThreshold` | `number` | 0.3 | Define a threshold for the drag distance required to trigger a slide transition. |
22+
| `mouseScroll` | `boolean` | false | Enables/disables mouse wheel scrolling for carousel navigation. |
23+
| `mouseScrollThreshold` | `number` | 10 | Controls the sensitivity threshold for mouse scrolling. Higher values require more scrolling. |
2224
| `pauseAutoplayOnHover` | `boolean` | false | When true, autoplay pauses while the mouse cursor is over the carousel. |
2325
| `preventExcessiveDragging` | `boolean` | false | Limits dragging behavior at carousel boundaries for better UX. <Badge text="0.13.0" /> |
2426
| `slideEffect` | 'slide', 'fade' | 'slide' | Determines the transition effect between slides. |

docs/examples-fallback.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ Illustrates the carousel with autoplay functionality enabled.
3030

3131
<ExampleAutoplay />
3232

33+
## Mouse Scroll
34+
35+
Demonstrates the carousel with mouse wheel scrolling navigation enabled.
36+
37+
<ExampleMouseScroll />
38+
3339
## Active Classes
3440

3541
An example highlighting active items with custom classes.
@@ -64,6 +70,7 @@ import ExampleCustomNavigation from './examples/ExampleCustomNavigation.vue';
6470
import ExampleGallery from './examples/ExampleGallery.vue';
6571
import ExampleVertical from './examples/ExampleVertical.vue';
6672
import ExampleDisable from './examples/ExampleDisable.vue';
73+
import ExampleMouseScroll from './examples/ExampleMouseScroll.vue';
6774
</script>
6875

6976
<style>

docs/examples.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ Illustrates the carousel with autoplay functionality enabled.
3232

3333
<live-codes v-bind="LiveCodeExamples.autoplay" />
3434

35+
## Mouse Scroll
36+
37+
Demonstrates the carousel with mouse wheel scrolling navigation enabled.
38+
39+
<live-codes v-bind="LiveCodeExamples.mouseScroll" />
40+
3541
## Active Classes
3642

3743
An example highlighting active items with custom classes.

docs/examples/ExampleMouseScroll.vue

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<script setup>
2+
import { Carousel, Slide, Pagination, Navigation } from 'vue3-carousel'
3+
import { ref } from 'vue'
4+
5+
const images = Array.from({ length: 10 }, (_, index) => ({
6+
id: index + 1,
7+
url: `https://picsum.photos/800/600?random=${index + 1}`,
8+
}))
9+
10+
const threshold = ref(30)
11+
12+
const config = {
13+
height: 200,
14+
itemsToShow: 2,
15+
gap: 5,
16+
mouseScroll: true,
17+
mouseScrollThreshold: threshold.value,
18+
wrapAround: true,
19+
}
20+
21+
function updateThreshold(event) {
22+
threshold.value = Number(event.target.value)
23+
config.mouseScrollThreshold = threshold.value
24+
}
25+
</script>
26+
27+
<template>
28+
<div class="example-container">
29+
<h3>Mouse Scroll Navigation</h3>
30+
<p>Scroll your mouse wheel over the carousel to navigate through slides</p>
31+
32+
<div class="threshold-control">
33+
<label for="threshold">Mouse Scroll Threshold: {{ threshold }}</label>
34+
<input
35+
id="threshold"
36+
type="range"
37+
min="10"
38+
max="100"
39+
step="10"
40+
v-model="threshold"
41+
@input="updateThreshold"
42+
/>
43+
<small>Higher values require more scrolling (less sensitive)</small>
44+
</div>
45+
46+
<Carousel v-bind="config">
47+
<Slide v-for="image in images" :key="image.id">
48+
<div class="carousel__item">
49+
<img :src="image.url" alt="image" />
50+
</div>
51+
</Slide>
52+
53+
<template #addons>
54+
<Navigation />
55+
<Pagination />
56+
</template>
57+
</Carousel>
58+
</div>
59+
</template>
60+
61+
<style scoped>
62+
.example-container {
63+
text-align: center;
64+
}
65+
66+
.threshold-control {
67+
margin: 20px 0;
68+
display: flex;
69+
flex-direction: column;
70+
align-items: center;
71+
gap: 8px;
72+
}
73+
74+
input[type='range'] {
75+
width: 200px;
76+
}
77+
78+
small {
79+
color: #888;
80+
font-size: 0.8em;
81+
}
82+
83+
img {
84+
width: 100%;
85+
height: 100%;
86+
object-fit: cover;
87+
border-radius: 8px;
88+
}
89+
</style>

docs/examples/LiveCodeExamples.ts

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -549,6 +549,109 @@ img {
549549
.thumbnail:hover {
550550
opacity: 1;
551551
}
552+
`.trimStart(),
553+
},
554+
mouseScroll: {
555+
code: `
556+
<script setup>
557+
import 'vue3-carousel/carousel.css';
558+
import { Carousel, Slide, Pagination, Navigation } from 'vue3-carousel';
559+
import { ref } from 'vue';
560+
561+
const images = Array.from({ length: 10 }, (_, index) => ({
562+
id: index + 1,
563+
url: \`https://picsum.photos/800/600?random=\${index + 1}\`,
564+
}));
565+
566+
const threshold = ref(30);
567+
568+
const config = {
569+
height: 200,
570+
itemsToShow: 2,
571+
gap: 5,
572+
mouseScroll: true,
573+
mouseScrollThreshold: threshold.value,
574+
wrapAround: true,
575+
};
576+
577+
function updateThreshold(event) {
578+
threshold.value = Number(event.target.value);
579+
config.mouseScrollThreshold = threshold.value;
580+
}
581+
</script>
582+
583+
<template>
584+
<div class="example-container">
585+
<h3>Mouse Scroll Navigation</h3>
586+
<p>Scroll your mouse wheel over the carousel to navigate through slides</p>
587+
588+
<div class="threshold-control">
589+
<label for="threshold">Mouse Scroll Threshold: {{ threshold }}</label>
590+
<input
591+
id="threshold"
592+
type="range"
593+
min="10"
594+
max="100"
595+
step="10"
596+
v-model="threshold"
597+
@input="updateThreshold"
598+
/>
599+
<small>Higher values require more scrolling (less sensitive)</small>
600+
</div>
601+
602+
<Carousel v-bind="config">
603+
<Slide v-for="image in images" :key="image.id">
604+
<img :src="image.url" alt="image" />
605+
</Slide>
606+
607+
<template #addons>
608+
<Navigation />
609+
<Pagination />
610+
</template>
611+
</Carousel>
612+
</div>
613+
</template>
614+
`.trimStart(),
615+
styles: `
616+
:root {
617+
background-color: #242424;
618+
}
619+
620+
.example-container {
621+
color: white;
622+
text-align: center;
623+
}
624+
625+
.threshold-control {
626+
margin: 20px 0;
627+
display: flex;
628+
flex-direction: column;
629+
align-items: center;
630+
gap: 8px;
631+
}
632+
633+
input[type="range"] {
634+
width: 200px;
635+
}
636+
637+
small {
638+
color: #888;
639+
font-size: 0.8em;
640+
}
641+
642+
.carousel {
643+
--vc-pgn-background-color: rgba(255, 255, 255, 0.7);
644+
--vc-pgn-active-color: rgba(255, 255, 255, 1);
645+
--vc-nav-background: rgba(255, 255, 255, 0.7);
646+
--vc-nav-border-radius: 100%;
647+
}
648+
649+
img {
650+
border-radius: 8px;
651+
width: 100%;
652+
height: 100%;
653+
object-fit: cover;
654+
}
552655
`.trimStart(),
553656
},
554657
}

playground/App.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ const defaultConfig = {
4141
autoplay: null,
4242
wrapAround: true,
4343
height: '200',
44+
mouseScroll: true,
4445
dir: 'left-to-right',
4546
breakpointMode: 'carousel',
4647
gap: 10,

src/components/Carousel/Carousel.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -837,6 +837,7 @@ export const Carousel = defineComponent({
837837
style: { transform: trackTransform.value },
838838
onMousedownCapture: config.mouseDrag ? handleDragStart : null,
839839
onTouchstartPassiveCapture: config.touchDrag ? handleDragStart : null,
840+
onWheelPassive: config.mouseScroll ? handleScroll : null,
840841
},
841842
output
842843
)

src/components/Carousel/carouselProps.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export const carouselProps = {
4040
clamp: {
4141
type: Boolean,
4242
},
43-
// control the gap between slides
43+
// control the direction of the carousel
4444
dir: {
4545
type: String as PropType<Dir>,
4646
default: DEFAULT_CONFIG.dir,
@@ -63,11 +63,6 @@ export const carouselProps = {
6363
return true
6464
},
6565
},
66-
// control the threshold to trigger slide change
67-
dragThreshold: {
68-
default: DEFAULT_CONFIG.dragThreshold,
69-
type: Number,
70-
},
7166
// enable/disable the carousel component
7267
enabled: {
7368
default: DEFAULT_CONFIG.enabled,
@@ -78,7 +73,7 @@ export const carouselProps = {
7873
default: DEFAULT_CONFIG.gap,
7974
type: Number,
8075
},
81-
// control the gap between slides
76+
// set carousel height
8277
height: {
8378
default: DEFAULT_CONFIG.height,
8479
type: [Number, String],
@@ -112,6 +107,21 @@ export const carouselProps = {
112107
default: DEFAULT_CONFIG.mouseDrag,
113108
type: Boolean,
114109
},
110+
// control the threshold to trigger slide change with mouse drag
111+
mouseDragThreshold: {
112+
default: DEFAULT_CONFIG.mouseDragThreshold,
113+
type: Number,
114+
},
115+
// toggle mouse wheel scrolling
116+
mouseScroll: {
117+
default: DEFAULT_CONFIG.mouseScroll,
118+
type: Boolean,
119+
},
120+
// control mouse scroll threshold
121+
mouseScrollThreshold: {
122+
default: DEFAULT_CONFIG.mouseScrollThreshold,
123+
type: Number,
124+
},
115125
pauseAutoplayOnHover: {
116126
default: DEFAULT_CONFIG.pauseAutoplayOnHover,
117127
type: Boolean,

src/shared/constants.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ export const DEFAULT_CONFIG: CarouselConfig = {
5050
breakpointMode: BREAKPOINT_MODE_OPTIONS[0],
5151
breakpoints: undefined,
5252
dir: DIR_OPTIONS[0],
53-
dragThreshold: 0.3,
5453
enabled: true,
5554
gap: 0,
5655
height: 'auto',
@@ -60,6 +59,9 @@ export const DEFAULT_CONFIG: CarouselConfig = {
6059
itemsToShow: 1,
6160
modelValue: 0,
6261
mouseDrag: true,
62+
mouseDragThreshold: 0.3,
63+
mouseScroll: false,
64+
mouseScrollThreshold: 10,
6365
pauseAutoplayOnHover: false,
6466
preventExcessiveDragging: false,
6567
slideEffect: SLIDE_EFFECTS[0],

0 commit comments

Comments
 (0)