记录--Vue3基于Grid布局简单实现一个瀑布流组件
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助
前言
在学习Grid布局之时,我发现其是CSS中的一种强大的布局方案,它将网页划分成一个个网格,可以任意组合不同的网格,做出各种各样的布局,在刷某书和某宝首页时,我们发现其展示方式就是一种瀑布流,是一种流行的网站页面布局,视觉表现为参差不齐的多栏布局,随着页面向下滚动,这种布局会不断加载数据块并附加到当前尾部。采用瀑布流布局的方式可以打破常规网站布局排版,给用户眼前一亮的新鲜感,更好的适应移动端。
因此结合二者,本文将通过grid布局简单实现一个瀑布流组件,该组件已开源上传npm,可以直接安装使用,Git地址在文尾。
实现效果:
实现原理
1、使用grid布局将页面分为无数个小网格,每个网格高度为1px。
1 2 3 4 5 | .grid-content { display: grid; grid-auto-rows: minmax(1px, 1px); overflow: auto; } |
2、宽度根据需要自定义的列数自动分配。
1 | 'grid-template-columns' : `repeat(${props.columns}, 1fr)`, |
3、根据每个卡片窗口的高度计算每个卡片需要跨越几个网格(因为每个网格设置高为1px,所以高度就是需要跨越的网格数)
1 | 'grid-row-end' : `span ${gridItem.value.clientHeight - 1}` |
4、监听瀑布流滚动事件,通过判断滚动条距离底部的高度,在滚动到底部一定距离时加载更多的数据,以实现无限滚动。
主要代码实现
gridContent
组件主要代码,循环展示每个条目,根据自定义的列展示不同的列数量,根据触底数据判断获取最新数据。监听传入的数据进行处理,目前只是做了简单处理,后面将通过虚拟列表的形式,动态处理该数据,以增加性能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 | <template> <div class = "grid-content" ref = "gridContent" :style= "gridStyle" @scroll= "getMoreData" > <grid-item v- for = "item in showDataList" :key= "item.dataIndex" :data= "item" > <template #slot-scope= "slotProps" > <slot name= "slot-scope" :slotProps= "slotProps" ></slot> </template> </grid-item> </div> </template> <script lang= "ts" setup> import GridItem from './gridItem.vue' ; import { ref , watch } from 'vue' ; const props = defineProps({ dataList: { type: Array, default : [] }, columns: { type: Number, default : 2 }, width: { type: Number, default : 300 }, height: { type: Number, default : 400 }, bottom:{ type: Number, default : 50 }, loading:{ type: Boolean, default : true } }) const emit=defineEmits([ 'getMoreData' ]); const gridStyle = ref ({}); const showDataList = ref <any>([]) watch(() => props.dataList, (newValue) => { let tempData: any = []; newValue.forEach((item: any, index) => { tempData.push({ ...item, dataIndex: index }) }) showDataList.value = tempData; gridStyle.value = { 'grid-template-columns' : `repeat(${props.columns}, 1fr)`, width:props.width + 'px' , height:props.height + 'px' } }, { immediate: true ,deep: true }) const isLoading= ref <boolean>( false ); watch(()=>props.loading,(newValue:boolean)=>{ isLoading.value=newValue; }) const gridContent= ref <any>( null ); //根据触底数据判断获取最新数据 const getMoreData=()=>{ const scrollHeight = gridContent.value.scrollHeight || 0; const clientHeight = gridContent.value.clientHeight || 0; const scrollTop = gridContent.value.scrollTop || 0; if (scrollHeight - clientHeight - scrollTop < props.bottom && !isLoading.value){ isLoading.value= true ; emit( 'getMoreData' ); } } </script> |
grid-item
组件代码,主要通过获取组件高度设置跨越的网格数,通过插槽展示每个卡片。1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <template> <div class = "grid-item" :style= "itemStyle" > <div ref = "gridItem" > <slot name= "slot-scope" :data= "data" ></slot> </div> </div> </template> <script lang= "ts" setup> import { ref , onMounted } from 'vue' ; defineProps({ data: { type: Object, default : () => { } } }) const gridItem = ref <any>( null ); const itemStyle = ref ({}) onMounted(() => { itemStyle.value = { 'grid-row-end' : `span ${gridItem.value.clientHeight - 1}` } }) </script> <style scoped> .grid-item { grid-row-end: span 100; } </style> |
使用示例
1 2 3 4 5 6 | npm install @fcli/vue-grid-waterfall --save-dev 来安装 在项目中使用 import VueGridWaterfall from '@fcli/vue-grid-waterfall' ; const app=createApp(App) app.use(VueGridWaterfall); |
使用示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | <template> <div class = "content" > <vue-grid-waterfall :data-list= "dataList" :columns= "3" @getMoreData= "getMoreData" :loading= "isLoading" > <template #slot-scope= "{ slotProps }" > <div class = "item" :style= "{ height: slotProps.data.height, background: slotProps.data.color }" >{{ slotProps.data.color }}</div> </template> </vue-grid-waterfall> </div> </template> <script setup lang= "ts" > import vueGridWaterfall from './plugin/index.vue' ; import { ref , onMounted } from 'vue' component: { vueGridWaterfall } const dataList = ref <any>([]); //获取随机颜色 const getRandomColor = () => { const getColor: any = (color: any) => { return (color += '0123456789abcdef' [Math.floor(Math.random() * 16)]) && (color.length == 6) ? color : getColor(color); }; return '#' + getColor( '' ) } const getMoreData = () => { isLoading.value = true ; getData() } const isLoading = ref ( true ); //获取数据 const getData = () => { for ( let i = 0; i < 100; i++) { dataList.value.push({ height: 50 + Math.random() * 50 + 'px' , color: getRandomColor() }) } setTimeout(()=>{ isLoading.value = false ; }) } onMounted(() => { getData() }) </script> |
· [翻译] 为什么 Tracebit 用 C# 开发
· 腾讯ima接入deepseek-r1,借用别人脑子用用成真了~
· Deepseek官网太卡,教你白嫖阿里云的Deepseek-R1满血版
· DeepSeek崛起:程序员“饭碗”被抢,还是职业进化新起点?
· RFID实践——.NET IoT程序读取高频RFID卡/标签
2020-11-06 简易双色球dome分享