likes
comments
collection
share

uniapp 小程序如何实现瀑布流布局

作者站长头像
站长
· 阅读数 16

概述:在做小程序论坛主页的时候,常规的排版,已满足不了设计Ui需求,网上又有很多开源的插件,虽然用起来方便,但是不利于自定化,本文只讲纯css样式实现,废话不多说,直接上代码!

实际效果图展示:

基本模板,没调接口数据之前

uniapp 小程序如何实现瀑布流布局

源码解析

: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>
      
      

联调接口之后:

uniapp 小程序如何实现瀑布流布局

接口数据结构

uniapp 小程序如何实现瀑布流布局

完整代码如下:

<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为13为正常的图片
       <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
评论
请登录