做了一个图片管理系统,由于图片的高度不一,需要瀑布流布局。
先看效果
技术需求
HTML、javascript、数学
固定数据
js函数是我参照其他网页的布局效果想出来的,这里我们先采用固定的列数来进行分析。
首先我们假设会有5列的图片,这样每行最多就会有5张图片。
我们将父容器的position设置为相对定位,图片的位置设置成绝对定位,然后在图片加载完成后js遍历修改图片的位置。
function rePosion(){
// 假设固定有5列
var nowNumber=5
// 行数是后面校验了需要从0开始,因为第一行不需要加top的位置
var nowRow=0
// 获取所有的图片元素
var imgs=document.querySelectorAll('.img-box')
// 开始遍历
imgs.forEach((ele,index)=>{
// 先调整水平位置
// 假设我们设置图片水平宽度为200,左右边距为16,那第一个的水平位置就是16,第二个水平位置就是16+200+16*2=254,第三个位置就是16+(200+16*2)*2=454
ele.style.left=((index%nowNumber)*238)+16+'px'
// 高度需要获取前一排的元素位置来确定,因为我们设置的是有5列,那么第6张图片就会换行。第6章图片top的位置就是第1张图片的底部位置加边距。规律就是top位置就是index比自己少5的底部位置加边距。那这样就好办了
// 第一行的top位置不需要添加,所以这里增加一个行数判断
if(index>=nowNumber){
let nowHeight=document.querySelectorAll('.img-box')[index-nowNumber].offsetHeight
nowHeight=nowHeight+document.querySelectorAll('.img-box')[index-nowNumber].offsetTop
// 上一行,同一列的底部像素位置等于:其top位置(offsetTop)+其高度(offsetHeight)
// 设置图片的top位置,再加上一个边距就可以了
ele.style.top=nowHeight+12+'px'
}else{
ele.style.top=12+'px'
}
// 给行数的序号增加,这里没有用到这个参数,但是避免会用到,所以还是计算一下
if(index>=(nowNumber-1) && index%(nowNumber-1)==0){
nowRow++
// console.log(nowRow)
}
})
}
1.先调整水平位置
假设我们设置图片水平宽度为200,左右边距为16,那第一个的水平位置就是16,第二个水平位置就是16+200+162=254,第三个位置就是16+(200+162)*2=454
2.高度需要获取前一排的元素位置来确定,因为我们设置的是有5列,那么第6张图片就会换行。第6章图片top的位置就是第1张图片的底部位置加边距。规律就是top位置就是index比自己少5的底部位置加边距。那这样就好办了
第一行的top位置不需要添加,所以这里增加一个行数判断
3.上一行,同一列的底部像素位置等于:其top位置(offsetTop)+其高度(offsetHeight)
设置图片的top位置,再加上一个边距就可以了
这样就完成了一个固定列数的函数了,现在我们只需根据动态获取父容器的宽度来除以图片的宽度,向下取整就看获的动态的列数了。
动态数据
这样就完成了一个固定列数的函数了,现在我们只需根据动态获取父容器的宽度来除以图片的宽度,向下取整就看获的动态的列数了。
// 计算出目前行的大小
var imgBoxWidth=document.querySelector('.list-box').offsetWidth
// 动态获取列数,这里我设置图片宽度加边距为246
var nowNumber=Math.ceil(imgBoxWidth/246)-1
防抖节流
既然我们已经把上面的函数做成动态获取了,那肯定需要有一个执行的节点。我这里假设的节点是再图片加载完成获取窗口大小发生改变。
这里就需要我们做一个防抖函数。【需要先理解闭包】
我们真正执行的函数会是防抖函数计算后返回的闭包函数。
所以我们需要先执行防抖函数,获取到动态调整位置函数的闭包,再在时间节点执行返回的闭包函数。
function debounce(func,delay=500){
// let timer // 实在不行只能报timer放到全局了
return function(){
let context=this;
let args=arguments;
if(timer) clearTimeout(timer);
timer=setTimeout(()=>{
func.apply(this,args)
},delay)
}
}
这是防抖函数,比较简单。现在的模式是将timer变量全局化,这样我们直接执行防抖函数就可以。
这样是可以达到效果的,但是这样有点不太对。
function debounce(func,delay=500){
let timer // timer放到闭包
return function(){
let context=this;
let args=arguments;
if(timer) clearTimeout(timer);
timer=setTimeout(()=>{
func.apply(this,args)
},delay)
}
}
我们需要将timer放到闭包,然后在哪里执行以下防抖函数,在声明一个变量来接收防抖函数返回的函数。
var refreshPosion=debounce(rePosion,100)
// console.log(refreshPosion)
// 我们最好真正需要执行的其实是refreshPosion()
防抖常见错误
常见的错误:直接在防抖的地方执行debounce函数,但其实它只是作为一层计算,返回一个函数,真正执行的是返回的这个函数。
js闭包
关于闭包其原理大家自行了解吧,这里就不拓展了。简单的模式就是函数里会返回一个函数,函数里面的变量会被返回的函数带着跑。
优化
大家在使用的时候可能会发现问题了,就是防抖里面会有几秒的延时,但是我们需要的就是这个延时,但是这个延时会导致第一个延时时页面布局像是bug一样。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H7xIgy9d-1641657178486)(/article/img/AI22eq010h08.png)]
所以我们在延时前也需要调用一次reposion()。一般的我会在ajax请求完成之后执行一次
this.$nextTick(()=>{
this.rePosion()
})
——————————
转载请注明出处!!!
本文首发:https://www.ic365club.com
ic365club是知识分享和学习交流的扁平化平台,欢迎广大爱好学习的伙伴一起加入