uni-app选项卡内容区联动

1,266 阅读1分钟

需求: 实现选项卡与内容区域双向联动

  问题: (1) 点击选项卡页面滚动到对应区域
        (2) 滑动页面选项卡切换到对应Tab
        (3) 选项卡切换与页面滚动双向联动
  解决: (1) 通过
              uni.createSelectorQuery().select('#id').boundingClientRect(data => {
                 console.log(data.top)
              }).exec();获取所需节点距离页面顶部高度, 节点本身高度信息
        (2) 通过 
        	uni.createSelectorQuery().selectViewport('#id').scrollOffset(data => {
                 this.serviceDescScroll = data.scrollTop
            }).exec(); 记录节点滑动距离
        (3) 使用
            uni.pageScrollTo({
                scrollTop: 200, // 移动距离
                duration:0 // 移动距离所需时间
            }) 滚动页面到指定位置
        (4) 在onReady中执行获取所需节点信息的方法
        (5) 在onPageScroll中进行页面滑动切换选项卡的判断
        (6) 在选项卡点击事件中处理页面滑动判断   
     

image.png

<template>
    <view class="animal-container">
    <!-- 导航栏 -->
        <my-custom id="my-custom" :bgImage="require('@/static/shop/shop-top2.png')">
            <block slot="left">
                <view class="status-left">
                    <view class="status-left-content" @click="backTo()">
                        <text class="cuIcon-back"></text>返回
                    </view>
                </view>
            </block>
            <block slot="center">
                <view class="search-form round">
                    <text class="cuIcon-search"></text>
                        <input :adjust-position="false" type="text" placeholder="搜索文章" confirm-type="search" @confirm="search" v-model="searchName"
					 @blur="checkInput"></input>
                </view>
            </block>
            <block slot="right">
                <view class="status-right"></view>
            </block>
        </my-custom>
        <view class="article-box2" v-show="isShow">
            <view class="article-title2">
                <view class="article-title-item" :class="index==TabCur?'text-jianbian':''" v-for="(item, index) in navTitle" :key="index" @click="tabChange(index)">
					{{item.nav}}
                </view>
            </view>
        </view>
        <!-- 轮播图 -->
        <view class="shop-image">
            <swiper class="carousel box" :class="dotStyle?'square-dot':'round-dot'"
            :indicator-dots="true" indicator-active-color="#55aaff" indicator-color="#f1f1f1"
			:autoplay="true" interval="5000" duration="500">
                        <swiper-item v-for="n in 3" :key="n">
                            <view class="carousel-item">
                        <image src="../../static/activity/writingBg.png" mode=""></image>
                            </view>
                        </swiper-item>
                    </swiper>
		</view>
            <view class="article-box">
                <view class="article-title" v-if="navShow">
                    <view class="article-title-item" :class="index==TabCur?'text-jianbian':''" v-for="(item, index) in navTitle" :key="index" @click="tabChange(index)">
                        {{item.nav}}
                    </view>
                </view>
            <view class="article-content">
                <view class="service-desc">
                    <view class="service-desc-item" v-for="n in 60">
                        项目简介{{n}}
                    </view>
                </view>
                <view class="user-know">
                    <view class="user-know-item" v-for="n in 60">
                        用户须知{{n}}
                    </view>
                </view>
                <view class="comment-desc">
                    <view class="comment-desc-item" v-for="n in 60">
                        评论内容{{n}}
                    </view>
                </view>
            </view>
        </view>
    </view>
</template>
<script>
    export default {
        data() {
            return {
                searchName: '',
                dotStyle: true,
                TabCur: 0,
                statusHeight: 0, // 状态栏高度
                navHeight: 0, // 选项卡 高度
                navPageTop: 0, // 选项卡距离顶部高度
                serviceDesc: 0, // 项目简介距离顶部高度
                userKnow: 0, // 用户须知距离顶部高度
                commentDesc: 0, // 评论内容距离顶部高度
                navMobileTop: 0, //选项卡需要移动的距离
                articleBoxHeight: 0, // 隐藏Nav选项卡距离顶部高度
                serviceDescScroll: 0, // 项目简介距离顶部高度
                userKnowScroll: 0, // 用户须知距离顶部高度
                commentDescScroll: 0, // 评论内容距离顶部高度
                lunboHeight: 0, // 轮播图高度
                navShow: true,
                isShow: false,
                navTitle: [
                        {'nav': '项目简介', 'id': 0},
                        {'nav': '用户须知', 'id': 1},
                        {'nav': '评论内容', 'id': 2}
                ]
        };
    },
    onLoad() {
    },
    onShow() {

    },
    onReady() {
            this.getNodeScroll()
    },
    onPageScroll(e) {
        // 隐藏Nav高度
        uni.createSelectorQuery().select('.article-box2').boundingClientRect(data => {
                this.articleBoxHeight = data.height
        }).exec();

        // Nav选项卡距离页面顶部高度
        uni.createSelectorQuery().select('.article-title').boundingClientRect(data => {
                this.navPageTop = data.top
        }).exec();

        // 轮播图高度
        uni.createSelectorQuery().select('.shop-image').boundingClientRect(data => {
                this.lunboHeight = data.height
        }).exec();

        // 项目简介距离顶部高度
        uni.createSelectorQuery().selectViewport('.service-desc').scrollOffset(data => {
                this.serviceDescScroll = data.scrollTop
        }).exec();
        // 用户须知距离顶部高度
        uni.createSelectorQuery().selectViewport('.user-know').scrollOffset(data => {
                this.userKnowScroll = data.scrollTop
        }).exec();
        // 评论内容距离顶部高度
        uni.createSelectorQuery().selectViewport('.comment-desc').scrollOffset(data => {
                this.commentDescScroll = data.scrollTop
        }).exec();
        if (e.scrollTop > this.lunboHeight) {
                this.isShow = true
                // 项目简介上移过程中距离当前可视窗口顶部高度
                const serviceScrollTop = this.serviceDesc - this.serviceDescScroll
                // 用户须知上移过程中距离当前可视窗口顶部高度
                const userKnowScrollTop = this.userKnow - this.userKnowScroll
                // 评论内容上移过程中距离当前可视窗口顶部高度
                const commentDescScrollTop = this.commentDesc - this.commentDescScroll
                // 状态栏加隐藏Nav选项卡高度
                const allHeight1 = this.articleBoxHeight + this.statusHeight
                // 状态栏加Nav选项卡高度
                const allHeight2 = this.navHeight + this.statusHeight

                if (serviceScrollTop < allHeight1 || serviceScrollTop == allHeight1) {
                        // 设置当前选项卡为项目简介
                        this.TabCur = 0
                }

                if (userKnowScrollTop < allHeight1 || userKnowScrollTop == allHeight1) {
                        // 设置当前选项卡为用户须知
                        this.TabCur = 1
                }

                if (commentDescScrollTop < allHeight1 || commentDescScrollTop == allHeight2) {
                        // 设置当前选项卡为评论内容
                        this.TabCur = 2
                }
        } else if(e.scrollTop < this.lunboHeight){
                this.isShow = false
        }
    },
    methods:{
        // 返回上一级
        backTo(){
            uni.navigateBack({
                    delta:1
            })
        },
        // 切换tab
        tabChange(e) {
            this.TabCur = e
            this.navMobileTop = this.navPageTop - this.statusHeight 
            // 项目简介需要上移距离
            const serviceMobileTop = this.serviceDesc - this.statusHeight - this.navHeight
            // 用户须知需要上移距离
            const userKnowMobileTop = this.userKnow - this.statusHeight - this.navHeight
            // 评论内容需要上移距离
            const commentMobileTop = this.commentDesc - this.statusHeight - this.navHeight
            uni.pageScrollTo({
                    scrollTop: this.navMobileTop,
                    duration:0
            })
            this.isShow = true
            if (e == 0){
                uni.pageScrollTo({
                        scrollTop: serviceMobileTop,
                        duration:0
                })
            } else if (e == 1){
                uni.pageScrollTo({
                        scrollTop: userKnowMobileTop,
                        duration:300
                })
            } else if(e == 2){
                uni.pageScrollTo({
                        scrollTop: commentMobileTop,
                        duration:300
                })
            }
        },
        // 获取节点滚动信息
        getNodeScroll(){
            const query = uni.createSelectorQuery().in(this);
            // 项目简介距离顶部高度
            query.select('.service-desc').boundingClientRect(data => {
                    this.serviceDesc = data.top
            }).exec();
            // 用户须知距离顶部高度
            query.select('.user-know').boundingClientRect(data => {
                    this.userKnow = data.top
            }).exec();
            // 评论内容距离顶部高度
            query.select('.comment-desc').boundingClientRect(data => {
                    this.commentDesc = data.top
            }).exec();
            // Nav选项卡高度
            query.select('.article-title').boundingClientRect(data => {
                    this.navHeight = data.height
            }).exec();
            // 状态栏高度
            query.select('#my-custom').boundingClientRect(data => {
                    this.statusHeight = data.height
            }).exec();
        }
    }
}
</script>

<style lang="scss">
.animal-container{
	width: 100%;
	background-color: #F1F1F1;
	.status-left{
		display: flex;
		flex-direction: column;
		.status-left-content{
			display: flex;
			justify-content: center; 
			font-size: 36rpx;
		}
	}
	
	.status-right{
		display: flex;
		flex-direction: column;
		
		.status-right-content{
			display: flex;
			justify-content: center;
			
			image{
				width: 40rpx;
				height: 40rpx;
			}
		}
	}
	
	.shop-image{
		width: 100%;
		height: 360rpx;
		padding-top: 0;
		/* position: absolute;
		top: 0;
		left: 0; */
		
		.carousel{
			width: 100%;
			height: 100%;
			
			::v-deep .uni-swiper-wrapper{
				.uni-swiper-dots{
					bottom: 60rpx;
					.uni-swiper-dot-active{
						
					}
					.uni-swiper-dot{
					}
				}
			}
			
			.carousel-item{
				width: 100%;
				height: 100%;
				
				image{
					width: 100%;
					height: 100%;
				}
			}
		}
		
	}

	
	.article-box2{
		width: 100%;
		position: fixed;
		z-index: 999;
		.article-title2{
			width: 100%;
			background-color: #FFFFFF;
			display: flex;
			justify-content: space-around;
			.article-title-item{
				font-size: 36rpx;
				line-height: 80rpx;
			}
		}
	}
	
	.article-box{
		width: 100%;
		background-color: #1CBBB4;
		
		.article-title{
			width: 100%;
			background-color: #FFFFFF;
			display: flex;
			justify-content: space-around;
			.article-title-item{
				font-size: 36rpx;
				line-height: 80rpx;
			}
		}
		
		.article-title-fixed{
			position: fixed;
			z-index: 999999;
			/* top: 93px; */
			left: 0;
		}
		
		.article-content{
			width: 100%;
			.service-desc{
				width: 94%;
				margin: 20rpx 3%;
				background-color: #4399FC;
			}
			.user-know{
				width: 94%;
				margin: 20rpx 3%;
				background-color: #ffaa7f;
			}
			.comment-desc{
				width: 94%;
				margin: 20rpx 3%;
				background-color: #55ff7f;
			}
		}
	}
}
</style>