如何使用Vue实现一个图片水平瀑布流插件?这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助。
一.需求来源
今天碰到了一个需求,需要在页面里,用水平瀑布流的方式,将一些图片进行加载,这让我突然想起我很久以前写的一篇文章《JS两种方式实现水平瀑布流布局》
但是有个问题,这个需求是Vue项目的,那没办法,这里给大家分享下我的开发过程,项目主体用的是之前在学习的CRMEB的后端框架来开发,UI使用iView-UI,其余的场景与其他的vue项目一致。【相关推荐:vuejs视频教程】
二.逻辑设想
如果不是vue环境,我们的逻辑为
1.获取所有的p元素2.获取盒子的宽度,宽度都是相同,高度不同3.在浮动布局中每一行的盒子个数不固定,是根据屏幕宽度和盒子宽度决定4.获取屏幕宽度5.求出列数,屏幕宽度/盒子宽度取整6.瀑布流最关键的是第二行的盒子的排布方式,通过获取第一行盒子中最矮的一个的下标,绝对定位,top是最矮盒子的高度,left是最矮盒子的下标*盒子的宽度7.循环遍历所有的盒子,通过列数找到第一行所有的盒子,将第一行盒子的高度放入数组,再取出数组中最小的一个的下标,就是第6步思路的第一行盒子中最矮盒子的下标。8.循环继续,第二行第一个盒子,通过绝对定位,放进页面。9.关键,需要将数组中最小的值加上放进的盒子的高度10.继续循环,遍历所有11.如果想要加载更多,需要判断最后一个盒子的高度和页面滚动的距离,再将数据通过创建元素,追加进页面,再通过瀑布流布局展示
但如果是Vue项目,我们可以把逻辑归结为以下几步
1.获取屏幕宽度2..获取盒子的宽度,宽度都是相同,高度不同3.在浮动布局中每一行的盒子个数不固定,是根据屏幕宽度和盒子宽度决定4.求出列数,屏幕宽度/盒子宽度取整5.瀑布流最关键的是第二行的盒子的排布方式,通过获取第一行盒子中最矮的一个的下标,绝对定位,top是最矮盒子的高度,left是最矮盒子的下标*盒子的宽度6.继续循环,遍历所有7.如果想要加载更多,需要判断最后一个盒子的高度和页面滚动的距离,再将数据通过创建元素,追加进页面,再通过瀑布流布局展示
三.最终效果图片
四.代码分析
先看下我的html部分
<template><divclass="tab-container"id="tabContainer"><divclass="tab-item"v-for="(item,index)inpbList":key="index"><img:src="item.url"/></div></div></template><stylescoped>*{margin:0;padding:0;}/*最外层大盒子*/.tab-container{padding-top:20px;position:relative;}/*每个小盒子*/.tab-container.tab-item{position:absolute;height:auto;border:1pxsolid#ccc;box-shadow:02px4pxrgba(0,0,0,.12),006pxrgba(0,0,0,.04);background:white;/*元素不能中断显示*/break-inside:avoid;text-align:center;}.tab-container.tab-itemimg{width:100%;height:auto;display:block;}</style>
核心js部分
<script>exportdefault{name:'compList',props:{pbList:{type:Array,default:()=>{return[]}}},data(){return{};},mounted(){this.$nextTick(()=>{this.waterFall("#tabContainer",".tab-item");//实现瀑布流})},methods:{waterFall(wrapIdName,contentIdName,columns=5,columnGap=20,rowGap=20){//获得内容可用宽度(去除滚动条宽度)constwrapContentWidth=document.querySelector(wrapIdName).offsetWidth;//间隔空白区域constwhiteArea=(columns-1)*columnGap;//得到每列宽度(也即每项内容宽度)constcontentWidth=parseInt((wrapContentWidth-whiteArea)/columns);//得到内容项集合constcontentList=document.querySelectorAll(contentIdName);//成行内容项高度集合constlineConentHeightList=[];for(leti=0;i<contentList.length;i++){//动态设置内容项宽度contentList[i].style.width=contentWidth+"px";//获取内容项高度constheight=contentList[i].clientHeight;if(i<columns){//第一行按序布局contentList[i].style.top="0px";contentList[i].style.left=contentWidth*i+columnGap*i+"px";//将行高push到数组lineConentHeightList.push(height);}else{//其他行//获取数组最小的高度和对应索引letminHeight=Math.min(...lineConentHeightList);letindex=lineConentHeightList.findIndex((listH)=>listH===minHeight);contentList[i].style.top=minHeight+rowGap+"px";contentList[i].style.left=(contentWidth+columnGap)*index+"px";//修改最小列的高度最小列的高度=当前自己的高度+拼接过来的高度+行间距lineConentHeightList[index]+=height+rowGap;}}},},};</script>
这里要给大家提个醒,在当插件使用的时候,我们需要用this.$nextTick()来进行页面初始化,因为方法成功的前提是要等页面初始化加载完毕后才能进行获取和计算
总体插件代码为:
<template><divclass="tab-container"id="tabContainer"><divclass="tab-item"v-for="(item,index)inpbList":key="index"><img:src="item.url"/></div></div></template><script>exportdefault{name:'compList',props:{pbList:{type:Array,default:()=>{return[]}}},data(){return{};},mounted(){this.$nextTick(()=>{this.waterFall("#tabContainer",".tab-item");//实现瀑布流})},methods:{waterFall(wrapIdName,contentIdName,columns=5,columnGap=20,rowGap=20){//获得内容可用宽度(去除滚动条宽度)constwrapContentWidth=document.querySelector(wrapIdName).offsetWidth;//间隔空白区域constwhiteArea=(columns-1)*columnGap;//得到每列宽度(也即每项内容宽度)constcontentWidth=parseInt((wrapContentWidth-whiteArea)/columns);//得到内容项集合constcontentList=document.querySelectorAll(contentIdName);//成行内容项高度集合constlineConentHeightList=[];for(leti=0;i<contentList.length;i++){//动态设置内容项宽度contentList[i].style.width=contentWidth+"px";//获取内容项高度constheight=contentList[i].clientHeight;if(i<columns){//第一行按序布局contentList[i].style.top="0px";contentList[i].style.left=contentWidth*i+columnGap*i+"px";//将行高push到数组lineConentHeightList.push(height);}else{//其他行//获取数组最小的高度和对应索引letminHeight=Math.min(...lineConentHeightList);letindex=lineConentHeightList.findIndex((listH)=>listH===minHeight);contentList[i].style.top=minHeight+rowGap+"px";contentList[i].style.left=(contentWidth+columnGap)*index+"px";//修改最小列的高度最小列的高度=当前自己的高度+拼接过来的高度+行间距lineConentHeightList[index]+=height+rowGap;}}},},};</script><stylescoped>*{margin:0;padding:0;}/*最外层大盒子*/.tab-container{padding-top:20px;position:relative;}/*每个小盒子*/.tab-container.tab-item{position:absolute;height:auto;border:1pxsolid#ccc;box-shadow:02px4pxrgba(0,0,0,.12),006pxrgba(0,0,0,.04);background:white;/*元素不能中断显示*/break-inside:avoid;text-align:center;}.tab-container.tab-itemimg{width:100%;height:auto;display:block;}</style>
五.外层使用和懒加载
在使用这个插件的时候,有两个问题,就是因为内层是position: absolute;定位,不会撑开外部的p,会导致外层盒模型不好布局,还有就是页面下拉懒加载,那要怎么办呢?
这里我给出我的处理方法
整体代码如下:
<template><div><divclass="list-box"@scroll="scrollFun"><compList:pbList="pbList"ref="compList"></compList></div></div></template><script>importcompListfrom"@/pages/test/components/compList";exportdefault{name:'testList',components:{compList},data(){return{//瀑布流数据pbList:[{url:"/image_search/src=http%3A%2F%%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",},{url:"/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",},{url:"/image_search/src=http%3A%2F%%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",},{url:"/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",},{url:"/image_search/src=http%3A%2F%%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",},{url:"/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",},{url:"/image_search/src=http%3A%2F%%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",},{url:"/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",},{url:"/image_search/src=http%3A%2F%%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",},{url:"/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",},{url:"/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",}],addList:[{url:"/image_search/src=http%3A%2F%%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",},{url:"/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",},{url:"/image_search/src=http%3A%2F%%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",},{url:"/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",},{url:"/image_search/src=http%3A%2F%%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",},{url:"/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",},{url:"/image_search/src=http%3A%2F%%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",},{url:"/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",},{url:"/image_search/src=http%3A%2F%%2Fview%2Fphoto%2Fm%2Fpublic%2Fp2650049201.jpg&refer=http%3A%2F%&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1664935370&t=d4bf3e4d352c277a1bdebfcc8fda959f",},{url:"/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",},{url:"/it/u=2911909188,130959360&fm=253&fmt=auto&app=138&f=JPEG?w=440&h=641",}],bottomMain:true};},methods:{scrollFun(e){constoffsetHeight=e.target.offsetHeightconstscrollHeight=e.target.scrollHeightconstscrollTop=e.target.scrollTopif((scrollHeight-(offsetHeight+scrollTop))<10){if(this.bottomMain){this.bottomMain=falsethis.addListDataFun()}}},addListDataFun(){this.$Spin.show({render:(h)=>{returnh('div',[h('Icon',{'class':'demo-spin-icon-load',props:{type:'ios-loading',size:18}}),h('div','数据更新中...')])}});setTimeout(()=>{this.pbList=this.pbList.concat(this.addList)this.bottomMain=truethis.$nextTick(()=>{this.$pList.waterFall("#tabContainer",".tab-item")this.$Spin.hide()})},1000)}}};</script><stylescoped>.list-box{position:relative;width:100%;height:calc(100vh-240px);background:white;padding:20px30px20px20px;margin-top:20px;box-sizing:border-box;overflow:auto;}</style>
下拉的核心代码为:
scrollFun(e){constoffsetHeight=e.target.offsetHeightconstscrollHeight=e.target.scrollHeightconstscrollTop=e.target.scrollTopif((scrollHeight-(offsetHeight+scrollTop))<10){if(this.bottomMain){this.bottomMain=falsethis.addListDataFun()}}},addListDataFun(){this.$Spin.show({render:(h)=>{returnh('div',[h('Icon',{'class':'demo-spin-icon-load',props:{type:'ios-loading',size:18}}),h('div','数据更新中...')])}});setTimeout(()=>{this.pbList=this.pbList.concat(this.addList)this.bottomMain=truethis.$nextTick(()=>{this.$pList.waterFall("#tabContainer",".tab-item")this.$Spin.hide()})},1000)}
这里使用的是iView-UI的全局加载事件,如果你要用其他的UI框架,也可以自行修改
到这里,所有的思路就结束了
(学习视频分享:web前端开发、编程基础视频)