vue实现瀑布流
作者:互联网
本文中所采用的纯css实现瀑布流的效果并不是很好分析如下(个人见解,可能会有错误,请见谅):
1.如果要实现下拉加载更多功能的话就不能用视频(https://www.bilibili.com/video/BV1xa4y147JP)中的第二种方法。虽然第二种方法可以实现图像的横向排序(第一行显示序号为0~n),但是需要固定距离,我觉得用这个方法的话有所限制,也可能我理解不够,见谅哈。
2.过要实现图片的横向排序的话,就不能用视频(https://www.bilibili.com/video/BV1xa4y147JP)中的第一种方法,因为第一种方法图像时纵向排序的(第一列为0~n,第二列为n+1~2n,......)。这样对之后上传新的图片不是很友好,因为新的始终是在第一列或者最后一列,按常理来讲,新图片应该是放在第一行最好。
最后我采用的是js实现瀑布流(这玩意确实是顶级折磨)。参考链接为"https://segmentfault.com/a/1190000012621936"(这个很重要)。中心思想:1.预先设定好要多少列(这里是更具我自己需求来的)2.排列第一行,动态设置每个盒子的left就可以排列好3.排列好第一行后,获取第一行盒子的高度,并且存到一个数组arr中,因为第二行的排列只需要考虑top值(这里可以适当加一个值,让上下两张图片有间隙)4.排列第二行,以此类推。详细的可以看那位作者的文章(上面的连接),里面的说明更加具体。5.在mounted中进行监听,以便随着窗口的改变,里面的item也跟着改变。
我用的是vue2,据说是要避免直接操作dom(我也还没遇到直接操作导致的bug),我用的是ref来操作的,相关代码贴图如下(我自己写的网站还不知道怎么实现贴代码,且不打乱格式,见谅哈)
实验结果
代码部分
html代码
<div class="picList">
<div class="masonry" ref="pictest">
<div class="item" v-for="(picture,index) in piclist" :key="index">
<!-- 删掉一个ifload@load="ifLoad" -->
<!--判断图片是否加载好-->
<img :src="picture.pic" :alt="index" @click="showBigPic(index)" class="picture" @load="ifLoad">
<el-dialog
title="大图"
:visible.sync="centerDialogVisible"
width="50%"
center>
<img :src="bigPic" alt="" style="width:96%">
<span slot="footer" class="dialog-footer">
<el-button @click="centerDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="centerDialogVisible = false">确 定</el-button>
</span>
</el-dialog>
<div class="picName">{{picture.name}}</div>
<div class="icon">
<!-- 点赞 -->
<i class="iconfont like" :ref="'picLike'+index" @click="like(index)"></i>
<!-- 收藏 -->
<i class="iconfont collect" :ref="'picCollect'+index" @click="collect(index)"></i>
<!-- 评论可以不用变色 <i class="iconfont comment" :ref="'picComment'+index" @click="comment(index)"></i> -->
<i class="iconfont comment"></i>
</div>
</div>
</div>
</div>
css代码
.picList{
padding: 20px; // 这个会导致this.$refs.pictest.clientWidth多20
width: 95%;
margin: 0px auto;
box-sizing: border-box;
list-style: none;
background-color: #fff;
border-radius: 10px;
.masonry{
// float: left;
position: relative;
overflow: hidden;
border-radius: 10px;
margin: 0;
background-color: rgb(233, 233, 233);
.item{
// float: left;
position: absolute;
max-width: 260px; // 设置最大的width,避免loadmore时太大影响观感
background-color: #fff;
border-radius: 10px;
img{
display: block;
text-align: center;
width: 95%;
// height: auto;
border-radius: 10px;
margin: 10px auto;
cursor: pointer;
}
.picName{
padding-left: 10px;
padding-bottom: 5px;
}
js代码
data部分
data() {
return {
scrollHeight: 0, // 滚动的高度
itemWidth: 0 + 'px', // 每个item 的宽度
showPicList: [], // 获取全部图片的src
piclist: [], // 获取showPicList中部分图片进行显示,之后再从loadmore()中获取更多(下拉加载功能)
bigPic: '',
centerDialogVisible: false,
loadCount: 0
// width: this.$refs.pictest.clientWidth
}
},
methods部分
waterfall() {
var items = this.$refs.pictest.children
// var itemWidth = 270
var columns1 = 4
var arr = []
// 距离左右边缘的距离固定,item的缝隙固定
var gapToTop = 10 // 与顶部之间的距离,避免盒子重合
var gap = 20 // 上下间距
var gapBetween = 10 // 左右item之间的距离
var gapToSide = 10 // 到masonry左、右边缘的距离
// var calcItemWidth = (this.$refs.pictest.clientWidth - 2 * gapToSide - (columns1 - 1) * gapBetween) / 4
console.log(this.$refs.pictest.clientWidth)
// 实现左右对称
// items[0].style.left = gapToSide + 'px'
// items[columns1 - 1].style.right = gapToSide + 'px'
var calcItemWidth = (this.$refs.pictest.clientWidth - 2 * gapToSide - (columns1 - 1) * gapBetween) / 4
this.itemWidth = calcItemWidth + 'px'
for (var i = 0; i < items.length; i++) {
this.$refs.pictest.children[i].style.width = calcItemWidth + 'px'
console.log(calcItemWidth)
// console.log(items[i].offsetHeight, items[i].clientHeight, items[i].offsetWidth, items[i].clientWidth)
if (i < columns1) {
items[i].style.top = gapToTop + 'px'
// 调节第一行偏移量,让整个瀑布流区域居中
// items[i].style.left = (calcItemWidth + gapBetween) * i + gapToSide + 'px'
items[i].style.left = (calcItemWidth + gapBetween) * i + gapToSide + 'px'
// console.log(items[i].style.top)
arr.push(items[i].offsetHeight + gap)
// console.log(items[i].offsetHeight)
} else {
var minHeight = arr[0]
var index = 0
for (var j = 0; j < arr.length; j++) {
if (minHeight > arr[j]) {
minHeight = arr[j]
index = j
}
}
items[i].style.top = arr[index] + gapToTop + 'px'
// console.log(arr[0])
items[i].style.left = items[index].offsetLeft + 'px'
arr[index] = arr[index] + items[i].offsetHeight + gap
}
}
// console.log(Math.max(...arr))
this.$refs.pictest.style.height = Math.max(...arr) + 25 + 'px'
},
// 等图片加载完后执行瀑布流函数
ifLoad() {
this.loadCount++
if (this.loadCount === this.$refs.pictest.children.length) {
console.log(this.loadCount)
this.waterfall()
}
},
// 下拉加载更多
loadMore() {
var piclistlen = this.piclist.length
var showPicListlen = this.showPicList.length
if (showPicListlen - piclistlen >= 4) {
this.piclist.push(...(this.showPicList.slice(piclistlen, piclistlen + 4)))
// this.$refs.pictest.children.map(child => { child.style.width = that.itemWidth })
} else {
this.piclist.push(...(this.showPicList.slice(piclistlen, showPicListlen)))
}
},
mounted部分
mounted() {
// console.log(document.getElementsByClassName('masonry'))
window.addEventListener('resize', () => {
this.waterfall()
})
window.addEventListener('scroll', () => {
console.log(window.scrollY)
var that = this
if (window.scrollY > that.scrollHeight) {
if (window.scrollY - that.scrollHeight >= 100) {
this.loadMore()
this.scrollHeight = window.scrollY
}
}
})
},
补充说明
没有图片素材的同学可以尝试一下下面提到的这个网址,加到img的src就可以了(https://picsum.photos/360/400) 第一个参数360是指图片宽度,第二个参数400是指图片高度,每次都是会随机显示不同图片的,可以用代码把这些src放到一个arr里面,然后v-for循环显示。例子如下:
for (let i = 0; i <= 19; i++) {
const height = Math.floor((Math.random() * 200) + 201)
this.piclist.push('https://picsum.photos/360/' + height + '?random=' + (i + 1))
}
参考连接
https://www.bilibili.com/video/BV1xa4y147JP
https://segmentfault.com/a/1190000012621936(多看这个,这个很重要)
标签:arr,vue,console,实现,items,px,style,瀑布,var 来源: https://blog.csdn.net/wenguangze/article/details/122011400