column-count : 定义列数
column-gap :列与列之间的间隔
顺序只能是 从上到下, 再左到右
由于排列顺序是先 从上到下, 再左到右,只能用于数据固定, 无法动态的加载追加,对于滚动到底部加载新数据则无法实现。
有时候页面会出现在前几列的最后一个元素的内容被自动断开,一部分在当前列尾,一部分在下一列的列头。这时候子元素可以用 break-inside设置为不被截断 avoid来控制
break-inside: avoid; // 不被截断 默认值是auto,会被截断
<template>
<div class="page-main">
<div class="card">
<div class="card-item" v-for="(item,index) in cardList" :key="index" :style="[{background: item.color},{height: item.height},{lineHeight: item.height}]">
<p class="text">{{index}}</p>
</div>
</div>
</div>
</template>
<script setup>
import {ref} from 'vue'
const cardList = ref([ // 模拟数据
{
color: '#FCCF0A',
height: '100px',
},
......
])
</script>
<style lang="scss" scoped>
.page-main{
background: #ffffff;
min-height:100vh;
padding: 0 30px;
.card{
column-count: 3; // 定义三列
column-gap: 20px; // 列与列的距离为20px
.card-item{
text-align: center;
width: 216px;
border-radius: 16px;
grid-row-start: auto;
margin-bottom: 20px;
break-inside: avoid; // 不被截断
}
}
}
</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',
},
...测试数据
])
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){
cardList1.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
}
<style lang="scss" scoped>
.page-main{
background: #ffffff;
height:100vh;
overflow: hidden;
padding: 0 30px;
.card{
display: flex;
flex-direction: row;
justify-content: space-around;
.card-item{
visibility: hidden;
margin-bottom: 20px;
text-align: center;
width: 216px;
border-radius: 16px;
}
.visibles {
visibility: visible;
}
}
}
</style>
npm i vue-waterfall2@latest --save
import waterfall from 'vue-waterfall2'
Vue.use(waterfall)
<template>
<div class="container-water-fall">
<waterfall
:col="col"
:data="data"
@scroll="scroll"
@finish="finish"
:height="'100vh'"
>
<template>
<div
class="cell-item"
v-for="(item, index) in data"
:key="index"
>
<img v-if="item.img" :src="item.img" alt="加载错误" />
<div class="item-body">
<div class="item-desc">{{ item.title }}</div>
<div class="item-footer">
<div
v-if="item.avatar"
class="avatar"
:style="{
backgroundImage: `url(${item.avatar})`,
}"
></div>
<div class="name">{{ item.user }}</div>
<div
class="like"
:class="item.liked ? 'active' : ''"
>
<i></i>
<div class="like-total">{{ item.like }}</div>
</div>
</div>
</div>
</div>
</template>
</waterfall>
</div>
</template>
<script>
import json from "./components/data.json";
export default {
name: "App",
/*
注意:
1. gutterWidth需要与width一起使用才会生效,否则会进行自适应宽度(使用rem布局时,需先计算出自适应后的宽度再传值)
2. 使用了`waterfall`的父组件,如果样式存在问题,可去掉css `scoped`尝试一下
*/
data() {
return {
data: [
{
img:
"https://image.watsons.com.cn//upload/8a316140.png?w=377&h=451&x-oss-process=image/resize,w_1080",
avatar:
"https://img.xiaohongshu.com/avatar/5b7d198a7e6e15000155f7c9.jpg@80w_80h_90q_1e_1c_1x.jpg",
title: "最近浴室新宠,日系神仙好物了解一下~",
user: "迷人的小妖精迷人的小妖精",
like: "953",
},
{
img:
"https://image.watsons.com.cn//upload/5c3e51e4.jpg?w=720&h=960&x-oss-process=image/resize,w_1080",
avatar:
"https://img.xiaohongshu.com/avatar/5b7d198a7e6e15000155f7c9.jpg@80w_80h_90q_1e_1c_1x.jpg",
title: "真的是万能.超级实用.包包必备单品! ! !",
user: "迷人的小妖精迷人的小妖精",
like: "952",
},
{
img:
"https://image.watsons.com.cn//upload/bef41e67.JPG?w=712&h=534&x-oss-process=image/resize,w_1080",
avatar:
"https://img.xiaohongshu.com/avatar/5b7d198a7e6e15000155f7c9.jpg@80w_80h_90q_1e_1c_1x.jpg",
title: "150元搞定全套护肤品,这些护肤好物必须交出来!",
user: "迷人的小妖精迷人的小妖精",
like: "953",
},
{
img:
"https://image.watsons.com.cn//upload/13afe9a7.jpg?x-oss-process=image/resize,w_1080",
avatar:
"https://img.xiaohongshu.com/avatar/5b7d198a7e6e15000155f7c9.jpg@80w_80h_90q_1e_1c_1x.jpg",
title: "夏天用这款姨妈巾,让你体验真正的清爽",
user: "迷人的小妖精迷人的小妖精",
like: "953",
},
{
img:
"https://image.watsons.com.cn//upload/642cb83c.jpeg?w=1080&h=1080&x-oss-process=image/resize,w_1080",
avatar:
"https://img.xiaohongshu.com/avatar/5b7d198a7e6e15000155f7c9.jpg@80w_80h_90q_1e_1c_1x.jpg",
title:
"贵妇级好用的水乳有哪些呢?千万不要去乱尝试贵妇级好用的水乳有哪些呢?千万不要去乱尝试贵妇级好用的水乳有哪些呢?千万不要去乱尝试",
user: "迷人的小妖精迷人的小妖精",
like: "953",
},
{
img:
"https://image.watsons.com.cn//upload/98c7c4c3.jpg?w=1210&h=1210&x-oss-process=image/resize,w_1080",
avatar:
"https://img.xiaohongshu.com/avatar/5b7d198a7e6e15000155f7c9.jpg@80w_80h_90q_1e_1c_1x.jpg",
title:
"贵妇级好用的水乳有哪些呢?千万不要去乱尝试贵妇级好用的水乳有哪些呢?千万不要去乱尝试贵妇级好用的水乳有哪些呢?千万不要去乱尝试",
user: "迷人的小妖精迷人的小妖精",
like: "953",
},
{
img:
"https://image.watsons.com.cn//upload/25ab20fe.JPG?w=1000&h=1200&x-oss-process=image/resize,w_1080",
avatar:
"https://img.xiaohongshu.com/avatar/5b7d198a7e6e15000155f7c9.jpg@80w_80h_90q_1e_1c_1x.jpg",
title: "夏天用这款姨妈巾,让你体验真正的清爽",
user: "迷人的小妖精迷人的小妖精",
like: "953",
},
{
img:
"https://image.watsons.com.cn//upload/083767f0.JPG?w=828&h=620&x-oss-process=image/resize,w_1080",
avatar:
"https://img.xiaohongshu.com/avatar/5b7d198a7e6e15000155f7c9.jpg@80w_80h_90q_1e_1c_1x.jpg",
title: "150元搞定全套护肤品,这些护肤好物必须交出来!",
user: "迷人的小妖精迷人的小妖精",
like: "953",
},
],
col: 2,
};
},
computed: {
itemWidth() {
return 138 * 0.5 * (document.documentElement.clientWidth / 375); //rem布局 计算宽度
},
gutterWidth() {
return 9 * 0.5 * (document.documentElement.clientWidth / 375); //rem布局 计算x轴方向margin(y轴方向的margin自定义在css中即可)
},
},
methods: {
scroll(scrollData) {
console.log(scrollData);
},
switchCol(col) {
this.col = col;
console.log(this.col);
}
},
};
</script>
<style>
* {
margin: 0;
}
.cell-item {
width: 100%;
margin-bottom: 10px;
background: #ffffff;
border: 2px solid #f0f0f0;
border-radius: 12px 12px 12px 12px;
overflow: hidden;
box-sizing: border-box;
}
img {
width: 100%;
height: auto;
display: block;
}
</style>