Grid 布局 实现瀑布流
-
html
<div class="gridDiv"> <div v-for="(item,index) in 20" :style="{'grid-row': `auto / span ${heightArray[index]}`}" > <div class="gridItemConten"> <div class="gridText"> {{item}}-{{index}} <div v-for="el in item" v-if="item<6"> item:{{item}}--{{el}} ++ item:{{item}}--{{el}} </div> </div> </div> </div> </div> <style lang="scss" scoped> .gridDiv { width: calc(100% - 20px); margin: 0 auto; display: grid; grid-template-columns: repeat(3, 1fr); // 指定三列,自动宽度 grid-column-gap: 30px; //横向间隔 grid-auto-flow: row dense; // 是否自动补齐空白 grid-auto-rows: 1px; // base高度,grid-row基于此运算 // justify-items: center; .gridItemConten { padding-bottom: 10px; // 纵向间距 .gridText { text-align: center; background-color: red; } } } </style>
-
js
data() { return { heightArray:[] } }, created(){ this.$nextTick(()=>{ const gridHeightFn=()=>{ Array.prototype.slice.call(document.querySelectorAll('.gridDiv .gridItemConten')).forEach((item,index)=>{ this.heightArray.splice(index,1,parseInt(item.getBoundingClientRect().height)) }) } window.onresize=()=>{ gridHeightFn() } gridHeightFn() }) },
-
实现:
multi-column 多列布局实现瀑布流
-
html:
<div class="gapDiv"> <div class="gapItem" v-for="item in 10"> {{item}} <div v-for="el in item" v-if="item<6"> item:{{item}}--{{el}} ++ item:{{item}}--{{el}} </div> </div> </div> <style lang="scss" scoped> .gapDiv { width: calc(100% - 20px); margin: 0 auto; column-count: 4; // 默认列数 column-gap: 30px; // 列间距 .gapItem { break-inside: avoid; // 防止item展示不全裂开 width: 150px; background-color: red; margin-bottom: 10px; } } </style>
-
实现:
flex 布局实现瀑布流
-
html:
<div class="flexDiv"> <div class="flexItem" v-for="item in 10"> {{item}} <div v-for="el in item" v-if="item<6"> item:{{item}}--{{el}} ++ item:{{item}}--{{el}} </div> </div> </div> <style lang="scss" scoped> .flexDiv { width: calc(100% - 20px); margin: 0 auto; display: flex; flex-flow: column wrap; height: 30vh; .flexItem { background-color: red; margin-bottom: 10px; width: calc(100% / 3 - 20px); } } </style>
-
实现:
js 实现
-
html:
<div class="divBox" id="divBox1"> <div class="boxItem" v-for="item in 10"> {{item}} <div v-for="el in item" v-if="item<6"> item:{{item}}--{{el}} ++ item:{{item}}--{{el}} </div> </div> <div class="div-main"></div> </div> <style lang="scss" scoped> .divBox { width: calc(100% - 20px); margin: 0 auto; .boxItem { // width:150px; background-color: red; } .div-main { margin-top: 20px; width: 100%; height: 300px; background-color: #3772f6; } } </style>
-
js:
created(){ this.$nextTick(()=>{ window.onresize=()=>{ this.waterfallView('#divBox1',{itemEl:'.boxItem',paddingX:20,paddingY:10,isFollow:true,justifyContent:'center'}) } this.waterfallView('#divBox1',{itemEl:'.boxItem',paddingX:20,paddingY:10,isFollow:true,justifyContent:'center'}) }) }, methods:{ // 瀑布流 /** * elName:父级元素的class 或者id,如:'.boxItem'、'#boxItem' * objectData:{ * itemEl:对应item的calss,如:'.boxItem',不传入默认是父级元素下面的所有元素 * paddingX:横向的间距 * paddingY:竖向的间距 * rowNum:显示多少列,不传就设置固定宽度 * justifyContent:横向的位置,left、center、right(设置这个的时候,不需要传入rowNum才会生效) * isFollow:是否自动填充到最小列 * } * */ waterfallView(elName,objectData){ let thisIsReset=false const elDom=document.querySelectorAll(elName) const changeView = async (el)=>{ const {itemEl=null,paddingX=0,paddingY=0,rowNum=0,isFollow=false,justifyContent=''}=objectData let thisRowNum=rowNum let thisPaddingX=paddingX const allWidth = el.offsetWidth const thisBlaknDom=el.querySelector('.--blakn-div--') if(thisBlaknDom && el.getAttribute('elWidth')){ if(thisBlaknDom.offsetWidth!=Number(el.getAttribute('elWidth'))){ thisIsReset=true }else{ thisIsReset=false } } el.setAttribute('elWidth',allWidth) el.style.position='relative' // 获取已经存在的对应列的数组 const heightArray=thisIsReset?[]:(el.getAttribute('heightArray')?JSON.parse(el.getAttribute('heightArray')):[]); // 获取需要瀑布流的dom的元素组 const elChildrenArray=itemEl? Array.prototype.slice.call(el.querySelectorAll(itemEl)):Array.prototype.slice.call(el.children).filter(n=>n.className.indexOf('--blakn-div--')==-1) let firstLeft=0 let itemWidth =0 if(thisRowNum>0){ itemWidth = (allWidth-(thisRowNum-1)*thisPaddingX)/thisRowNum }else{ itemWidth = elChildrenArray[0].offsetWidth thisRowNum = parseInt((allWidth+thisPaddingX) / (itemWidth + thisPaddingX)) if(justifyContent!=''){ thisPaddingX=0 if(justifyContent=='left'){ thisPaddingX=paddingX thisRowNum = parseInt((allWidth+thisPaddingX) / (itemWidth + thisPaddingX)) }else if(justifyContent=='center'){ let newRowNum= parseInt(allWidth/itemWidth) newRowNum=newRowNum>elChildrenArray.length?elChildrenArray.length:newRowNum if(newRowNum-1>0){ thisPaddingX = (allWidth - itemWidth*newRowNum)/(newRowNum-1)-0.5 }else{ thisPaddingX=0 firstLeft=(allWidth-itemWidth) /2 } thisRowNum = parseInt((allWidth+thisPaddingX) / (itemWidth + thisPaddingX)) }else{ thisPaddingX=paddingX thisRowNum = parseInt((allWidth+thisPaddingX) / (itemWidth + thisPaddingX)) firstLeft = allWidth-(itemWidth*thisRowNum + thisPaddingX*(thisRowNum-1)) if(allWidth <= itemWidth){ firstLeft = 0 } } } } thisRowNum = thisRowNum<=0?1:thisRowNum const elLength=elChildrenArray.length // 获取开始循环的index const addIndex=thisIsReset?0:(el.getAttribute('lastIndex')?Number(el.getAttribute('lastIndex')):0) for (let i=addIndex+1;i<=elLength;i++){ const thisIndex=i-1 const item=elChildrenArray[thisIndex] const indexNum=i%thisRowNum==0?thisRowNum:i%thisRowNum // -------------------- // const itemHeight=elChildrenArray[0].offsetHeight // item.style.height=itemHeight+(indexNum-1)*40+'px' // -------------------- let newIndex=indexNum // 动态将item追加到最小高度列的数组位置 if(isFollow){ const newHeightArray=heightArray.map(n=>n+item.offsetHeight+(thisIndex-thisRowNum<0?0:paddingY)) if(newHeightArray.length>=thisRowNum){ newIndex=newHeightArray.indexOf(Math.min(...newHeightArray))+1 } } const styleObject={} // 获取对应列的高度,并计算当前item对应的top let thisTop=heightArray[newIndex-1]?heightArray[newIndex-1]:0 thisTop=(thisTop + (thisIndex-thisRowNum<0?0:paddingY)) // 计算对应item的left let thisLeft=(newIndex-1)*itemWidth thisLeft =firstLeft+thisLeft+(newIndex==1?0:thisPaddingX*(newIndex-1)) // 设置item对应的style styleObject['position']='absolute' styleObject['top']=thisTop+'px' styleObject['left']=thisLeft+'px' styleObject['width']=itemWidth+'px' Object.keys(styleObject).forEach(key=>{ item.style[key]=styleObject[key] }) // 给对应列的高度增加当前item的高度 heightArray[newIndex-1]=thisTop+item.offsetHeight // 设置当前的index item.setAttribute('index',thisIndex) } // 在父级元素设置每列高度的数组 el.setAttribute('heightArray',JSON.stringify(heightArray)) // 设置当前循环的最后一个元素的index el.setAttribute('lastIndex',elLength) // 为父级元素填充占位div const blaknDivStyle=`width:100%;height:${Math.max(...heightArray)}px` if(el.querySelectorAll('.--blakn-div--').length>0){ Array.prototype.slice.call(el.querySelectorAll('.--blakn-div--'))[0].style=blaknDivStyle }else{ const blaknDom=document.createElement('div') blaknDom.setAttribute('class','--blakn-div--') blaknDom.style=blaknDivStyle el.insertBefore(blaknDom, elChildrenArray[0]); } } Array.prototype.slice.call( elDom ).forEach(elItem=>{ changeView(elItem) }) }, }
-
实现: