uniapp+vue3+ts --微信小程序tab导航可以上下滚动选中选项组件代码

news2024/9/28 15:30:34

uniapp+vue3+ts --微信小程序tab导航可以上下滚动选中选项组件代码

废话不多说,直接上代码。

组件代码:

<template>
    <view class="scroll-tabs-container">
        <view class="radiusz bg-white pt-[10rpx] z-[999]" :class="{ 'scroll-tabs-sticky': sticky }">
            <u-tabs
                id="tabScrollTop"
                ref="tabScrollTop"
                :list="tabs"
                :current="active"
                @change="handleChangeTab"
                :active-color="mainColor"
                bg-color="transparent"
                :bar-width="90"
                font-size="24"
                :gutter="26"
                name="title"
            >
            </u-tabs>
        </view>
        <view class="content">
            <view
                class="px-[20rpx] pt-[20rpx] pb-[40rpx] w-full bg-white mb-[30rpx] box-border"
                v-for="(item, index) in tabs"
                :key="item.id"
                :class="'tabs' + index"
            >
                <view v-if="item.is_show == 1">
                    <view class="text-center"
                        ><text class="pr-[10rpx]">———</text>{{ item.title
                        }}<text class="pl-[10rpx]">———</text></view
                    >
                    <view class="mt-[40rpx]">
                        <u-parse :html="item.description"></u-parse>
                    </view>
                    <view class="mt-[40rpx]"
                        ><apply-btn
                            :customClass="customClass"
                            btnText=""
                            @popTrue="popTrue"
                        ></apply-btn
                    ></view>
                </view>
            </view>
        </view>
    </view>
</template>

<script lang="ts" setup>
const { proxy } = getCurrentInstance() as any
import cache from '@/utils/cache'
import { onShow } from '@dcloudio/uni-app'
/**
 * @description props参数说明
 * @property [number] scrollTop 外部传入的滚动条高度
 * @property [array] tabs tabs
 * @property [number] current 设置默认的tabIndex
 * @property [number]  stickyTop 固定定位的高度
 * @property [boolean] sticky 是否设置tab为固定定位
 * @property [number] itemOffsetTop 自定义滚动相隔间距,到达会切换tab
 * @property [object]  tabOptions tab的配置
 * @property [boolean] scrollTab 滚动时是否切换到指定的tab
 * @property [boolean] clickScroll 点击时是否滚动到相应位置
 * @property [number ] duration  滚动持续时长
 * @property [number] offsetTop 点击滚动到某一模块时的偏离值,每次点击滚动都会减去这个偏离值,这个值的单位是px,
 */
const mainColor = ref(cache.get('btnCon').btn_bgColor)
const customClass = ref('m-auto') //按钮样式
const props = defineProps({
    value: {
        type: String || Number,
        default: null
    },
    scrollTab: {
        type: Boolean,
        default: true
    },
    clickScroll: {
        type: Boolean,
        default: true
    },
    scrollTop: {
        type: Number || String,
        default: null
    },
    tabs: {
        type: Array,
        default: () => [] as any[]
    },
    // 设置默认的tabIndex
    current: {
        type: Number,
        default: 0
    },
    stickyTop: {
        type: String,
        default: null
    },
    // 是否将tab设为固定定位
    sticky: {
        type: Boolean,
        default: true
    },
    // 自定义间距 ,当 top小于等于这个距离的时候会切换tab
    itemOffsetTop: {
        type: Number,
        default: 60
    },
    tabOptions: {
        type: Object,
        default: () => ({})
    },
    duration: {
        type: Number,
        default: 300
    },
    offsetTop: {
        type: Number,
        default: 0
    }
})
const tabTopHeight = ref(0)
const active = ref(0)
const click = ref(false)
const timer = ref()
watch(
    [() => props.scrollTop, () => active.value],
    ([newScrollTop, newActive], [oldScrollTop, oldActive]) => {
        if (newScrollTop) {
            if (!click.value) {
                scrollToTab()
            }
        }
        if (newActive != oldActive) {
            nextTick(() => {
                scrollToElement()
            })
        }
    },
    { immediate: true }
)

onMounted(() => {
    getScrollTabTopHeight()
})
onShow(() => {
    isPopup.value = false
})
/**
 * @description: 跳转加盟申请页面
 * @param {*} isLogin 是否登陆
 * @return {*}
 */
const popTrue = (isLogin: boolean) => {
    if (!isLogin) {
        isPopup.value = !isLogin
    } else {
        uni.navigateTo({
            url: linkUrl.value
        })
    }
}

const getScrollTabTopHeight = async () => {
    // 获取tab的高度
    const query = (await createSelectorQueryForThis('#tabScrollTop', false)) as any
    if (!query) return
    tabTopHeight.value = query.height
}

const createSelectorQueryForThis = (selector: string, all: any) => {
    return new Promise((resolve) => {
        proxy
            .createSelectorQuery()
            [all ? 'selectAll' : 'select'](selector)
            .boundingClientRect((rect: any) => {
                resolve(rect)
            })
            .exec()
    })
}

const createSelectorQuery = (selector: any, all: any) => {
    return new Promise((resolve) => {
        proxy
            .createSelectorQuery()
            [all ? 'selectAll' : 'select'](selector)
            .boundingClientRect((rect: any) => {
                resolve(rect)
            })
            .exec()
    })
}
const emit = defineEmits<{
    (event: 'onChange', index: number): void
    (event: 'input', active: any): void
}>()
const handleChangeTab = (index: any) => {
    active.value = index
    click.value = true
}
// 点击滑动到指定元素
const scrollToElement = async () => {
    if (!click.value) return false
    const tab = props.tabs[active.value]
    if (!tab) return
    const { scroll_id } = tab as any
    if (!scroll_id) return false
    clearTimeout(timer.value)
    const queryData = (await createSelectorQuery(`.${scroll_id}`, false)) as any
    if (tabTopHeight.value === 0) await getScrollTabTopHeight()
    if (!queryData) {
        click.value = true
        return false
    }
    let scrollTop = props.scrollTop + queryData.top - props.offsetTop
    scrollTop -= tabTopHeight.value
    // 页面滚动函数
    uni.pageScrollTo({
        scrollTop,
        duration: props.duration,
        success: () => {
            timer.value = setTimeout(() => {
                click.value = false
            }, props.duration + 500)
        }
    })
}
const scrollToTab = async () => {
    if (!props.scrollTab && !click.value) return false
    const length = props.tabs.length
    let allClass = ''
    for (let i = 0; i < length; i++) {
        const { scroll_id } = props.tabs[i] as any
        allClass += i < length - 1 ? `.${scroll_id},` : `.${scroll_id}`
    }
    const queryData = (await createSelectorQuery(allClass, true)) as any

    for (let i = 0; i < queryData.length; i++) {
        if (queryData[i].top <= props.itemOffsetTop) {
            active.value = i
        }
    }
}
</script>

<style lang="scss" scoped>
.scroll-tabs-container {
    .status_bar {
        display: none;
        // height: var(--status-bar-height);
    }

    .scroll-tabs-sticky {
        z-index: 9999;
        overflow: hidden;
        -webkit-transform: translateZ(0);
        transform: translate3d(0, 0, 0);
        background: white;
    }
}
.radiusz {
    border-radius: 26rpx 26rpx 0 0;
    margin-top: -98rpx;
    border-bottom: 2px solid #eee;
    position: fixed;
    width: 100%;
}
</style>

页面调用代码:

<template>
  <view class="bg-[#F3F4F6] w-full">
     <scroll-tabs
           :value="current"
           :tabs="graphicIntroduction"
           :tabOptions="{ label: 'title', activeColor: '#222', barColor: '#ff9f0f' }"
           :offsetTop="180"
           :scrollTop="scrollTop"
           :sticky="true"
           :itemOffsetTop="300 + statusBarHeight"
           :clickScroll="clickScroll"
       >
       </scroll-tabs>
   </view>
 </template>
 <script lang="ts" setup>
import { getMiniNavigation } from '@/api/shop'
import { onLoad, onPageScroll } from '@dcloudio/uni-app'

const bannerImg = ref('')
const graphicIntroduction = ref([]) as any
const current = ref(0) // tab默认索引

const scrollTop = ref(0)
const clickScroll = ref(false)
const stickyTop = ref()
const statusBarHeight = ref()
onPageScroll((e: any) => {
    scrollTop.value = e.scrollTop
})

onLoad(() => {
    getMiniNavigationData()
    // getHeight()
    uni.getSystemInfo({
        success: function (e: any) {
            stickyTop.value = e.statusBarHeight + 44 + 'px'
            statusBarHeight.value = e.statusBarHeight
        }
    })
})

/**
 * @description: 获取首页轮播图和图文介绍
 * @return {*}
 */
const getMiniNavigationData = async () => {
    const { data } = await getMiniNavigation({ type: 'plan' })
    bannerImg.value = data.bannerImg
    const list = data.graphicIntroduction
    list.forEach((item: any) => {
        if (item.is_show == 1) {
            graphicIntroduction.value.push(item)
        }
    })
    graphicIntroduction.value.forEach((item: any, index: number) => {
        item.scroll_id = `tabs${index}`
    })
}
</script>

效果图:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1407876.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Flutter 面试题】Flutter 是什么?它与其他移动开发框架有什么不同?

文章目录 写在前面Flutter是什么&#xff1f;定义和起源核心设计思想架构组成总结 Flutter与其他移动开发框架的差异1. 跨平台性能2. Dart语言的全面优势3. 热重载功能的优化体验4. 丰富的组件和库的生态系统5. UI一致性和用户体验总结 写在前面 &#x1f44f;&#x1f3fb; 正…

幻兽帕鲁4核16G配置推荐价格表阿里云和腾讯云

阿里云幻兽帕鲁服务器Palworld服务器推荐4核16G配置&#xff0c;可以选择通用型g7实例或通用算力型u1实例&#xff0c;ECS通用型g7实例4核16G配置价格是502.32元一个月&#xff0c;算力型u1实例4核16G是432.0元/月&#xff0c;经济型e实例是共享型云服务器&#xff0c;价格是32…

Qt配置OpenCV

首先安装好Qt Createor&#xff0c;CMake&#xff0c;OpenCV,我本次使用的是Qt6.3.4和OpenCV4.6.0 Qt Creator清华镜像源:https://mirrors.tuna.tsinghua.edu.cn/qt/official_releases/qtcreator/OpenCV官网下载: https://opencv.org/releases/ 一. 编译OpenCV 首先使用Qt C…

【GitHub项目推荐--不错的 Go 学习项目】【转载】

开源实时性能分析平台 Pyroscope 是基于 Go 的开源实时性能分析平台&#xff0c;在源码中添加几行代码 pyroscope 就能帮你找出源代码中的性能问题和瓶颈、CPU 利用率过高的原因&#xff0c;调用树展示帮助你理解程序&#xff0c;支持 Go、Python、Ruby 语言。 Pyroscope 可以…

Linux RTC 子系统

rtc 一般负责系统关机后计时、闹钟等&#xff0c;Linux 内核提供了一个 rtc 子系统&#xff0c;来支持所有的 rtc 设备。 rtc 设备本质上是一个字符设备&#xff0c;rtc 子系统在字符设备的基础上抽象与硬件无关的部分&#xff0c;并在这个基础上拓展 sysfs 和 proc 文件系统下…

纯前端实现上一条下一条切换,并实现跨页切换上一条下一条

1.开发遇到了新的需求&#xff1a;再进入到视频播放界面&#xff0c;需要支持可以点击上一条下一条按钮&#xff0c;实现数据切换的功能 2.功能开发的理解 需要考虑到以上的需求&#xff0c; 1.由于视频的入口不一致&#xff0c;要根据入口的不同&#xff0c;实现不同的上一条…

Vue 的 事件修饰符and按键修饰符

1、事件修饰符概览 修饰符说明 .prevent阻止默认事件 .stop阻止冒泡.once事件只触发一次 .capture 添加事件侦听器时使用事件捕获模式.self只有点击当前元素本身时才会触发回调.passive事件的默认行为立即执行&#xff0c;无需等待事件回调执行完毕(不常用).native 将vue组件…

Qt 拖拽事件示例

一、引子 拖拽这个动作,在桌面应用程序中是非常实用和具有很友好的交互体验的。我们常见的譬如有,将文件拖拽到某个窗口打开,或者拖拽文件到指定位置上传;在绘图软件中,选中某个模板、并拖拽到画布上,画布上变回绘制该模板的图像… 诸如此类,数不胜数。 那么,在Qt中我…

学习笔记-李沐动手学深度学习(一)(01-07,概述、数据操作、tensor操作、数学基础、自动求导)

个人随笔 第三列是 jupyter记事本 官方github上啥都有&#xff08;代码、jupyter记事本、胶片&#xff09; https://github.com/d2l-ai 多体会 【梯度指向的是值变化最大的方向】 符号 维度 &#xff08;弹幕说&#xff09;2&#xff0c;3&#xff0c;4越后面维度越低 4…

Java线程池七大参数详解和配置(面试重点!!!)

一、corePoolSize核心线程数 二、maximunPoolSize最大线程数 三、keepAliveTime空闲线程存活时间 四、unit空闲线程存活时间的单位 五、workQueue线程工作队列 1、ArrayBlockingQueue FIFO有界阻塞队列 2、LinkedBlockingQueue FIFO无限队列 3、PriorityBlockingQueue V…

电池回收产业东风中,吉利科技集团如何先行一步?

随着绿色低碳可持续发展理念深入人心&#xff0c;全球能源变革和转型升级持续推进&#xff0c;新能源行业不断涌现新的机遇。 动力电池回收和再利用&#xff0c;就是近在眼前的“红利型”产业。 我国新能源汽车市场近年来爆发式增长&#xff0c;动力电池生产紧随电动车普及步…

Elasticsearch分布式一致性原理剖析(三)-Data篇

本文首发于云栖社区(Elasticsearch分布式一致性原理剖析(三)-Data篇-博客-云栖社区-阿里云)&#xff0c;由原作者转载。 前言 “Elasticsearch分布式一致性原理剖析”系列将会对Elasticsearch的分布式一致性原理进行详细的剖析&#xff0c;介绍其实现方式、原理以及其存在的问…

OceanBase创建租户

租户是集群之上的递进概念&#xff0c;OceanBase 数据库采用了多租户架构。 集群偏部署层面的物理概念&#xff0c;是 Zone 和节点的集合&#xff0c;租户则偏向于资源层面的逻辑概念&#xff0c;是在物理节点上划分的资源单元&#xff0c;可以指定其资源规格&#xff0c;包括…

常见的二十种软件测试方法详解

一.单元测试&#xff08;模块测试&#xff09; 单元测试是对软件组成单元进行测试。其目的是检验软件组成单位的正确性。测试对象是&#xff1a;模块。 对模块进行测试&#xff0c;单独的一个模块测试&#xff0c;属于静态测试的一类 测试阶段&#xff1a;编码后或者编码前&…

大数据学习之Redis、从零基础到入门(一)

目录 一、Redis入门概述 1. 是什么&#xff1f; 官方解释&#xff1a; 2. 能干嘛&#xff1f; 2.1 主流功能与应用 2.1.1分布式缓存 2.1.2内存存储和持久化(RDBAOF) 2.1.3高可用架构搭建 2.1.4缓存穿透、击穿、雪崩 2.1.5分布式锁 2.1.6队列 2.2 总体功能概括 2.3…

JeecgBoot 3.6.1实现Modal对话框,以为审核数据为例

JeecgBoot 3.6.1实现Modal对话框 vue使用的是3.0版本 文章目录 JeecgBoot 3.6.1实现Modal对话框前言一、列表页面关键代码示例二、textAuditModal.vue代码示例三、test.api.ts总结 前言 在工作中&#xff0c;有一个需求&#xff0c;要求&#xff0c;在数据列表页&#xff0c;…

容联云入选中国大模型产业新锐企业TOP30

近日&#xff0c; WIM 2023&#xff08;世界创新者年会&#xff09;发布《2023中国大模型产业创新服务商TOP 30》名单&#xff0c;评选出30家2023年中国大模型领域做出卓越贡献的企业&#xff0c;凭借垂直行业大模型“赤兔大模型”展现出的创新力与商业落地速度&#xff0c;容联…

springboot项目中使用iframe引入页面

一、 <iframe name"uploadPage" src"/uploadImg.html" width"100%" height"50" marginheight"0" marginwidth"0" scrolling"no" frameborder"0"></iframe> 前提是将要引入的页面…

2012-2022年全国各省数字经济相关指标数据合集(18个指标)

2012-2022年全国各省数字经济相关指标数据合集&#xff08;18个指标&#xff09; 1、时间&#xff1a;2012-2022年 2、指标&#xff1a;地区、year、互联网接入端口数、互联网宽带接入用户数、互联网域名数、移动电话普及率、长途光缆线路长度&#xff08;万公里&#xff09;…

Linux 文件:IO接口详解及实操

一、C语言中的文件IO读写操作 在c语言文件中&#xff0c;创建、打开、读、写操作可以通过如下的代码进行&#xff1a; 1.1写文件 通过w指令对文件进行写入操作时&#xff0c;编译器会先将文件内容清空然后重新写入。 #include <stdio.h> #include <string.h> i…