In my few years of experience as a web developer I have used more than a dozen carousel plugins, some of them were really good and some of them were rather clunky.
Recently I wanted to use a small image carousel(for a personal project of mine) that would cover the upper part of a card. The carousel should have arrows and thumbnails to navigate through the images and the thumbnails should be on the bottom of the carousel.
I was tempted to use something like owl-carousel and get on with my day, but I also kind of wanted to build something new with Vue, which I have been learning for a few months now.
Since I had plenty of time I fired up my favorite IDE and started a new project.
For this project I used Bulma (a CSS framework based on Flexbox) and of course Vue.js. If you are new to Vue I encourage you to read my first post and the awesome documentation.
Sneak peek
Let’s get technical
HTML skeleton of the project
<section class="section" id="app">
<div class="columns">
<div class="column is-4">
<div class="card">
<div class="card-content">
<div class="card-carousel">
<div class="card-img">
<img src="/some-image.jpg" alt="Some image">
<div class="actions">
<span class="prev">
<i class="fas fa-chevron-left"></i>
</span>
<span class="next">
<i class="fas fa-chevron-right"></i>
</span>
</div>
</div>
<div class="thumbnails">
<div class="thumbnail-img">
<img src="/some-thumbnail.jpg" alt="Some thumbnail">
</div>
<div class="thumbnail-img active">
<img src="/some-thumbnail.jpg" alt="Some thumbnail">
</div>
<div class="thumbnail-img">
<img src="/some-thumbnail.jpg" alt="Some thumbnail">
</div>
</div>
</div>
<p>Card description.</p>
</div>
</div>
</div>
</div>
</section>
After a little CSS it ended up looking like this:
Now that we got the styling out of the way, we can focus on the functionality 🤖
<div class="card-carousel">
<div class="card-img">
<img :src="currentImage" alt="">
<div class="actions">
<span @click="prevImage" class="prev">
<i class="fas fa-chevron-left"></i>
</span>
<span @click="nextImage" class="next">
<i class="fas fa-chevron-right"></i>
</span>
</div>
</div>
<div class="thumbnails">
<div
v-for="(image, index) in images"
:key="image.id"
:class="['thumbnail-image', (activeImage == index) ? 'active' : '']"
@click="activateImage(index)"
>
<img :src="image.thumb">
</div>
</div>
</div>
A short explanation
<!--
Bind the source of the image to a variable,
so when the variable changes, so does the photo
-->
<img :src="currentImage" alt="">
<!--
Loop through the images array,
@click="activateImage(index)" on click call the function activateImage
:class="['thumbnail-image', (activeImage == index) ? 'active' : '']"
bind the class of the div to an array. Always show 'thumbnail-image' class
and show the class 'active' only for the image that is currently active
-->
<div
v-for="(image, index) in images"
:key="image.id"
:class="['thumbnail-image', (activeImage == index) ? 'active' : '']"
@click="activateImage(index)"
>
<img :src="image.thumb">
</div>
and all the JS code that was needed:
var app = new Vue({
el: '#app',
data() {
return {
//Array to hold all carousel images
images: [
{
id: '1',
big: 'images/p1.jpeg',
thumb: 'images/thumbs/p1.jpeg'
},
{
id: '2',
big: 'images/p2.jpeg',
thumb: 'images/thumbs/p2.jpeg'
},
{
id: '3',
big: 'images/p3.jpeg',
thumb: 'images/thumbs/p3.jpeg'
},
{
id: '4',
big: 'images/p4.jpeg',
thumb: 'images/thumbs/p4.jpeg'
}
],
//Index of the active image on the images array
activeImage: 0
}
},
computed: {
// currentImage gets called whenever activeImage changes
// and is the reason why we don't have to worry about the
// big image getting updated
currentImage() {
return this.images[this.activeImage].big;
}
},
methods: {
// Go forward on the images array
// or go at the first image if you can't go forward :/
nextImage() {
var active = this.activeImage + 1;
if(active >= this.images.length) {
active = 0;
}
this.activateImage(active);
},
// Go backwards on the images array
// or go at the last image
prevImage() {
var active = this.activeImage - 1;
if(active < 0) {
active = this.images.length - 1;
}
this.activateImage(active);
},
activateImage(imageIndex) {
this.activeImage = imageIndex;
}
}
});
Here is the finished project (again 😅)
🎉Thank you for reading through all this and I hope you found something helpful🎉
You can find all the code on github
All the photos were taken from Pexels.com
https://www.pexels.com/photo/bang-blast-celebration-color-287487/
https://www.pexels.com/photo/person-hands-squash-fruit-112352/
https://www.pexels.com/photo/action-blur-car-daylight-246320/
https://www.pexels.com/photo/auto-automobile-blur-bokeh-242276/
Top comments (9)
This is really cool. Great Job.
Thank you. Let me know if you are interested in some more advanced Vue posts.
Yeah sure. I will really love to see those.
Well done!
Thanks a lot!
Thank you for sharing. Please make mo tuts in this style. Simple and without CLI
Thank you for your comment. I have some tutorials in my mind, will write them when I have some free time.
awesome, thanks 🌸
Well done! This is so much helpful! Thank you!