前言:笔者一名业余的码农,只是爱好而已。刚从html转到uni-app这边,vue也是第一次写。
效果:
在这里插入图片描述

像这个可以左右切换的长列表,上面的tab倒是很好实现,直接使用官方的scroll-view组件就可以了。
但是下方的长列表,要实现并且保证高性能,可不是一件容易事。

下方内容列表一开始,我是考虑使用swiper组件的,但是swiper组件的高度始终是固定的,即便你动态修改了。但依旧是固定的高度,以后内容变化的话,都得重新获取高度。所以排除掉

scroll-view组件的话,官方文档早就说明不适合做长列表了。
看来就剩下插件市场的list组件了,但是我没用过,但是我可以预见的是,list组件,扩展性低,复杂(相较于我这种第一次写vue的),性能也就那样,至少在HTML端的性能是不会很好的

所以就自己写一个,原理就是直接使用view,父view设置css属性为display: inline-flex;。大致如下图
在这里插入图片描述
然后切换使用css平移动画 transform:translateX(0); 性能非常的好。缺点就是要实现手势左右滑动进行切换得自己写js,不过也不难。

另外上面的tab。那条橙色的线,位置和宽度是会变化的,一开始使用的是left 和 width,来实现。然后发现在列表内容很多的情况下,性能很差,所以就改成了css的平移和缩放动画,来实现偏移和宽度变化(性能很好)。
上面tab被点击之后也会居中显示

下面是具体代码

根目录中创建components文件夹(components是自定义组件的存放地),components文件夹中创建list.vue文件,文件内容如下

<template><view>
<view style="background:#FFFFFF;">
	<scroll-view class="navList" scroll-x="true" scroll-with-animation="true" :scroll-left="setNavScrollLeft">
		<text class="text" v-for="(item, index) in nav" :class="{'active':navListIndex === index}" @click="navClick(index)">
			<text class="span">{{item}}</text>
			<text v-if="index === 0" class="line" :style="{'transform':lineTransform,'transition-duration':line_transitionDuration}"></text>
		</text>
	</scroll-view>
</view>

<view class="content" :style="{'transform':contentTransform}">
	<view v-for="(item, index) in nav" :key="index" class="goodsList">
		<view class="li">
			<image class="zutu" src="//img.alicdn.com/bao/uploaded/i2/3339093752/O1CN01zDfREI1daShv15vyU_!!0-item_pic.jpg" mode="scaleToFill"></image>
			{{index}}1
		</view>
		<view class="li">
			<image class="zutu" src="//img.alicdn.com/bao/uploaded/i2/3339093752/O1CN01zDfREI1daShv15vyU_!!0-item_pic.jpg" mode=""></image>
			{{index}}2
		</view>
		<view class="li"><image class="zutu" src="//img.alicdn.com/bao/uploaded/i2/3339093752/O1CN01zDfREI1daShv15vyU_!!0-item_pic.jpg" mode=""></image>3</view>
		<view class="li"><image class="zutu" src="//img.alicdn.com/bao/uploaded/i2/3339093752/O1CN01zDfREI1daShv15vyU_!!0-item_pic.jpg" mode=""></image>4</view>
		<view class="li"><image class="zutu" src="//img.alicdn.com/bao/uploaded/i2/3339093752/O1CN01zDfREI1daShv15vyU_!!0-item_pic.jpg" mode=""></image>5</view>
		<view class="li"><image class="zutu" src="//img.alicdn.com/bao/uploaded/i2/3339093752/O1CN01zDfREI1daShv15vyU_!!0-item_pic.jpg" mode=""></image>6</view>
		<view class="li"><image class="zutu" src="//img.alicdn.com/bao/uploaded/i2/3339093752/O1CN01zDfREI1daShv15vyU_!!0-item_pic.jpg" mode=""></image>7</view>
		<view class="li"><image class="zutu" src="//img.alicdn.com/bao/uploaded/i2/3339093752/O1CN01zDfREI1daShv15vyU_!!0-item_pic.jpg" mode=""></image>8</view>
		<view class="li"><image class="zutu" src="//img.alicdn.com/bao/uploaded/i2/3339093752/O1CN01zDfREI1daShv15vyU_!!0-item_pic.jpg" mode=""></image>9</view>
		<view class="li"><image class="zutu" src="//img.alicdn.com/bao/uploaded/i2/3339093752/O1CN01zDfREI1daShv15vyU_!!0-item_pic.jpg" mode=""></image>10</view>
		<view class="li"><image class="zutu" src="//img.alicdn.com/bao/uploaded/i2/3339093752/O1CN01zDfREI1daShv15vyU_!!0-item_pic.jpg" mode=""></image>11</view>
		<view class="li"><image class="zutu" src="//img.alicdn.com/bao/uploaded/i2/3339093752/O1CN01zDfREI1daShv15vyU_!!0-item_pic.jpg" mode=""></image>12</view>
		<view class="li"><image class="zutu" src="//img.alicdn.com/bao/uploaded/i2/3339093752/O1CN01zDfREI1daShv15vyU_!!0-item_pic.jpg" mode=""></image>13</view>
		<view class="li"><image class="zutu" src="//img.alicdn.com/bao/uploaded/i2/3339093752/O1CN01zDfREI1daShv15vyU_!!0-item_pic.jpg" mode=""></image>14</view>
		<view class="li"><image class="zutu" src="//img.alicdn.com/bao/uploaded/i2/3339093752/O1CN01zDfREI1daShv15vyU_!!0-item_pic.jpg" mode=""></image>15</view>
	</view>
</view>
</view></template>
<script>
	export default {
		props:{
			nav:{type:Array,default:[]},
		},
		data() {
			return {
				lineTransform:'',//线条
				setNavScrollLeft:0,//初始化的位置,可以动态调节
				navListIndex:0,//默认第几个
				line_transitionDuration:'0ms',//过渡时间
				navWidth:0,//滚动条视口的宽度
				
				contentTransform:'translateX(0)',//当前所在滑块的 index
			};
		},
		mounted(){//该组件被挂载到实例上去之后调用
			//初始化 线 显示的位置	transition-duration: 400ms;
				uni.createSelectorQuery().in(this).select(".navList .text.active .span").boundingClientRect((data) => {
					this.lineTransform = 'translateX('+(data.left+(data.width-19)/2)+'px) scaleX('+(data.width/20)+')';
					//偏移值=第一个分类相对于视口的水平位置+(第一个分类的宽度-19)/2		除以2的原因是后面的缩放是沿着左右两边进行缩放的,所以除以2可以让其居中
					//减19是因为默认宽度就是20,需要减到1px之后才进行结算,那么问题就来了,为什么不在直接使用1px呢?因为使用1px,需要缩放很多倍,在移动端,进行多倍的缩放之后,宽度是不对的,所以设置20px,就不需要进行很多倍的缩放,没有css兼容问题
					setTimeout(() => {
						this.line_transitionDuration = '400ms';
					}, 50);
				}).exec();
			//nav的可视宽度【不包含margin】
				uni.createSelectorQuery().in(this).select(".navList").boundingClientRect((data) => {
					this.navWidth = data.width;
				}).exec();
		},
		methods: {
			navClick(index){
				this.navListIndex = index;
				this.contentTransform = 'translateX(-'+index+'00vw)';
				let left = 0,
					avtiveNavWidth = 0;//当前nav中被选择的分类的宽度
				//滚动条已滚动的水平长度
					uni.createSelectorQuery().in(this).select(".navList").scrollOffset((data) => {
						left = data.scrollLeft;
					}).exec();
				//偏移(元素的相对于视口的定位 + 滚动条横向的位置) and 缩放(实现宽度变化)
					uni.createSelectorQuery().in(this).select(".navList .text:nth-of-type("+(index+1)+") .span").boundingClientRect((data) => {
						left += data.left;
						this.lineTransform = 'translateX('+(left+(data.width-19)/2)+'px) scaleX('+(data.width/20)+')';
						left -= (data.width/2);
						avtiveNavWidth = data.width;
					}).exec();
				//居中
				//计算nav的margin-left,27是他的“27rpx”【【居中时,要减去这部分】】
					uni.getSystemInfo({
						success: (res) => {
							this.setNavScrollLeft = left - Math.round(res.windowWidth*(27/750)) - (this.navWidth / 2) + avtiveNavWidth;
						}
					});
			},
		},
	}
</script>
<style>
	.content{
		display: inline-flex;
		transition-duration:500ms;
	}
	.goodsList{
		width: 750rpx;
		box-sizing:border-box;
		padding: 20rpx;
	}
	.goodsList .li{
		display:inline-block;
		background:#FFF;
		width:347rpx;
		margin-bottom: 15rpx;
		border-radius:14rpx;
		overflow:hidden;
	}
	.goodsList .li:nth-child(even){
		margin-left: 20rpx;
	}
	.goodsList .li .zutu{
		width:100%;
		height:347rpx;
	}
	
	
	.navList{
		Position:relative;
		background:#FFFFFF;
		font-size: 28rpx;
		width:696rpx;
		margin:0 27rpx;
		box-shadow: inset -31rpx 0 31rpx -31rpx rgba(0,0,0,0.15);
		line-height:66rpx;
		white-space: nowrap;
	}
	.navList .text{
		padding-left:30rpx;
		color: #333;
	}
	.navList .text:nth-of-type(1){
		padding-left:0;
	}
	.navList .text:nth-last-child(1){
	    padding-right: 19rpx;
	}
	.navList .text.active{
		color:#FF5500;
	}
	.navList .line{
		background: #ff5500;
		Position: absolute;
		left: -27rpx;
		bottom: 4rpx;
		width:20px;
		height: 4rpx;
	}
</style>

然后在你需要的地方引入这个文件

<script>
import list from '@/components/list.vue'
export default {
	components:{//注册组件
		list
	},
    data() {
        return {
			nav:['精选','女装','家居数码','数码家电数码家电','食品','母婴','鞋包配饰','美妆个护','男装','内衣','运动个护','淘宝','京东'],
        }
    },
}
</script>

调用组件

<list :nav="nav"></list>

性能没问题,在13个分类列表里面,每个列表中创建93个商品,切换过程没有一点点卡顿

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐