纯css
这是两列外层的盒子作为主控。(ps:他是先加载一列在加载一列,如果有分页也是如此,体验效果不好,如果没数字显示,没分页加载影响不大)
.box {
width: 100%;
-moz-column-count: 4;
/* Firefox */
-webkit-column-count: 4;
/* Safari 和 Chrome */
column-count: 2;//分为几列
-moz-column-gap: 1em;
-webkit-column-gap: 1em;
column-gap: 5px;
}
绝对定位
用vue和vant的list和图片懒加载加预览
css
@keyframes data-item-ani {
0% {
transform: scale(0.5);
}
100% {
transform: scale(1);
}
}
.data-list-box {
position: relative;
min-height: 100vh;
}
.img-div {
position: absolute;
/*width: 100%;*/
overflow: auto;
/*break-inside: avoid;*/
margin-bottom: 2vw;
animation: data-item-ani 0.4s;
transition: left 0.6s, top 0.6s;
transition-delay: 0.1s;
}
.img {
width: 100%;
}
.finished-text1 {
width: 100%;
color: #969799;
font-size: 14px;
line-height: 50px;
text-align: center;
}
.album-user {
width: 100%;
padding: 1%;
display: -webkit-flex;
display: flex;
}
.album-user-head {
width: 10vw;
height: 10vw;
border-radius: 50%;
}
.album-user-name {
line-height: 5vw;
display: inline-block;
margin-left: 2%;
}
.box2 {
width: 100%;
height: 100%;
}
.video-div {
width: 96%;
margin-left: 2%;
margin-bottom: 2vw;
}
.video-div video {
width: 100%;
height: 30vw;
}
.van-sticky--fixed {
width: 100%;
}
.data-list-box {
min-height: 50vh;
}
.album-user-name {
color: #646566;
font-size: 0.12rem;
}
.img-div {
display: none;
}
html
<div id="app">
<nav-back></nav-back>
<van-tabs v-model="activeName" @click="onTabClick" sticky>
<van-tab title="相册" name="1">
<div class="box2">
<van-pull-refresh v-model="loading" @refresh="onRefresh">
<van-list
v-model="loading"
:error.sync="error"
loading-text="加载中"
error-text="请求失败,点击重新加载"
:finished="finished"
@load="getImgList"
finished-text="没有更多了"
:offset='90'
>
<div class="data-list-box" id="data-list-box">
<div v-for="(item,indexImg) in imgList" :key="indexImg" class="img-div" :style="{width: boxWidth + 'px'}" :data-id="indexImg">
<img class="img" v-lazy="item.img" @click="onPreviewAvatar(item.img,indexImg)"
:style="{width: '100%', height: item.imgHeight + 'px'}">
<div class="album-user">
<img :src="item.head" class="album-user-head">
<span class="album-user-name">
{{item.name}}
<br/>
{{item.time}}
</span>
</div>
</div>
</div>
</van-list>
</van-pull-refresh>
</div>
</van-tab>
<van-tab title="视频" name="2">
<div class="box2">
<van-pull-refresh v-model="loading2" @refresh="onRefresh2">
<van-list
v-model="loading2"
:finished="finished2"
@load="getVideoList"
finished-text="没有更多了"
:offset='30'
>
<div class="video-div" v-for="(itemVideo,indexVideo) in videoList" :key="indexVideo">
<video controls="" muted="muted" oncontextmenu="return false;" controlslist="nodownload"
:poster="itemVideo.img" style="width: 100%; height:100%; object-fit: fill">
<source :src="itemVideo.video" type="video/mp4">
</video>
<div class="album-user">
<img :src="itemVideo.head" class="album-user-head">
<span class="album-user-name">
{{itemVideo.name}}
<br/>
{{itemVideo.time}}
</span>
</div>
</div>
</van-list>
</van-pull-refresh>
</div>
</van-tab>
</van-tabs>
<!-- 返回顶部-->
<button-icon></button-icon>
</div>
js
Vue.use(vant.Lazyload, {
lazyComponent: true
});
new Vue({
el: '#app',
data: {
id: "{$id}",
Page: 1,
pageSize: 20, //每页加载数据数量
imgList: [],
error: false,
loading: false,
finished: false,
Page2: 1,
videoList: [],
loading2: false,
finished2: false,
imagePreview: {
images: []
},
activeName: '1',
itemCount: 0, //上一次加载完成后的瀑布流item个数
lastRowHeights: [0, 0], //最后一行标签的顶部间距+高度,2列
boxMargin: 15, //每个item之间的边距
boxWidth: 165 //每个item宽度px
},
created() {
//当前瀑布流设置为两列,计算瀑布流每个item和图片的宽度
let screenWidth = document.body.offsetWidth; //屏幕宽度
this.boxWidth = (screenWidth - this.boxMargin * 3) / 2; //每个item的宽度
},
methods: {
// 顶部标签点击事件
onTabClick(name) {
if (name == 1) {
this.onRefresh();
} else {
this.onRefresh2();
}
},
getImgList() {
let that = this;
this.$http.post('{:url("xxxxxx")}', {"page": that['Page'], "id": that['id']}, {
before: function () {
that['loading'] = true;
}
}).then(function (res) {
if (res.body.code === 1) {
var resList = res.body.data['lists'];
that['Page'] += 1;
// that['imgList'] = that['imgList'].concat(resList);
resList.forEach(function (item) {
that['imagePreview']['images'] = that['imagePreview']['images'].concat(item.img);
});
if (res.body.data['finished'] === 1) {
that['finished'] = true;
}
} else {
that['finished'] = true;
vant.Toast.fail(res.body['msg']);
}
that['loading'] = false;
this.loadImagesHeight(resList); //模拟预加载图片,获取图片高度
}).catch((res) => {
that['loading'] = false;
that['finished'] = true;
vant.Toast.fail('发生错误!');
});
},
getVideoList() {
let that = this;
this.$http.post('{:url("xxxxxx")}', {"page": that['Page2'], "id": that['id']}, {
before: function () {
that['loading2'] = true;
}
}).then(function (res) {
if (res.body.code === 1) {
var resList = res.body.data['lists'];
that['Page2'] += 1;
that['videoList'] = that['videoList'].concat(resList);
if (res.body.data['finished'] === 1) {
that['finished2'] = true;
}
} else {
that['finished2'] = true;
vant.Toast.fail(res.body['msg']);
}
that['loading2'] = false;
}).catch((res) => {
that['loading2'] = false;
that['finished2'] = true;
vant.Toast.fail('发生错误!');
});
},
onRefresh() {
this.Page = 1;
this.imgList = [];
this['imagePreview']['images'] = [];
this.itemCount = 0; //上一次加载完成后的瀑布流item个数
this.lastRowHeights = [0, 0]; //最后一行标签的顶部间距+高度,2列
this.finished = false;
this.loading = true;
this.getImgList();
},
onRefresh2() {
this.Page2 = 1;
this.videoList = [];
this.finished2 = false;
this.loading2 = true;
this.getVideoList();
},
//图片预览
onPreviewAvatar(images, startPosition) {
vant.ImagePreview({
'images': this['imagePreview']['images'],
startPosition: startPosition,
closeable: true
});
},
onChange(index) {
this.imagePreview.index = index + 1;
},
//瀑布流
loadImagesHeight(list) {
var count = 0; //用来计数,表示是否所有图片高度已经获取
list.forEach((item, index) => {
//创建图片对象,加载图片,计算图片高度
var img = new Image();
img.setAttribute('crossOrigin', 'anonymous');
img.src = item.img;
img.onload = img.onerror = (e) => {
count++;
if (e.type == 'load') { //图片加载成功
//计算图片缩放后的高度:图片原高度/原宽度 = 缩放后高度/缩放后宽度
list[index].imgHeight = Math.round(img.height * this.boxWidth / img.width);
} else { //图片加载失败,给一个默认高度50
list[index].imgHeight = 50;
console.log("index: ", index, ", 加载报错:", e);
}
//加载完成最后一个图片高度,开始下一步数据处理
if (count == list.length) {
this.resolveDataList(list);
}
};
});
},
resolveDataList(list) { //处理数据
//合并新老两个数组数据
this.imgList = this.imgList.concat(list);
//判断页面是否有数据
// this.haveData = this.imgList.length > 0 ? 2 : 1;
// this.loading = false; //上拉加载更多请求完成
this.$nextTick(() => {
//渲染完成,计算每个item宽高,设置标签坐标定位
if (this.page == 1) {
this.setItemElementPosition();
} else {
setTimeout(() => {
//渲染完成,计算每个item宽高,设置标签坐标定位
this.setItemElementPosition();
}, 1000);
}
});
},
//获取每个item标签高度,设置item的定位
setItemElementPosition() {
let parentEle = document.getElementById('data-list-box');
let boxEles = parentEle.getElementsByClassName("img-div");
for (let i = this.itemCount; i < (boxEles.length); i++) {
let tempEle = boxEles[i];
tempEle.style.display = "block";
//上一个标签最小高度的列索引
let curColIndex = this.getMinHeightIndex(this.lastRowHeights);
let boxTop = this.lastRowHeights[curColIndex] + this.boxMargin;
let boxLeft = curColIndex * (this.boxWidth + this.boxMargin) + this.boxMargin;
tempEle.style.left = boxLeft + 'px';
tempEle.style.top = boxTop + 'px';
this.lastRowHeights[curColIndex] = boxTop + tempEle.offsetHeight;
// console.log('i = ', i, ', lastRowHeights: ', this.lastRowHeights[curColIndex], ', boxTop: ', boxTop, ', eleHeight: ', tempEle.offsetHeight);
}
this.itemCount = boxEles.length;
//修改父级标签的高度
let maxHeight = Math.max.apply(null, this.lastRowHeights);
parentEle.style.height = maxHeight + 'px';
this.$toast.clear();
// console.log("...boxEles: ", boxEles.length, ", maxH: ", maxHeight, ", loading: ", this.loading, ", finished: ", this.finished);
},
//获取数组中最小值的索引
getMinHeightIndex(arr) {
var minHeight = Math.min.apply(null, arr);
for (let i = 0; i < arr.length; i++) {
if (arr[i] == minHeight) {
return i;
}
}
}
}
});