loading...
Cover image for Building an image carousel with Vue.js

Building an image carousel with Vue.js

tsanak profile image tsanak ・3 min read

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

alt text

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:
alt text

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 😅)
alt text

🎉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/

Posted on by:

tsanak profile

tsanak

@tsanak

Full stack developer, with much love for the front end.

Discussion

markdown guide
 
 

Thank you. Let me know if you are interested in some more advanced Vue posts.

 

Yeah sure. I will really love to see those.

 
 

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.

 
 

Well done! This is so much helpful! Thank you!