How to make a simple image gallery in VUE3

Home Code How to make a simple image gallery in VUE3

One of the first exercises I do when learning a new frontend framework, is making a simple image gallery. To my mind it's a good litmus test for any Javascript framework as nine out of ten websites I make at the office has some way to browse through a set of images. Here's how I made the one this website uses. 

Write the component

The component code is rather simple. The gallery takes a single prop in the form of an Array of images. The reason I wanted to inject them via a prop is simple: that way I can prepare any image array in any ProcessWire template file, without having to make a seperate call or write some JSON endpoint for image data. It also comes in handy when using Repeater fields, such as the tutorial template you're looking at right now uses.

                            app.component('image-gallery',
{
    name:"Image Gallery",
    props:{
        images:{type:Array, default:[]}
    },
    data(){
        return{
            index:0,
            maxindex:0
        }
    },
    methods:{
        prev:function()
        {
            if (this.index > 0) {
                this.index--;
            } else {
                this.index = this.maxindex;
            }
        },
        next:function()
        {
            if (this.index < this.maxindex) {
                this.index++;
            } else {
                this.index = 0;
            }
        },
        setIndex:function(ind)
        {
            this.index = ind;
        }
    },
    mounted:function()
    {
        this.index = 0;
        this.maxindex = this.images.length -1;
    },
    template:
    `
    <div class="imagegallery">
        <div class="items">                             
            <div 
                class="item" 
                v-for="(image, index) of images" 
                v-bind:style="{ backgroundImage: 'url(' + image.url + ')' }" 
                :class="index==this.index ? 'active' : ''">
            </div>                                
        </div>
        <div class="buttons" v-if="maxindex!=0">
            <a class="button button-prev" v-on:click="prev()"><i class="fa fa-angle-left"></i></a>
            <a class="button button-next" v-on:click="next()"><i class="fa fa-angle-right"></i></a>
        </div>
        <div class="dots" v-if="maxindex!=0">
            <a 
                class="dot" 
                v-for="(image, index) of images" 
                v-on:click="setIndex(index)" 
                :class="index==this.index ? 'active' : ''"><span class='inner'></span>
            </a>
        </div>
    </div>
    `
});                        

Implement the component

Implementing this gallery is easy, as you only have a single property to set. In this example I'm rendering the gallery only when there's a field on the page named image_gallery, and it has at least one image. As you can see the array of images consists of objects each having two properties: url and description. At this time the description is not actually used in the component code, but including it in the data array allows us to work with these descriptions later.

                            <?php if ($page->image_gallery && $step->image_gallery->count > 0) : ?>
<?php $images = ""; foreach($page->image_gallery as $image) { $images .= "{url:'" . $image->url . "', alt:'" . $image->description . "'},"; } ?>
<image-gallery :images="[<?php echo $images; ?>]"></image-gallery> 
<?php endif; ?>                        

Add CSS to style the component

Here's all the CSS I used to style the image gallery. This is more or less intended as a basis to work from, as each implementation might have it's own styling.

                            .imagegallery {
    position:relative;
    float:left;
    width:100%;
}
.imagegallery .items {
    position:relative;
    float:left;
    width:100%;
    padding-top:50%;
    background-color:#000000;
}
.imagegallery .items .item {
    position:absolute;
    top:0px;
    left:0px;
    width:100%;
    height:100%;
    background-repeat:no-repeat;
    background-position:center center;
    background-size:cover;
    opacity:0;
    transition:all linear 0.4s;
}
.imagegallery .items .item.active {
    opacity:1;
    transition:all linear 0.4s 0.4s;
}
.imagegallery .buttons {
    position:absolute;
    top:0px;
    left:0px;
    width:100%;
    height:100%;
    display:flex;
    align-items:center;
}
.imagegallery .buttons .button {
    position:absolute;
    height:100px;
    width:40px;
    cursor:pointer;
    background-color:rgba(255,255,255,0.5);
    display:flex;
    justify-content:center;
    align-items:center;
    transition:all linear 0.2s;
}
.imagegallery .buttons .button i {
    font-size:1.4em;
}
.imagegallery .buttons .button-prev {
    left:0px;
}
.imagegallery .buttons .button-next {
    right:0px;
}
.imagegallery .buttons .button:hover {
    background-color:rgba(255,255,255,1.0);
}
.imagegallery .dots {
    position:absolute;
    bottom:10px;
    left:0px;
    width:100%;
    display:flex;
    justify-content:center;
}
.imagegallery .dots .dot {
    position:relative;
    width:10px;
    height:10px;
    border-radius:10px;
    border:1px solid #ffffff;
    background-color:rgba(255,255,255,0);
    margin:0px 2.5px 0px 2.5px;
    cursor:pointer;
}
.imagegallery .dots .dot.active {
    background-color:rgba(255,255,255,0.5);
}
.imagegallery .dots .dot .inner {
    position:absolute;
    top:0%;
    left:0%;
    width:100%;
    height:100%;
    border-radius:15px;
    transform:scale(1, 1);
    background-color:rgba(255,255,255,0);
    transition:all linear 0.2s;
}
.imagegallery .dots .dot.active .inner {
    transform:scale(0.5, 0.5);
    background-color:rgba(255,255,255,1);
}                        

Look at the pretty pictures

And since this template uses the above Image Gallery, why not render it for good measure? Show, don't tell, after all.

Tech Stack

PHP

HTML

Javascript

C#

CSS