loreeemartiii
loreeemartiii

Reputation: 385

Vuetify v-card fit like a puzzle (Masonry layout)

I have a page with 6 or more v-card.

Is there a way to fit them like a puzzle? I would like to remove white space between small v-card on first and second line.

Now is like this:

enter image description here

Upvotes: 3

Views: 4715

Answers (3)

Eduardo Jiménez
Eduardo Jiménez

Reputation: 378

I found a simple solution (in this blog) with anything else than CSS. The same blog gives more examples of different grids and also some examples to make things prettier with JS.

The easiest way is to define a grid of columns the way you want. For example, I want on my proyect a 3 column grid about 400px each.

So we define a class masonry-with-columns, we put all the properties we want (margin, padding, column-gap, row-gap, etc)

.masonry-with-columns {
    columns: 3 400px;
    column-gap: 10px;
    max-width: 100%;
}

Each child must have 2 properties

.masonry-with-columns div {
    display: inline-block;
    width: 100%;
}
  • inline-block: prevents objects from being cut
  • width: 100%: When you do not put this property, at the time of loading it is displayed with a reduced width and then it expands, to avoid that, we must define a width

Example

<template>
    <v-container
        fill-height
        fluid
    >
        <div class="masonry-with-columns">
            <NoticeCard
                v-for="(notice, idx) in notices"
                :key="idx"
                v-bind.sync="notice"
            ></NoticeCard>
        </div>
    </v-container>
</template>
import NoticeCard from '@/components/common/Notices/NoticeCard';
export default {
    data() {
        return {
            notices: []
        }
    },
    components:{
        NoticeCard
    },
    methods: {
        getNotices() {
            axiosRequest.get('/get-cards').then((response) => {
                if ( response.status === 200 )
                    this.notices = response.data[0];
            }).catch(e => axiosResponseErrorHandler(this, e));
        },
    }
}
</script>
<style scoped>
    .masonry-with-columns {
        columns: 3 400px;
        column-gap: 10px;
        max-width: 100%;
    }
    .masonry-with-columns div {
        display: inline-block;
        width: 100%;
    }
</style>

Comments

  • Notices are filled with a server response
  • NoticeCard is your card

Results

enter image description here

enter image description here

Upvotes: 0

Ezra Siton
Ezra Siton

Reputation: 7781

No way (Yet) to solve this by Vuetify API. Related Github Feature Req:

[Feature Request] Masonry Layout #4091

Solution by Masonry.js

Use Javascript plugin. For example masonry.js.

Codepen Demo: https://codepen.io/ezra_siton/pen/gOpZqKr

Masonry.js & vuetify grid - Code snippet

<!-- https://vuetifyjs.com/en/ -->
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/@mdi/[email protected]/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.min.css" rel="stylesheet">

<div id="app">
<v-app>
  <v-content>
    <v-container>
      <v-row class="masonry">
        <v-col class="child" cols="12" sm="6">
          <v-card class="pa-2" color="pink darken-1" dark>
            <v-card-title>Card 1</v-card-title>
            <v-card-text>
              The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections form, Rackham. One of three columns
              The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections form, Rackham.
              </v-card-text>
          </v-card>
        </v-col>
        <!-- card 2-->
        <v-col class="child" cols="12" sm="6">
          <v-card class="pa-2"  color="orange darken-3" dark>
            <v-card-title>Card 2</v-card-title>
            <v-card-text>
              One The standard chunk of Lorem Ipsum used since the 1500s is 
              </v-card-text>
          </v-card>
        </v-col>
        <!-- card 3 -->
        <v-col class="child" cols="12" sm="6">
          <v-card class="pa-2" color="#385F73" dark >
            <v-card-title>Card 3</v-card-title>
            <v-card-text>
              The chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections form, Rackham.
              </v-card-text>
          </v-card>
        </v-col>
        <!-- card 4 -->
        <v-col class="child" cols="12" sm="6">
          <v-card class="pa-2" color="blue darken-4" dark >
            <v-card-title>Card 4</v-card-title>
            <v-card-text>
              The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections form, Rackham.
              </v-card-text>
          </v-card>
        </v-col>
      </v-row>
    </v-container>
  </v-content>
</v-app>
</div>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vuetify.js"></script>
<script src="https://unpkg.com/[email protected]/dist/masonry.pkgd.min.js"></script>

<script>
 new Vue({
  el: '#app',
  vuetify: new Vuetify(),
  mounted: function () {
      // Code that will run only after the
      // entire view has been rendered
      var msnry = new Masonry( '.masonry', {
        // options
        itemSelector: "[class*='col-']",
      });
  }
})
</script>

How to (Less than 1 min)

Step 1/3: CDN before body

Link directly to Masonry files on unpkg.

<script src="https://unpkg.com/[email protected]/dist/masonry.pkgd.min.js"></script>

-or- Install with npm: npm install masonry-layout and use import import Masonry from 'masonry-layout'

Step 2/3: HTML - Set container element

Masonry works on a container grid element with a group of child items.

Add a class (or id) to your flexbox grid (Set as container element). masonry in this example (Use any name you want).

<v-row class="masonry">
  <v-col class="child" cols="12" sm="6">
   <v-card class="pa-2" outlined >
..rest of the code

Step 3/3: Initialize with vanilla js

new Masonry( elem, options )

3.1: Use .masonry as a container element argument.

3.2: Inside options object - set itemSelector to: itemSelector: "[class*='col-']"

[class*='col-']: Wildcard selector (Select any class Contains col. For example: .col-6 -or- .col-md-2 ==> DRY // Clean solution)

I load the script inside vue mounted() lifecycle hook (After the components added to the DOM).

 new Vue({
  el: '#app',
  vuetify: new Vuetify(),
  mounted: function () {
      // Code that will run only after the
      // entire view has been rendered
      var msnry = new Masonry( '.masonry', {
        // options
        itemSelector: "[class*='col-']",
      });
  }
})

Docs:


Solution by custom CSS

One more option is to use flexbox/Grid and Custom CSS, in my opinion, this is too much code & ideas for such a simple task. Related article:

https://css-tricks.com/piecing-together-approaches-for-a-css-masonry-layout/

Upvotes: 8

Rohan
Rohan

Reputation: 678

If you are using VueJs and Vuetify then try to use vue.js masonry. With vue-masony, all you need to do is v-row and v-col instead of div.

<v-row v-masonry transition-duration="0.3s" item-selector=".item">
    <v-col v-masonry-tile class="item" cols="12" v-for="(content_obj, index) in class_contents" key="`content${index}`" sm="3" >
            ...
    </v-col>
</v-row>

Upvotes: 1

Related Questions