uniapp 小程序如何实现瀑布流布局
概述:在做小程序论坛主页的时候,常规的排版,已满足不了设计Ui需求,网上又有很多开源的插件,虽然用起来方便,但是不利于自定化,本文只讲纯css样式实现,废话不多说,直接上代码!
实际效果图展示:
基本模板,没调接口数据之前
源码解析
:style="{ '--layout-width': 100 / flowData.column - flowData.columnSpace + '%' }"
flowData.column: 分的列数,本文是2列
flowData.columnSpace: 瀑布流列宽距
这样就能让两列之间形成间距空隙
循环分成的列数
v-for="(item, j) in flowData[`column_${index + 1}`]"
基本模板,完整代码如下:
<template>
<view class="list-container">
<view class="my-title">我的帖子({{postList.length}})</view>
<!-- <navigator :url="'/subpages/content/article/article?id=' + item.id" class="item" v-for="(item, index) in postList" :key="index" hover-class="none"> -->
<view class="container">
<view
class="cont-box"
:style="{ '--layout-width': 100 / flowData.column - flowData.columnSpace + '%' }"
v-for="(numVal, index) in flowData.column"
:key="numVal"
>
<view class="item-box" v-for="(item, j) in flowData[`column_${index + 1}`]" :key="j">
<image class="img-tip" :src="item.imgUrl" mode="widthFix" lazy-load />
<view class="tit-tip multi-line-omit">{{ item.title }}</view>
<view class="desc-tip multi-line-omit">{{ item.desc }}</view>
</view>
</view>
</view>
<!-- </navigator> -->
</view>
</template>
<script>
export default {
data() {
return {
postList: [],
flowData: {
list: [], // 数据值
column: 2, // 瀑布列数
columnSpace: 2 // 瀑布列宽间距
}
};
},
created() {
/* 初始化每一列的数据 */
for (let i = 1; i <= this.flowData.column; i++) {
this.$set(this.flowData, `column_${i}`, []);
}
/* 数据赋值 */
this.flowData.list = [
{
imgUrl: "https://www.logosc.cn/uploads/resources/2023/03/17/1679045108_thumb.jpg",
title: "自动驾驶汽车对交通和城市规划的未来影响与挑战",
desc: "分析自动驾驶汽车对未来交通和城市规划的潜在影响,探讨相关挑战。"
},
{
imgUrl: "https://www.logosc.cn/uploads/resources/2023/03/17/1679044581_thumb.jpg",
title: "可持续城市发展:构建环保城市的策略和实践",
desc: "分析建设可持续城市的战略和实际方法,强调环保、资源利用和城市规划的重要性。"
},
{
imgUrl: "https://www.logosc.cn/uploads/resources/2023/03/17/1679045190_thumb.jpg",
title: "消灭传染病:全球卫生领域的挑战与创新",
desc: "探讨在全球范围内消灭传染病的挑战,突出卫生领域的创新方法。"
},
{
imgUrl: "https://www.logosc.cn/uploads/resources/2023/03/17/1679044667_thumb.jpg",
title: "人工智能与机器学习:颠覆性技术对未来的巨大影响",
desc: "探讨人工智能和机器学习如何在多个领域引发革命性变革,从工业到医疗,对未来产生深远影响。"
},
{
imgUrl: "https://www.logosc.cn/uploads/resources/2023/03/17/1679044562_thumb.jpg",
title: "生命科学的新前沿:基因编辑和生物技术的伦理挑战",
desc: "研究生命科学领域的最新发展,聚焦基因编辑和生物技术的伦理考量,探讨科技前沿的道德挑战。"
}
];
this.$nextTick(() => {
this.initData(); // 数据初始化
});
},
methods: {
/* 数据初始化 */
initData() {
const groupList = this.dynamicGrouping(this.flowData.list, this.flowData.column); // 数据动态分组
groupList.forEach((item, i) => (this.flowData[`column_${i + 1}`] = item));
},
/** 数据动态分组
* @param {Object} data 分组的数据列表
* @param {Object} i 需要分几组
* @returns {Array} groups 已分组的数据
*/
dynamicGrouping(data, i) {
if (i <= 0) return "分组数必须大于0";
const groups = [];
for (let j = 0; j < i; j++) {
groups.push([]);
}
for (let k = 0; k < data.length; k++) {
const groupIndex = k % i;
groups[groupIndex].push(data[k]);
}
return groups;
}
}
};
</script>
<style lang="scss" scoped>
.container {
display: flex;
justify-content: space-between;
padding: 20rpx;
$borderRadius: 12rpx;
.cont-box {
width: var(--layout-width);
.item-box {
width: 100%;
padding-bottom: 20rpx;
margin-bottom: 30rpx;
border-radius: $borderRadius;
box-shadow: 0rpx 3rpx 6rpx rgba(0, 46, 37, 0.08);
.img-tip {
width: 100%;
border-radius: $borderRadius $borderRadius 0 0;
}
.tit-tip {
text-align: justify;
font-size: 30rpx;
padding: 10rpx 20rpx 0;
font-weight: 900;
}
.desc-tip {
text-align: justify;
font-size: 26rpx;
padding: 5rpx 20rpx 0;
margin-top: 10rpx;
}
}
}
}
/* 多行省略 */
.multi-line-omit {
word-break: break-all; // 允许单词内自动换行,如果一个单词很长的话
text-overflow: ellipsis; // 溢出用省略号显示
overflow: hidden; // 超出的文本隐藏
display: -webkit-box; // 作为弹性伸缩盒子模型显示
-webkit-line-clamp: 2; // 显示的行
-webkit-box-orient: vertical; // 设置伸缩盒子的子元素排列方式--从上到下垂直排列
}
/* 单行省略 */
.one-line-omit {
width: 100%; // 宽度100%:1vw等于视口宽度的1%;1vh等于视口高度的1%
white-space: nowrap; // 溢出不换行
overflow: hidden; // 超出的文本隐藏
text-overflow: ellipsis; // 溢出用省略号显示
}
</style>
联调接口之后:
接口数据结构
完整代码如下:
<view class="container ">
<view class="cont-box":style="{ '--layout-width': 100 / dataList.column - dataList.columnSpace + '%' }" v-for="(numVal, index) in dataList.column" :key="numVal" :class="`box-style` + index">
<view class="item-box" v-for="(item, j) in dataList[`column_${index + 1}`]" :key="j">
<view class="post-item" @click="jump(dataList[`column_${index + 1}`][j])">
// type为1和3为正常的图片
<image v-if="item.type == 1 || item.type == 3" :src="item.media[0]" mode="aspectFill" class="basic-img"></image>
// 4为默认写死的图片
<image v-if="item.type == 4" src="/static/images/vote-cover.png" mode="aspectFill"class="basic-img"></image>
// 2为视频
<view v-if="item.type == 2">
<view class="video-wrap">
// 截取视频的某一帧,作为图片封面
<image class="cover-img" mode="aspectFill" :src="item.media[0] + '?x-oss-process=video/snapshot,t_0,f_jpg'"></image>
// 播放的图标
<image class="icon" src="/static/play.png"></image>
</view>
</view>
<view class="img-content">
<view class="title" v-if="item.type != 4">
<text>{{ item.title}}</text>
</view>
<view class="title" v-else>
<text>{{ item.content}}</text>
</view>
<view style="display: flex;justify-content: space-between;">
<view class="userBox" @click.stop="toUcenter(dataList[`column_${index + 1}`][j], j)">
<image style="width: 42rpx;height: 42rpx;border-radius: 21rpx;" :src="item.userInfo.avatar" mode=""></image>
<text class="username">
{{ item.userInfo.username.substring(0, 9) }}
</text>
</view>
<view style="display: flex;align-items: center;" v-if="item.isCollection" @click.stop="cancelCollection(dataList[`column_${index + 1}`][j],`column_${index + 1}`, j)">
<u-icon name="heart-fill" color="#FF7171"></u-icon>
<view class="heart-count">
{{ item.collectionCount }}
</view>
</view>
<view style="display: flex;align-items: center;" v-if="!item.isCollection" @click.stop="addCollection(dataList[`column_${index + 1}`][j], `column_${index + 1}`, j)">
<u-icon name="heart" color="#666666"></u-icon>
<view class="heart-count">
{{ item.collectionCount }}
</view>
</view>
</view>
</view>
</view>
</view>
</view>
</view>
<script>
export default {
name: 'post-list-twice',
props: {
optionsData: Object,
currentTabs: String,
tittle: String,
loadingFlag: Boolean,
},
data() {
return {
page: 1,
pageType: '',
$IMG: this.$IMG,
postType: '',
dataList: {
list: [], // 数据值
column: 2, // 瀑布列数
columnSpace: 2 // 瀑布列宽间距
},
uid: '',
myUid: '',
isMySelf: '',
last_page: 0
}
},
created(){
this.getMypostlist()
},
methods: {
getMypostlist(type, pageType, operateType, isMySelf, uid, myUid) {
for (let i = 1; i <= this.dataList.column; i++) {
this.$set(this.dataList, `column_${i}`, []);
}
this.getPostList();
},
getPostListByType() {
// 帖子下某一类型列表
// type 1图文 2视频 3文章 4投票
this.$H.get('post/getPostListByType', {
page: this.page,
type: this.postType
}).then(res => {
this.last_page = res.result.last_page
this.dataList.list = this.dataList.list.concat(res.result.data);
this.$nextTick(() => {
this.initData(); // 数据初始化
});
})
},
/* 数据初始化 */
initData() {
const groupList = this.dynamicGrouping(this.dataList.list, this.dataList.column); // 数据动态分组
groupList.forEach((item, i) => (this.dataList[`column_${i + 1}`] = item));
},
/** 数据动态分组
* @param {Object} data 分组的数据列表
* @param {Object} i 需要分几组
* @returns {Array} groups 已分组的数据
*/
dynamicGrouping(data, i) {
if (i <= 0) return "分组数必须大于0";
const groups = [];
for (let j = 0; j < i; j++) {
groups.push([]);
}
for (let k = 0; k < data.length; k++) {
const groupIndex = k % i;
groups[groupIndex].push(data[k]);
}
return groups;
},
cancelCollection(data, index, current) {
// let that = this
if (!uni.getStorageSync('hasLogin')) {
setTimeout(() => {
uni.showModal({
title: '提示',
content: '您还未登录,登录体验更多精彩',
confirmText: '去登录',
cancelText: '随便逛逛',
success: (res) => {
if (res.confirm) {
this.$refs.phoneLogin.getShow()
} else if (res.cancel) {
console.log('用户点击取消');
}
}
});
}, 500)
} else {
this.$H
.post('post/cancelCollection', {
id: data.id
})
.then(res => {
if (res.code === 0) {
this.dataList[index][current].isCollection = false;
this.dataList[index][current].collectionCount--;
}
});
}
},
}
}
</script>
<style lang="scss" scoped>
.main-index {
margin-bottom: 40rpx;
}
.box-style1 {
.basic-img {
height: 440rpx!important;
}
.cover-img {
height: 440rpx!important;
}
}
.post-list {
padding: 0 15rpx;
.post-item {
background: #fff;
width: 355rpx;
margin: 11rpx 0;
box-sizing: border-box;
border-radius: 20rpx 20rpx 20rpx 20rpx;
box-shadow: 0rpx 2rpx 6rpx 0rpx rgba(0,0,0,0.25);
image {
border-radius: 20rpx 20rpx 20rpx 20rpx;
}
.basic-img {
width: 100%;
display: block;
height: c(207);
border-radius: 20rpx 20rpx 20rpx 20rpx;
}
.img-content {
padding: 20rpx;
.title {
color: #333333;
font-size: 24rpx;
line-height: 28rpx;
height: 70rpx;
}
}
.avatar {
width: 28rpx;
height: 28rpx;
flex: 0 0 28rpx;
border-radius: 50%;
margin-right: 20rpx;
}
.username {
font-size: 20rpx;
font-weight: 600;
margin-left: 10rpx;
max-width: 140rpx;
white-space: nowrap;
overflow: hidden;
color: #666666;
text-overflow: ellipsis;
}
.heart-count {
color: #666666;
font-size: 24rpx;
margin-left: 10rpx;
}
}
}
.userBox {
display: flex;
align-items: center;
.username {
height: 27rpx;
line-height: 27rpx;
}
}
.video-wrap {
display: flex;
justify-content: center;
align-items: center;
position: relative;
width: 100%;
height: 440rpx;
border-radius: 13rpx 13rpx 0 0;
image {
position: absolute;
}
.icon {
width: 100rpx;
height: 100rpx;
z-index: 999;
}
}
.cover-img {
height: 100%;
width: 100%;
border-radius: 20rpx;
}
.main-post {
height: 100%;
padding: 20rpx 40rpx;
.post-list {
padding: 0;
.post-item {
width: 327rpx;
margin-bottom: 22rpx;
}
}
}
.main-index {
.container {
padding: 0 20rpx 20rpx 20rpx;
.cont-box {
width: 346rpx;
}
}
}
.main-post {
.container {
padding: 0;
.cont-box {
width: 329rpx;
}
}
}
.container {
display: flex;
justify-content: space-between;
$borderRadius: 12rpx;
.cont-box {
.item-box {
width: 100%;
margin-bottom: 22rpx;
border-radius: $borderRadius;
box-shadow: 0rpx 3rpx 6rpx rgba(0, 46, 37, 0.08);
.basic-img {
width: 100%;
display: block;
height: c(207);
border-radius: 20rpx 20rpx 20rpx 20rpx;
}
.img-content {
padding: 16rpx 20rpx 20rpx 20rpx;
.title {
color: #333333;
font-size: 24rpx;
line-height: 28rpx;
margin-bottom: 20rpx;
}
}
.avatar {
width: 28rpx;
height: 28rpx;
flex: 0 0 28rpx;
border-radius: 50%;
margin-right: 20rpx;
}
.username {
font-size: 20rpx;
font-weight: 600;
margin-left: 10rpx;
max-width: 140rpx;
white-space: nowrap;
overflow: hidden;
color: #666666;
text-overflow: ellipsis;
}
.heart-count {
color: #666666;
font-size: 24rpx;
margin-left: 10rpx;
}
}
}
}
</style>
END...
转载自:https://juejin.cn/post/7399433696653180947