Categories javascript

Responsive vuetify carousel with multiple items per slide

Post date January 15, 2021

Today I’ll show you how I used vuetify‘s carousel element to get a result requested by a client. The requirements are defined as follows:

  • images are grouped so that on desktop there are 4 visible at once, on tablet 3 visible at once and on mobile 2 visible at once
  • when the user goes to the next slide all previously visible items are swiped away and new images are shown
  • the number of images is not known in advance

Working demo for those, who want to see it first.

Introduction

Vue.js is a javascript framework, that is a base for the vuetify – material design framework. I rarely use frameworks in my personal projects, so when I have to use them in a client’s project it’s a learning process for me and I like to document it for the future reference, just in case I’ll need to do the same thing again. That’s the introduction, now the code.

Template

Template is the part of a vue component, that is responsible for displaying the data. It can use usual HTML tags (like div or img), vue components (when using vuetify these are for example v-carousel or v-carousel-item) and  the magic template tag, that does not have a representation in generated HTML code.

Ready template looks like this:

<v-carousel>
  <template v-for="(item, index) in slider">
    <v-carousel-item 
       v-if="(index + 1) % columns === 1 || columns === 1" 
       :key="index"
    >
       <v-row class="flex-nowrap">
         <template v-for="(n,i) in columns">
           <template v-if="(+index + i) < slider.length">
             <v-col :key="i">
              <!-- the content -->
              {{ +index + i + 1}} 
            </v-col>
          </template>
        </template>
      </v-row>
    </v-carousel-item>
  </template>
</v-carousel>

It looks quite complicated, so let’s break it down.

<v-carousel>

We want to use the v-carousel component as the base. I did not specify any attributes, but there are quite a few. They do not influence the final effect, so feel free to use them for customisation.

<template v-for="(item, index) in slider">

We want to display all elements from slider array. These can be simple strings of text, images, advanced objects, anything. To make the widget work, we do not need the item’s interns, but its index in the slider array. Because we want to execute some more logic before displaying anything (specifically: use v-if) we use the template element. If you want to learn more about it have a look at Vue’s documentation.

<v-carousel-item 
       v-if="(index + 1) % columns === 1 || columns === 1" 
       :key="index"
    >

The simple part: we want to use v-carousel-item component to display a single slide – a container for the multiple elements to be shown at once. The more complicated part goes this way:

We have the number of elements we want to display at once in the columns variable – it may be 1, 2, 3, 4 and so on; an integer. If it is set to 1 we want to display every element inside its own v-carousel-item. If we want more elements in one slide, we need to check for that comparing the current item’s index with the number of element per slide (columns). If we want 3 elements per slide, we trigger the generation of new slide for the first, fourth, seventh and so on items. They share the property of leaving the remainder of 1 when divided by 3.

The last thing we do here is setting the :key to make sure the items get rendered properly. If you want to know more about it have a look at Vue’s documentation.

<v-row class="flex-nowrap">

Inside an item we want a row to keep our elements aligned properly. We also set the flex-nowrap property to prevent wrapping of the slide’s contents.

<template v-for="(n,i) in columns">

Now we want to display exactly as many items as indicated by columns property. Assuming columns is equal to 3, this translates into for (let i = 0; i < 3; i++).

<template v-if="(+index + i) < slider.length">

Now that we initialized new slide we need to check if it can be filled by remaining items, because in case we want to display 4 items and have only 3 left we need to react accordingly. I have chosen to let the remaining elements stretch and take the whole width, but another strategy would be to generate all the slots always and only check if we still have items while writing into those slots.

<v-col :key="i">

Another wrapper to make the elements inside the slide take equal width. Whatever we write inside it is the actual content.

Logic

Now with our template ready to go we need to add the logic to keep the columns in sync with the current viewport. This gets done with a computed property.

computed: {
    columns() {
      if (this.$vuetify.breakpoint.xl) {
        return 4;
      }
      if (this.$vuetify.breakpoint.lg) {
        return 3;
      }

      if (this.$vuetify.breakpoint.md) {
        return 2;
      }

      return 1;
    }
  },

We go from the biggest screen size to the smallest and whenever we get a match we return the number of columns to generate. If we haven’t found a match, we return the default – number of columns for the smallest screen size. The breakpoints are defined by vuetify and can be looked up in vuetify’s documentation.

And that’s it when it comes to custom things. If you are interested in the whole code needed to render a vuetify carousel with multiple items per slide checkout this demo.

Conclusion

Even if I’m not an everyday framework user getting started and achieving my goals with vue and vuetify was rather pleasant. Both frameworks have good documentation and lot’s to offer. Building something, that does not require a lot of maths, was a good experience, however if I needed to respond to more edge cases or got into problems with performance I would rather go with vanilla javascript.

 

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *