vue瀑布流布局

'瀑布流’是前端的一种’布局’方式,就像我们经常’搜百度’看到的’图片列表’都是’瀑布流’完成的,如下图,
‘瀑布流’会根据’某一列’的’高度’,来自动向’最低高度’的一’列’下面继续添加元素,下面是在’vue’项目中
使用’瀑布流’的案例。
在这里插入图片描述

vue2
<template>
    <div class="box">
        <div class="col" ref="col1">
            <transition-group name="list">
                <div class="item" v-for="item in dataList1" :key="item.id">
                    {{item.text}}
                    <img :src="item.url" />
                </div>
            </transition-group>
        </div>
        <div class="col" ref="col2">
            <transition-group name="list">
                <div class="item" v-for="item in dataList2" :key="item.id">
                    {{item.text}}
                    <img :src="item.url" />
                </div>
            </transition-group>
        </div>
    </div>
</template>

<script>
    export default {
        data() {
            return {
            mainMenuList: [
                {
                    id: 1,
                    text: '我是1',
                    url: 'https://img.zcool.cn/community/01162e5d903ad6a8012060bee46bf3.jpg@1280w_1l_2o_100sh.jpg'
                },
                {
                    id: 2,
                    text: '我是2',
                    url: 'https://img.zcool.cn/community/0121a55d903ad6a801211d53066683.jpg@1280w_1l_2o_100sh.jpg'
                },
                {
                    id: 3,
                    text: '我是3',
                    url: 'http://pic39.nipic.com/20140321/18063302_210604412116_2.jpg'
                },
                {
                    id: 4,
                    text: '我是4',
                    url: 'https://img.zcool.cn/community/0121a55d903ad6a801211d53066683.jpg@1280w_1l_2o_100sh.jpg'
                },     
                {
                    id: 5,
                    text: '我是5',
                    url: 'http://pic39.nipic.com/20140321/18063302_210604412116_2.jpg'
                },     
                {
                    id: 6,
                    text: '我是6',
                    url: 'http://pic39.nipic.com/20140321/18063302_210604412116_2.jpg'
                },
                {
                    id: 7,
                    text: '我是7',
                    url: 'https://img.zcool.cn/community/0121a55d903ad6a801211d53066683.jpg@1280w_1l_2o_100sh.jpg'
                },                                                                           
            ],
            dataList1: [],
            dataList2: []
            }
        },
        mounted() {
            this.mountMenu()
        },
        methods: {
            mountMenu(arg) {
                var temp = this.mainMenuList
                var index = arg || 0
                var refName = this.selectCol()
                if (temp.length > index) {
                    this[refName].push(this.mainMenuList[index])
                    this.$nextTick(() => {
                        this.mountMenu(index + 1)
                    })
                }
            },
            selectCol() {
                var getHeight = (ref) => {
                    return this.$refs[ref].offsetHeight
                }
                var height1 = getHeight('col1')
                var height2 = getHeight('col2')

                switch (Math.min(height1, height2)) { // Math.min()方法返回参数中最小的值
                    case height1:
                    return 'dataList1'
                    case height2:
                    return 'dataList2'
                }
            }
        }
    }
</script>

<style scoped>
.box{
    overflow: hidden;
    width: 400px;
}
.col{
    float: left;
    width: 100px;
}
img{
    width: 100%;
}
</style>

vue3
<template>
  <div class="box">
    <div class="col" ref="col1">
      <div class="item" v-for="item in dataList1" :key="item.id">
        <img :src="item.url" loading="lazy" alt="" />
        {{ item.text }}
      </div>
    </div>
    <div class="col" ref="col2">
      <div class="item" v-for="item in dataList2" :key="item.id">
        <img :src="item.url" loading="lazy" alt="" />
        {{ item.text }}
      </div>
    </div>
  </div>
</template>
<script>
import { defineComponent, reactive, toRefs, onMounted, ref, nextTick } from "vue";
export default defineComponent({
  name: "waterfall",
  setup() {
    const col1 = ref(null);
    const col2 = ref(null);
    const state = reactive({
      mainMenuList: [
        {
          id: 1,
          text: "我是1",
          url: "https://img.zcool.cn/community/01162e5d903ad6a8012060bee46bf3.jpg@1280w_1l_2o_100sh.jpg"
        },
        {
          id: 2,
          text: "我是2",
          url: "https://img.zcool.cn/community/0121a55d903ad6a801211d53066683.jpg@1280w_1l_2o_100sh.jpg"
        },
        {
          id: 3,
          text: "我是3",
          url: "http://pic39.nipic.com/20140321/18063302_210604412116_2.jpg"
        },
        {
          id: 4,
          text: "我是4",
          url: "https://img.zcool.cn/community/0121a55d903ad6a801211d53066683.jpg@1280w_1l_2o_100sh.jpg"
        },
        {
          id: 5,
          text: "我是5",
          url: "http://pic39.nipic.com/20140321/18063302_210604412116_2.jpg"
        },
        {
          id: 6,
          text: "我是6",
          url: "http://pic39.nipic.com/20140321/18063302_210604412116_2.jpg"
        },
        {
          id: 7,
          text: "我是7",
          url: "https://img.zcool.cn/community/0121a55d903ad6a801211d53066683.jpg@1280w_1l_2o_100sh.jpg"
        },
        {
          id: 8,
          text: "我是8",
          url: "https://img.zcool.cn/community/01162e5d903ad6a8012060bee46bf3.jpg@1280w_1l_2o_100sh.jpg"
        },
        {
          id: 9,
          text: "我是9",
          url: "https://img.zcool.cn/community/0121a55d903ad6a801211d53066683.jpg@1280w_1l_2o_100sh.jpg"
        },
        {
          id: 10,
          text: "我是10",
          url: "https://img0shdi28hu1.sunuping.com/upload/jpg/20230804095136/1687280090275311617.jpg"
        },
        {
          id: 11,
          text: "我是11",
          url: "https://img.zcool.cn/community/0121a55d903ad6a801211d53066683.jpg@1280w_1l_2o_100sh.jpg"
        },
        {
          id: 12,
          text: "我是12",
          url: "http://pic39.nipic.com/20140321/18063302_210604412116_2.jpg"
        },
        {
          id: 13,
          text: "我是13",
          url: "http://pic39.nipic.com/20140321/18063302_210604412116_2.jpg"
        },
        {
          id: 14,
          text: "我是14",
          url: "http://pic39.nipic.com/20140321/18063302_210604412116_2.jpg"
        }
      ],
      dataList1: [],
      dataList2: []
    });
    onMounted(() => {
      mountMenu();
    });
    const mountMenu = arg => {
      let temp = state.mainMenuList;
      let index = arg || 0;
      let refName = selectCol();
      if (temp.length > index) {
        state[refName].push(state.mainMenuList[index]);
        nextTick(() => {
          mountMenu(index + 1);
        });
      }
    };
    const selectCol = () => {
      let height1 = col1.value.offsetHeight;
      let height2 = col2.value.offsetHeight;
      switch (
        Math.min(height1, height2) // Math.min()方法返回参数中最小的值
      ) {
        case height1:
          return "dataList1";
        case height2:
          return "dataList2";
      }
    };
    return {
      ...toRefs(state),
      col1,
      col2
    };
  }
});
</script>
<style lang="less" scoped>
.box {
  width: 375px;
}
.col {
  float: left;
  width: 185px;
}
img {
  width: 100%;
  object-fit: contain;
}
.item {
  text-align: center;
  margin: 10px 0;
}
</style>

两行

<template>
  <div class="page-main">
    <div class="card">
      <div class="coloum1">
        <div class="card-item" v-for="(item, index) in cardList1" :key="index" :style="[{ background: item.color }, { height: item.height }, { lineHeight: item.height }]" :class="{ visibles: isVisibility }">
          <p class="text">{{ item.num }}</p>
        </div>
      </div>
      <div class="coloum2">
        <div class="card-item" v-for="(item, index) in cardList2" :key="index" :style="[{ background: item.color }, { height: item.height }, { lineHeight: item.height }]" :class="{ visibles: isVisibility }">
          <p class="text">{{ item.num }}</p>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, reactive, nextTick } from "vue";
const cardList = reactive([
  // 测试数据
  {
    num: "0",
    color: "#FCCF0A",
    height: "100px"
  },
  {
    num: "1",
    color: "#e53e26",
    height: "200px"
  },
  {
    num: "2",
    color: "green",
    height: "300px"
  },
  {
    num: "3",
    color: "red",
    height: "100px"
  },
  {
    num: "4",
    color: "#000",
    height: "200px"
  },
  {
    num: "5",
    color: "blue",
    height: "300px"
  },
  {
    num: "6",
    color: "#691ba3",
    height: "100px"
  },
  {
    num: "7",
    color: "orange",
    height: "400px"
  },
  {
    num: "8",
    color: "yellow",
    height: "500px"
  },
  {
    num: "9",
    color: "#409eff",
    height: "100px"
  }
]);

const isVisibility = ref(true);
// 由于渲染时候对数据的两次赋值,则会出现一次闪现,需要防抖

onMounted(() => {
  equallyCard();
  nextTick(() => {
    caLFlex();
  }).then(() => {
    isVisibility.value = true;
  });
});

const cardList1 = ref([]); // 各列的数据
const cardList2 = ref([]);

function equallyCard() {
  // 平分数据,确保页面上遍历卡片dom的真实顺序与平分的一致 document.querySelectorAll('.card-item'))
  let num = parseInt(cardList.length / 2);
  cardList.forEach((item, index) => {
    if (index < num) {
      cardList1.value.push(item);
      return;
    }
    cardList2.value.push(item);
  });
}

function caLFlex() {
  let arr1 = []; // 第一列的值
  let arr2 = []; // 第二列的值
  let heightArry_1 = []; // 第一列的卡片高度
  let heightArry_2 = []; // 第二列的卡片高度
  Array.from(document.querySelectorAll(".card-item")).forEach((item, index) => {
    if (index === 0) {
      // 第一行中的元素无需判断,直接加到新的数组中
      heightArry_1.push(item.offsetHeight);
      arr1.push(cardList[index]);
      return;
    }
    if (index === 1) {
      heightArry_2.push(item.offsetHeight);
      arr2.push(cardList[index]);
      return;
    }
    const heightTotal_1 = heightArry_1.length ? Array.from(heightArry_1).reduce((acc, cur) => acc + cur) : 0; // 第一列的总高度
    const heightTotal_2 = heightArry_2.length ? Array.from(heightArry_2).reduce((acc, cur) => acc + cur) : 0; // 第二列的总高

    // 找到最小值
    let minNumber = Math.min(heightTotal_1, heightTotal_2);
    switch (minNumber) {
      case heightTotal_1:
        heightArry_1.push(item.offsetHeight);
        arr1.push(cardList[index]);
        break;
      case heightTotal_2:
        heightArry_2.push(item.offsetHeight);
        arr2.push(cardList[index]);
        break;
    }
  });

  // 重新将数据赋值给各列数组
  cardList1.value = arr1;
  cardList2.value = arr2;
}
</script>

<style lang="less" scoped>
.page-main {
  background: #ffffff;
  height: 100vh;
  overflow: auto;
  // padding: 0 30px;
  .card {
    display: flex;
    flex-direction: row;
    justify-content: space-around;
    .card-item {
      visibility: hidden;
      margin-bottom: 20px;
      text-align: center;
      width: 187.5px;
      border-radius: 16px;
    }
    .visibles {
      visibility: visible;
    }
  }
}
</style>

三行

<template>
  <div class="page-main">
    <div class="card">
      <div class="coloum1">
        <div class="card-item" v-for="(item, index) in cardList1" :key="index" :style="[{ background: item.color }, { height: item.height }, { lineHeight: item.height }]" :class="{ visibles: isVisibility }">
          <p class="text">{{ item.num }}</p>
        </div>
      </div>
      <div class="coloum2">
        <div class="card-item" v-for="(item, index) in cardList2" :key="index" :style="[{ background: item.color }, { height: item.height }, { lineHeight: item.height }]" :class="{ visibles: isVisibility }">
          <p class="text">{{ item.num }}</p>
        </div>
      </div>
      <div class="coloum3">
        <div class="card-item" v-for="(item, index) in cardList3" :key="index" :style="[{ background: item.color }, { height: item.height }, { lineHeight: item.height }]" :class="{ visibles: isVisibility }">
          <p class="text">{{ item.num }}</p>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, reactive, nextTick } from "vue";
const cardList = reactive([
  // 测试数据
  {
    num: "0",
    color: "#FCCF0A",
    height: "100px"
  },
  {
    num: "1",
    color: "#e53e26",
    height: "200px"
  },
  {
    num: "2",
    color: "green",
    height: "300px"
  },
  {
    num: "3",
    color: "red",
    height: "100px"
  },
  {
    num: "4",
    color: "#000",
    height: "200px"
  },
  {
    num: "5",
    color: "blue",
    height: "300px"
  },
  {
    num: "6",
    color: "#691ba3",
    height: "100px"
  },
  {
    num: "7",
    color: "orange",
    height: "400px"
  },
  {
    num: "8",
    color: "yellow",
    height: "500px"
  },
  {
    num: "9",
    color: "#409eff",
    height: "100px"
  }
]);

const isVisibility = ref(true);
// 由于渲染时候对数据的两次赋值,则会出现一次闪现,需要防抖

onMounted(() => {
  equallyCard();
  nextTick(() => {
    caLFlex();
  }).then(() => {
    isVisibility.value = true;
  });
});

const cardList1 = ref([]); // 各列的数据
const cardList2 = ref([]);
const cardList3 = ref([]);

function equallyCard() {
  // 平分数据,确保页面上遍历卡片dom的真实顺序与平分的一致 document.querySelectorAll('.card-item'))
  let num = parseInt(cardList.length / 3);
  cardList.forEach((item, index) => {
    if (index < num) {
      cardList1.value.push(item);
      return;
    }
    if (index < 2 * num) {
      cardList2.value.push(item);
      return;
    }
    cardList3.value.push(item);
  });
}

function caLFlex() {
  let arr1 = []; // 第一列的值
  let arr2 = []; // 第二列的值
  let arr3 = []; // 第二列的值
  let heightArry_1 = []; // 第一列的卡片高度
  let heightArry_2 = []; // 第二列的卡片高度
  let heightArry_3 = []; // 第二列的卡片高度
  Array.from(document.querySelectorAll(".card-item")).forEach((item, index) => {
    if (index === 0) {
      // 第一行中的元素无需判断,直接加到新的数组中
      heightArry_1.push(item.offsetHeight);
      arr1.push(cardList[index]);
      return;
    }
    if (index === 1) {
      heightArry_2.push(item.offsetHeight);
      arr2.push(cardList[index]);
      return;
    }
    if (index === 2) {
      heightArry_3.push(item.offsetHeight);
      arr3.push(cardList[index]);
      return;
    }
    const heightTotal_1 = heightArry_1.length ? Array.from(heightArry_1).reduce((acc, cur) => acc + cur) : 0; // 第一列的总高度
    const heightTotal_2 = heightArry_2.length ? Array.from(heightArry_2).reduce((acc, cur) => acc + cur) : 0; // 第二列的总高
    const heightTotal_3 = heightArry_3.length ? Array.from(heightArry_3).reduce((acc, cur) => acc + cur) : 0; // 第三列的总高度

    // 找到最小值
    let minNumber = Math.min(heightTotal_1, heightTotal_2, heightTotal_3);
    switch (minNumber) {
      case heightTotal_1:
        heightArry_1.push(item.offsetHeight);
        arr1.push(cardList[index]);
        break;
      case heightTotal_2:
        heightArry_2.push(item.offsetHeight);
        arr2.push(cardList[index]);
        break;
      case heightTotal_3:
        heightArry_3.push(item.offsetHeight);
        arr3.push(cardList[index]);
        break;
    }
  });

  // 重新将数据赋值给各列数组
  cardList1.value = arr1;
  cardList2.value = arr2;
  cardList3.value = arr3;
}
</script>

<style lang="less" scoped>
.page-main {
  background: #ffffff;
  height: 100vh;
  overflow: auto;
  // padding: 0 30px;
  .card {
    display: flex;
    flex-direction: row;
    justify-content: space-around;
    .card-item {
      visibility: hidden;
      margin-bottom: 20px;
      text-align: center;
      width: 125px; 
      border-radius: 16px;
    }
    .visibles {
      visibility: visible;
    }
  }
}
</style>

当然,我可以为您提供一个使用Vue实现的瀑布流布局案例。 首先,您需要安装Vuevue-masonry-css插件。您可以通过以下命令来安装它们: ``` npm install vue vue-masonry-css``` 接下来,您可以创建一个Vue组件来实现瀑布流布局。下面是一个简单的示例: ```vue<template> <div class="masonry"> <div v-for="(item, index) in items" :key="index" class="item"> <!-- 在这里放置您的内容 --> <img :src="item.imageUrl" alt="Item Image" /> <p>{{ item.title }}</p> </div> </div> </template> <script> import VueMasonryCss from "vue-masonry-css"; export default { name: "MasonryLayout", data() { return { items: [ { imageUrl: "https://example.com/image1.jpg", title: "Item1" }, { imageUrl: "https://example.com/image2.jpg", title: "Item2" }, // 添加更多项目... ] }; }, components: { VueMasonryCss } }; </script> <style scoped> .masonry { display: flex; flex-wrap: wrap; } .item { width:200px; /* 每个项的宽度 */ } </style> ``` 在上述示例中,我们使用了vue-masonry-css插件来实现瀑布流布局。通过在外部容器上添加`.masonry`类,并设置`display: flex; flex-wrap: wrap;`,我们创建了一个弹性布局并使其换行。 每个项使用`.item`类来设置宽度,您可以根据自己的需求进行调整。 在`data`中,我们定义了一个`items`数组来存储每个项目的数据。您可以根据自己的需求修改该数组,并在模板中使用`v-for`指令来循环渲染每个项目。 这只是一个简单的示例,您可以根据自己的需求进行更改和扩展。希望对您有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值