当前位置: 首页 > news >正文

uniapp tab切换及tab锚点效果(wx小程序及H5端)

1. uniapp tab切换及tab锚点效果(wx小程序及H5端)

在这里插入图片描述

在这里插入图片描述

1.1. 示例源码

1.1.1. 在 components 文件夹下,新建 tab-anchor.vue 文件,一键复制如下代码。

<!-- 可根据现有需求,"适当"更改组件源码! --><template><view :id="'luBarTabNav'+barId" class="lu-bar-tab-nav"><view v-if="!!barFixed" id="luTabFixed" class="lu-bar-tab lu-bar-tab-fixed" :style="{ top: barTopStyles,height:barHeightStyles,display:barShowStyles }"><view class="lu-tab-item" v-for="(item,index) in tabList" :key="index" :class="[selectedIndex==index? 'lu-active' : '',!!iconShow? 'lu-icon-show' : '']" :style="selectedIndex==index?tabActiveStyles:tabStyles"@tap="_scrollToTarget(index, item)"><view v-if="!!iconShow" class="lu-tab-icon" :class="selectedIndex==index?item.selectedIconClass:item.iconClass":style="selectedIndex==index?(!!item.selectedIconImage?'backgroundImage:url('+item.selectedIconImage+')':''):(!!item.iconImage?'backgroundImage:url('+item.iconImage+')':'')"></view><view:class="selectedIndex==index? 'lu-tab-text lu-tab-text-active' : 'lu-tab-text'">{{item.text}}</view></view></view><view id="luTabStatic" class="lu-bar-tab lu-bar-tab-static" :style="{height:barHeightStyles}"><view class="lu-tab-item" v-for="(item,index) in tabList" :key="index" :class="[selectedIndex==index? 'lu-active' : '',!!iconShow? 'lu-icon-show' : '']" :style="selectedIndex==index?tabActiveStyles:tabStyles"@tap="_scrollToTarget(index, item)"><view v-if="!!iconShow" class="lu-tab-icon" :class="selectedIndex==index?item.selectedIconClass:item.iconClass":style="selectedIndex==index?(!!item.selectedIconImage?'backgroundImage:url('+item.selectedIconImage+')':''):(!!item.iconImage?'backgroundImage:url('+item.iconImage+')':'')"></view><view:class="selectedIndex==index? 'lu-tab-text lu-tab-text-active' : 'lu-tab-text'">{{item.text}}</view></view></view><view class="lu-tab-content"><slot></slot></view></view>
</template><script>export default {props: {barFixed:{//选项卡是否启用浮动功能(可选)type:Boolean,default:true},iconShow:{//是否启用选项卡图标(可选)type:Boolean,default:false},transitionShow:{type:Boolean,default:false},barHeight:{type:[String,Number],default:44},barTop:{type:[String,Number],default:0},barId:{type:[String,Number],default:0},tabList: {type:Array,default:function () {return []}}},data() {return {barShow:false,selectedIndex:0,};},computed:{tabStyles:function () {return (!!this.color?'color:'+this.color+';':'')+(!!this.backgroundColor?'backgroundColor:'+this.backgroundColor+';':'');},tabActiveStyles:function () {return (!!this.selectedColor?'color:'+this.selectedColor+';':'')+(!!this.selectedBackgroundColor?'backgroundColor:'+this.selectedBackgroundColor+';':'');},barTopStyles:function () {// #ifndef H5return 'calc('+this.barTop+'px);';// #endif// #ifdef H5return 'calc('+this.barTop+'px + var(--window-top));';// #endif},barHeightStyles:function () {return this.barHeight?this.barHeight+'px':'44px';},barShowStyles:function () {return !this.barShow?'none':'';},},methods: {_barInit:async function (index){let navTargetTop = [];let duration = 0;let viewScrollTop = 0;let viewHeight = 0;for (let i = 0,len=this.tabList.length; i < len; i++) {navTargetTop[i]= await this._queryMultipleNodes(this.tabList[i]["navTarget"]).then(res => {let navTarget = res[0],viewPort = res[1];viewHeight = viewPort.height;viewScrollTop = viewPort.scrollTop;const scrollTop = parseInt(navTarget.top) + viewPort.scrollTop - this.barTop - this.barHeight;return scrollTop;});}if (!!this.transitionShow) {duration = 200;} return {navTargetTop:navTargetTop,duration:duration,viewHeight:viewHeight,viewScrollTop:viewScrollTop};},_pageScroll:async function(i){const elment = await this._barInit(i);let scrollTop = elment["navTargetTop"][i];let duration = elment["duration"];let viewHeight = elment["viewHeight"];let viewScrollTop = elment["viewScrollTop"];if (Math.abs(scrollTop-viewScrollTop)>viewHeight) {if (scrollTop>viewScrollTop) {await uni.pageScrollTo({scrollTop:(scrollTop-viewHeight),duration:0});}else{await uni.pageScrollTo({scrollTop:(scrollTop+viewHeight),duration:0});}}await uni.pageScrollTo({scrollTop:(scrollTop+1),duration:duration});// #ifndef H5const view = await this._queryMultipleNodes();viewScrollTop = view[0].scrollTop;if (scrollTop>viewScrollTop&&duration!==0) {uni.pageScrollTo({scrollTop:(scrollTop+1),duration: 0})}// #endif},_scrollToTarget:function(i, item) {this._pageScroll(i);// 通知父组件(把行数据传过去)// console.log(item)this.$emit('clickTabs', item)},_queryMultipleNodes:function (e,notThis) {return new Promise((resolve, reject) => {let view = uni.createSelectorQuery();if (!!notThis) {view.in(this);}if (!!e) {view.select(e).boundingClientRect();}view.selectViewport().fields({size: true,scrollOffset: true});view.exec(function(res) {resolve(res);});});},_showBarFixed:function () {this._queryMultipleNodes("#luTabStatic",true).then(res => {let tabNav = res[0];if (tabNav.top<=this.barTop) {this.barShow=true;}else{this.barShow=false;}});},_selectedTab:function (y) {this._barInit().then((res)=>{let itemIndex = 0;for (let i = 0,len=res["navTargetTop"].length; i < len; i++) {if (y >=res["navTargetTop"][i]) {itemIndex = i;}}this.selectedIndex = itemIndex;});if (!!this.barFixed) {this._showBarFixed();}}}};
</script><style lang="scss" >lu-bar-tab-nav{position:relative;width: 100%;}.lu-bar-tab-nav{position:relative;width: 100%;.lu-bar-tab{width: 100%;display: flex;flex-flow: row wrap;justify-content: space-around;align-items:center;// 选项卡背景颜色background-color: #fff;height: 44px;.lu-tab-item{//默认状态position: relative;flex: 1 1 auto;text-align: center;color: #333;height: 100%;font-size: 15px;display: flex;flex-flow: column nowrap;justify-content: center;align-items:center;// &::before{// 	position: absolute;// 	top: calc(50% - 15px);// 	left: 0px;// 	content: " ";// 	width: 1px;// 	height: 30px;// 	background-color: #eee;// }&:first-child::before{display: none;}.lu-tab-icon{font-size: inherit;color: inherit;}// 选项卡默认样式.lu-tab-text{font-size: inherit;color: inherit;// border: 1px solid red;padding-bottom: 6px;// 加个默认白色,防止高度不统一border-bottom: 3px solid #fff;}// 激活的选项卡-下划线.lu-tab-text-active {border-bottom: 3px solid #2979ff;}// 显示图标&.lu-icon-show{.lu-tab-icon{height: 24px;width: 24px;background-position: center center;background-repeat: no-repeat;background-size: 100% 100%;}.lu-tab-text{font-size: 12px;line-height: 16px;}}// 选中状态&.lu-active{color: #2979ff;// font-weight: bold;.lu-tab-icon{background-position: center center;background-repeat: no-repeat;background-size: 100% 100%;}}}}.lu-bar-tab-fixed{position:fixed;z-index: 1;top:calc(0px + var(--window-top));}.lu-bar-tab-static{position:static;z-index: 0;}}
</style>

1.1.2. 页面引用

<!-- 注意: 底部最好留空或业务内容占位,否则会出现最后一个锚点"激活时(到显示区域)",选项卡无高亮的问题 -->
<!-- 如果想要修改选项卡样式,请到组件中去修改 -->
<!-- 更多配置及用法,详见文章底部组件文档! --><template><view><!-- 占位内容 --><view class="seize"><text>顶部-占位内容(可有可无)</text></view><!-- END --><Anchor:tabList="tabs":barFixed="true":transitionShow="true"barHeight="50":barTop="0"barId="0"@clickTabs="clickTabs"ref="barTabNav"><!-- id与数据tabs->navTarget对应 --><view id="item1"><view class="box"><p v-for="item in 10">无论什么怪异容器,都能精准适应</p></view></view><view id="item2"><view class="box"><h1>只有简短的一行容器</h1></view></view><view id="item3"><view class="box"><p v-for="item in 30">红线就是每个锚点的区域</p></view></view></Anchor><!-- 占位内容 --><view class="seize"><text>底部-占位内容(可有可无)</text></view><!-- END --></view>
</template><script>// 注意引用位置
import Anchor from '@/components/anchor.vue'export default {components:{ Anchor },data() {return {// 选项卡// 注意: 必须有text/navTarget属性,其他键值不管随便写和用tabs: [{text: "选项一",//名称navTarget: "#item1",//锚点value: 'one'//比如},{text: "选项二",navTarget: "#item2",value: 'two'},{text: "选项三",navTarget: "#item3",value: 'three'}],// 当前选项卡valuecurrent: 'one'}},/*** 监听滚动* @description 传给子组件处理逻辑* @return void */onPageScroll(e) {this.$refs.barTabNav._selectedTab(e.scrollTop);},methods: {/*** 选项卡被点击* @description 你无需关心锚点如何工作,写你的业务即可* @param {Object} row - 行数据* @return void*/clickTabs(row) {// 避免重复点击if(this.current == row.value) return false;// 操作处理console.log(row)this.current = row.valueuni.showToast({title: row.text,icon: 'none'})},}
}
</script><style scoped>/* 占位内容 */
.seize {background: #dadada;padding: 40px;text-align: center;
}
/* END *//* 选项卡样式 */
.tabs {background: #fff;width: 100%;
}
/* END */.box {background: #dadada;padding: 20px;border-bottom: 5px solid red;
}
</style>

1.1.3. Props

在这里插入图片描述
注意:在APP下建议禁用transitionShow,不然会引起点击触发滚动中选项卡导航栏消失的bug。

1.1.4. tabList

在这里插入图片描述
除了这两个必填项,其他数据随便往里面追加,点击选项卡时会读出来。

1.2. 其它代码

在这里插入图片描述

1.2.1. tabAnchor.json

{"rows": [{"text": "选项一","navTarget": "#item1","navId": "item1","title": "one","subRows": [{"id": 1,"img": "../../../static/icon/base-menu/icon-base-community-add.png","title": "小区录入","url": "../eventInfo/eventInfo"},{"id": 2,"img": "../../../static/icon/base-menu/icon-base-building-add.png","title": "楼栋录入","url": "../baseInfo/baseInfo"},{"id": 3,"img": "../../../static/icon/base-menu/icon_base_house_add.png","title": "房屋录入","url": "../workCheckInfo/workCheckInfo"},{"id": 4,"img": "../../../static/icon/base-menu/icon_base_people_add.png","title": "人口录入","url": "../eventInfo/eventInfo"}]},{"text": "选项二","navTarget": "#item2","navId": "item2","title": "two","subRows": [{"id": 1,"img": "../../../static/icon/base-menu/icon-base-community-add.png","title": "小区录入","url": "../eventInfo/eventInfo"},{"id": 2,"img": "../../../static/icon/base-menu/icon-base-building-add.png","title": "楼栋录入","url": "../baseInfo/baseInfo"},{"id": 3,"img": "../../../static/icon/base-menu/icon_base_house_add.png","title": "房屋录入","url": "../workCheckInfo/workCheckInfo"},{"id": 4,"img": "../../../static/icon/base-menu/icon_base_people_add.png","title": "人口录入","url": "../eventInfo/eventInfo"},{"id": 4,"img": "../../../static/icon/base-menu/icon_base_people_add.png","title": "人口录入","url": "../eventInfo/eventInfo"},{"id": 4,"img": "../../../static/icon/base-menu/icon_base_people_add.png","title": "人口录入","url": "../eventInfo/eventInfo"},{"id": 4,"img": "../../../static/icon/base-menu/icon_base_people_add.png","title": "人口录入","url": "../eventInfo/eventInfo"},{"id": 4,"img": "../../../static/icon/base-menu/icon_base_people_add.png","title": "人口录入","url": "../eventInfo/eventInfo"},{"id": 4,"img": "../../../static/icon/base-menu/icon_base_people_add.png","title": "人口录入","url": "../eventInfo/eventInfo"},{"id": 4,"img": "../../../static/icon/base-menu/icon_base_people_add.png","title": "人口录入","url": "../eventInfo/eventInfo"}]},{"text": "选项三","navTarget": "#item3","navId": "item3","title": "three","subRows": [{"id": 1,"img": "../../../static/icon/base-menu/icon-base-community-add.png","title": "小区录入","url": "../eventInfo/eventInfo"},{"id": 2,"img": "../../../static/icon/base-menu/icon-base-building-add.png","title": "楼栋录入","url": "../baseInfo/baseInfo"},{"id": 3,"img": "../../../static/icon/base-menu/icon_base_house_add.png","title": "房屋录入","url": "../workCheckInfo/workCheckInfo"},{"id": 4,"img": "../../../static/icon/base-menu/icon_base_people_add.png","title": "人口录入","url": "../eventInfo/eventInfo"},{"id": 4,"img": "../../../static/icon/base-menu/icon_base_people_add.png","title": "人口录入","url": "../eventInfo/eventInfo"},{"id": 4,"img": "../../../static/icon/base-menu/icon_base_people_add.png","title": "人口录入","url": "../eventInfo/eventInfo"},{"id": 4,"img": "../../../static/icon/base-menu/icon_base_people_add.png","title": "人口录入","url": "../eventInfo/eventInfo"},{"id": 4,"img": "../../../static/icon/base-menu/icon_base_people_add.png","title": "人口录入","url": "../eventInfo/eventInfo"},{"id": 4,"img": "../../../static/icon/base-menu/icon_base_people_add.png","title": "人口录入","url": "../eventInfo/eventInfo"},{"id": 4,"img": "../../../static/icon/base-menu/icon_base_people_add.png","title": "人口录入","url": "../eventInfo/eventInfo"}]}]
}

1.2.2. tab-anchor.vue

<template><view :id="'luBarTabNav'+barId"><view v-if="!!barFixed"id="luTabFixed"class="stb-tab-layout stb-tab-fixed":style="{ top: barTopStyles,height:barHeightStyles}"><view v-for="(item,index) in tabList":key="index":class="selectedIndex===index? 'stb-tab-item stb-tab-active': 'stb-tab-item stb-tab-normal'"@tap="scrollToTarget(index, item)">{{ item.text }}</view></view><view id="luTabStatic"class="stb-tab-layout stb-tab-static":style="{height:barHeightStyles}"><view class=""v-for="(item,index) in tabList" :key="index":class="selectedIndex===index? 'stb-tab-item stb-tab-active': 'stb-tab-item stb-tab-normal'"@tap="scrollToTarget(index, item)">{{ item.text }}</view></view><view><slot></slot></view></view>
</template>
<script>
export default {props: {barFixed: {//选项卡是否启用浮动功能(可选)type: Boolean,default: true},/*** 是否动画切换*/transitionShow: {type: Boolean,default: false},barHeight: {type: [String, Number],default: 30},barTop: {type: [String, Number],default: 0},barId: {type: [String, Number],default: 0},tabList: {type: Array,default: function () {return []}}},data() {return {selectedIndex: 0,};},computed: {barTopStyles: function () {let barTopStyles = 'calc(' + this.barTop + 'px);'// #ifndef H5barTopStyles = 'calc(' + this.barTop + 'px);'// #endif// #ifdef APPbarTopStyles = 'calc(' + this.barTop + 'px + var(--window-top));';// #endifreturn barTopStyles;},barHeightStyles: function () {return this.barHeight ? this.barHeight + 'px' : '30px';},},methods: {/*** 初始化tab*/barInit: async function (index) {let navTargetTop = [];let duration = 0;let viewScrollTop = 0;let viewHeight = 0;for (let i = 0, len = this.tabList.length; i < len; i++) {navTargetTop[i] = await this.queryMultipleNodes(this.tabList[i]["navTarget"]).then(res => {let navTarget = res[0],viewPort = res[1];viewHeight = viewPort.height;viewScrollTop = viewPort.scrollTop;const scrollTop = parseInt(navTarget.top)+ viewPort.scrollTop- this.barTop - this.barHeight;return scrollTop;});}if (!!this.transitionShow) {duration = 200;}return {navTargetTop: navTargetTop,duration: duration,viewHeight: viewHeight,viewScrollTop: viewScrollTop};},pageScroll: async function (i) {const elment = await this.barInit(i);let scrollTop = elment["navTargetTop"][i];let duration = elment["duration"];let viewHeight = elment["viewHeight"];let viewScrollTop = elment["viewScrollTop"];if (Math.abs(scrollTop - viewScrollTop) > viewHeight) {if (scrollTop > viewScrollTop) {await uni.pageScrollTo({scrollTop: (scrollTop - viewHeight),duration: 0});} else {await uni.pageScrollTo({scrollTop: (scrollTop + viewHeight),duration: 0});}}await uni.pageScrollTo({scrollTop: (scrollTop + 1),duration: duration});// #ifndef H5const view = await this.queryMultipleNodes();viewScrollTop = view[0].scrollTop;if (scrollTop > viewScrollTop && duration !== 0) {uni.pageScrollTo({scrollTop: (scrollTop + 1),duration: 0})}// #endif},scrollToTarget: function (i, item) {this.pageScroll(i);// 通知父组件(把行数据传过去)this.$emit('clickTabs', item)},queryMultipleNodes: function (e, notThis) {return new Promise((resolve, reject) => {let view = uni.createSelectorQuery();if (!!notThis) {view.in(this);}if (!!e) {view.select(e).boundingClientRect();}view.selectViewport().fields({size: true,scrollOffset: true});view.exec(function (res) {resolve(res);});});},showBarFixed: function () {this.queryMultipleNodes("#luTabStatic",true).then(res => {let tabNav = res[0];this.barShow = tabNav.top <= this.barTop;});},selectedTab: function (y) {this.barInit().then((res) => {let itemIndex = 0;for (let i = 0, len = res["navTargetTop"].length;i < len; i++) {if (y >= res["navTargetTop"][i]) {itemIndex = i;}}this.selectedIndex = itemIndex;});if (!!this.barFixed) {this.showBarFixed();}}}
};
</script>
<style lang="scss">
.stb-tab-layout {width: 100%;display: flex;flex-flow: row wrap;justify-content: space-around;align-items: center;background-color: #fff;height: 30px;
}.stb-tab-fixed {position: fixed;z-index: 1;top: calc(0px + var(--window-top));
}.stb-tab-static {position: static;z-index: 0;
}.stb-tab-item {position: relative;flex: 1;height: 100%;display: flex;flex-flow: column nowrap;justify-content: center;border: 1px solid #2979ff;font-size: 12px;align-items: center;
}.stb-tab-normal {color: #2979ff;
}.stb-tab-active {background-color: #2979ff;color: white;
}</style>

1.2.3. tabAnchor.vue

<template><view><tab-anchor ref="barTabNav"barId="0"barHeight="40":tabList="tabList":barFixed="true":transitionShow="false":barTop="0"@clickTabs="clickTabs"><!--id与数据tabList->navTarget对应--><view class="anchor-menu-body"v-for="(item) in tabList"><view :id="item.navId"class="anchor-menu-main-layout"><view class="anchor-header-layout"><view class="anchor-header-left"><view class="anchor-header-icon"></view><text class="anchor-header-title">{{ item.title }}</text></view><view></view></view><view class="anchor-menu-layout"><view class="anchor-menu-item"v-for="(subItem) in item.subRows"@click="toDetail(subItem)"><image class="anchor-menu-item-image" :src="subItem.img"></image><view class="anchor-menu-item-title">{{ subItem.title }}</view></view></view></view></view></tab-anchor></view>
</template>
<script>
// 注意引用位置
import TabAnchor from "/components/tab-anchor.vue";
import tabAnchorJson from '../../../data/tabAnchor.json'export default {components: {TabAnchor},data() {return {// 注意: 必须有text/navTarget属性,其他键值不管随便写和用tabList: [],}},onLoad: function () {this.tabList = tabAnchorJson.rows},/*** 监听滚动* @description 传给子组件处理逻辑* @return void*/onPageScroll(e) {this.$refs.barTabNav.selectedTab(e.scrollTop);},methods: {/*** 选项卡被点击* @description 你无需关心锚点如何工作,写你的业务即可* @param {Object} row - 行数据* @return void*/clickTabs(row) {},}
}
</script><style scoped>.anchor-menu-body {display: flex;flex-direction: column;background: linear-gradient(180deg, #fff, #fff);
}.anchor-menu-main-layout {display: flex;flex-direction: column;
}.anchor-header-layout {display: flex;flex-direction: row;justify-content: space-between;align-items: center;margin-top:5px;
}.anchor-header-left {display: flex;flex-direction: row;align-items: center;
}.anchor-header-icon {width: 5px;height: 18px;margin: 0 5px 0 10px;border-radius: 3px;background-color: #007AFF;
}.anchor-header-title {font-size: 16px;color: #000000;
}.anchor-menu-layout {display: table;flex-wrap: wrap;justify-content: space-between;align-content: space-between;padding: 5px;
}.anchor-menu-item {width: 21%;display: flex;flex-direction: column;align-items: center;padding: 5px 0;margin: 5px 2%;border-radius: 10px;float: left;background-color: #EBF2F7;
}.anchor-menu-item-image {width: 32px;height: 32px;
}.anchor-menu-item-title {width: 100%;color: #06121e;font-size: 12px;text-align: center;overflow: hidden;display: -webkit-box;-webkit-line-clamp: 1;-webkit-box-orient: vertical;
}
</style>
http://www.dtcms.com/a/464739.html

相关文章:

  • Hadoop面试题及详细答案 110题 (71-85)-- 集群部署与运维
  • 5-1〔OSCP ◈ 研记〕❘ SQL注入攻击▸SQL注入理论基础
  • 南充市企业网站建设wordpress极客主题
  • 企业做小红书关键词搜索排名推广时,怎么找到小红书上有一定搜索量但竞争度低的蓝海词?
  • 数据仓库与数据挖掘基础知识
  • 鸿蒙:使用Rating组件实现五角星打分评价
  • 外国人可以在中国做网站吗做个网站得花多少钱
  • 双均线策略
  • 【vLLM 学习】Neuron
  • 网站做行业认证好处施工企业在施工过程中发现工程设计图纸存在差错的
  • 迅为RK3576开发板挂载Windows以及虚拟机Ubuntu测试
  • 第1篇:创建基础电商AI客服
  • 【MyBatis从入门到入土】告别JDBC原始时代:零基础MyBatis极速上手指南
  • MaxScript 科研绘图教程:从数据到精确的可视化
  • org.apache.http.conn.HttpHostConnectException: Connect to localhost:8086
  • 深度学习入门(一)——从神经元到损失函数,一步步理解前向传播(上)
  • 沧州网站制作公司宁波网站的优化
  • 工程承包去哪个网站做网站的步骤 优帮云
  • 网站建设课程wordpress 去掉左上角
  • 怎么创建一个博客网站wordpress电影下载
  • 搭建网站案例精粹北京cos网站
  • 云南澄江县建设局网站做网站江门
  • 企业外贸网站建设互联网创业项目概述
  • 关于企业网站建设数据现状分析重庆百度推广关键词优化
  • 手机网站免费网站安全建设费用预算表
  • 网站seo推广计划网页制作工具可以分为
  • 北京企业建设网站制作建设网站总经理讲话范本
  • 沂南网站设计不用wordpress
  • 长春网站建设吉网传媒实力牜网页视频下载安卓
  • 用书籍上的文章做网站更新南海建设工程交易中心网站