11 changed files with 991 additions and 9 deletions
-
4common/api.js
-
3common/http.js
-
26common/styles/sharedIconFont.css
-
191components/lf-nav/lf-nav.vue
-
149components/lf-tabbar/animate.scss
-
258components/lf-tabbar/lf-tabbar-item.vue
-
179components/lf-tabbar/lf-tabbar.vue
-
121components/lf-tabbar/style.scss
-
10components/lf-tabbar/utils.js
-
3pages.json
-
56pages/test/test.vue
@ -0,0 +1,191 @@ |
|||||
|
<template> |
||||
|
<view> |
||||
|
<view :style="{marginBottom: spreadOut ? headHeight + 'px' : '0px'}"> |
||||
|
<view :class="{head: true, 'border-b': boderBottom}" :style="{height: headHeight + 'px', background: baColor}"> |
||||
|
<block v-if="diy"> |
||||
|
<view class="diy-head" :style="{'top': headHeight - 38 + 'px'}"> |
||||
|
<slot></slot> |
||||
|
</view> |
||||
|
</block> |
||||
|
<block v-else> |
||||
|
<view class="head-nav" :style="{'top': headHeight - 40 + 'px'}" v-if="showIcon"> |
||||
|
<text class="le lf-icon-jiantouzuo font-40size" @click="clickDropOut"></text> |
||||
|
<text class="le lf-icon-home font-40size" @click="clickHome"></text> |
||||
|
</view> |
||||
|
<view class="title-box" :style="{'margin': headHeight - 36 + 'px auto 0'}"> |
||||
|
<input class="search" placeholder="搜你想要的" confirm-type="search" v-model="value" @confirm="onSearch" v-if="search" /> |
||||
|
<text class="font-30size" :style="{color: titleColor}" v-else>{{ title }}</text> |
||||
|
</view> |
||||
|
</block> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
props: { |
||||
|
title: { |
||||
|
type: String, // 标题 |
||||
|
default: '' |
||||
|
}, |
||||
|
showIcon: { |
||||
|
type: Boolean, // 是否显示左侧操作菜单 |
||||
|
default: false |
||||
|
}, |
||||
|
baColor: { |
||||
|
type: String, // head背景颜色 |
||||
|
default: '#f8f8f8' |
||||
|
}, |
||||
|
titleColor: { |
||||
|
type: String, // 标题字体颜色 |
||||
|
default: '#1d1d1d' |
||||
|
}, |
||||
|
spreadOut: { |
||||
|
type: Boolean, // 内容是否自动撑开,该组件悬浮了,原有的dom不再占用位置,如果需要占用值为true |
||||
|
default: true |
||||
|
}, |
||||
|
search: { |
||||
|
type: Boolean, // 是否显示搜索输入框 |
||||
|
default: false |
||||
|
}, |
||||
|
diy: { |
||||
|
type: Boolean, // 自定义内容,开启后title,showIcon,search均都不生效 |
||||
|
default: false |
||||
|
}, |
||||
|
backInquiry: { |
||||
|
type: Boolean, // 点击返回按钮后是否弹出询问弹窗,showIcon为true时生效 |
||||
|
default: false |
||||
|
}, |
||||
|
boderBottom: { |
||||
|
type: Boolean, // 是否显示底部边框线 |
||||
|
default: false |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
headHeight: 66, // 头部导航高度 |
||||
|
value: '' |
||||
|
}; |
||||
|
}, |
||||
|
created(){ |
||||
|
this.getSystemInfo(); |
||||
|
}, |
||||
|
methods: { |
||||
|
// 获取手机高度 |
||||
|
getSystemInfo(){ |
||||
|
let result = uni.getSystemInfoSync(); |
||||
|
this.headHeight = result.statusBarHeight + 46; |
||||
|
this.$emit('changeHeight', this.headHeight); |
||||
|
}, |
||||
|
// 监听返回 |
||||
|
clickDropOut(event){ |
||||
|
if(this.backInquiry){ |
||||
|
uni.showModal({ |
||||
|
title: '温馨提示', |
||||
|
content: '确定离开此页面吗?', |
||||
|
success: result => { |
||||
|
if(result.confirm){ |
||||
|
this.$back(); |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
}else{ |
||||
|
this.$back(); |
||||
|
} |
||||
|
}, |
||||
|
$back(){ |
||||
|
// #ifdef H5 |
||||
|
let pages = getCurrentPages(); |
||||
|
if(pages.length <= 1){ |
||||
|
uni.redirectTo({ |
||||
|
url: '/pages/index/index' |
||||
|
}) |
||||
|
return; |
||||
|
} |
||||
|
// #endif |
||||
|
uni.navigateBack(); |
||||
|
}, |
||||
|
// 监听回到首页 |
||||
|
clickHome(){ |
||||
|
uni.reLaunch({ |
||||
|
url: '/pages/index/index' |
||||
|
}) |
||||
|
}, |
||||
|
// 搜索 |
||||
|
onSearch(event){ |
||||
|
this.$emit('search', event.detail.value); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.font-40size{ |
||||
|
font-size: 40rpx; |
||||
|
} |
||||
|
.font-30size{ |
||||
|
font-size: 30rpx; |
||||
|
} |
||||
|
.border-b{ |
||||
|
border-bottom: 1rpx solid #e5e5e5; |
||||
|
} |
||||
|
.head{ |
||||
|
width: 100vw; |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
z-index: 99999; |
||||
|
} |
||||
|
.head .head-nav{ |
||||
|
position: absolute; |
||||
|
left: 2vw; |
||||
|
cursor: pointer; |
||||
|
width: 130rpx; |
||||
|
background-color: #FFFFFF; |
||||
|
border: 1rpx solid #dcd3d5; |
||||
|
border-radius: 30rpx; |
||||
|
height: 60rpx; |
||||
|
box-sizing: border-box; |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
padding: 0 12rpx; |
||||
|
} |
||||
|
.head .head-nav::after{ |
||||
|
position: absolute; |
||||
|
content: ''; |
||||
|
width: 2rpx; |
||||
|
height: 40rpx; |
||||
|
background-color: #f7f7f7; |
||||
|
left: 50%; |
||||
|
top: calc(50% - 20rpx); |
||||
|
} |
||||
|
.title-box{ |
||||
|
max-width: 49%; |
||||
|
height: 50rpx; |
||||
|
text-align: center; |
||||
|
overflow: hidden; |
||||
|
white-space: nowrap; |
||||
|
text-overflow: ellipsis; |
||||
|
} |
||||
|
.search{ |
||||
|
width: 280rpx; |
||||
|
height: 50rpx; |
||||
|
background-color: #f0f0f0; |
||||
|
color: #9a9a9a; |
||||
|
border-radius: 30rpx; |
||||
|
margin: 0 auto; |
||||
|
text-align: left; |
||||
|
box-sizing: border-box; |
||||
|
padding: 0 20rpx; |
||||
|
} |
||||
|
|
||||
|
.diy-head{ |
||||
|
position: absolute; |
||||
|
left: 2vw; |
||||
|
max-width: 70%; |
||||
|
height: 50rpx; |
||||
|
display: flex; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,149 @@ |
|||||
|
.animate__animated { |
||||
|
animation-duration: 0.5s; |
||||
|
animation-fill-mode: both; |
||||
|
} |
||||
|
|
||||
|
@keyframes zoomIn { |
||||
|
from { |
||||
|
opacity: 0; |
||||
|
-webkit-transform: scale3d(0.3, 0.3, 0.3); |
||||
|
transform: scale3d(0.3, 0.3, 0.3); |
||||
|
} |
||||
|
|
||||
|
50% { |
||||
|
opacity: 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.animate__zoomIn { |
||||
|
animation-name: zoomIn; |
||||
|
} |
||||
|
|
||||
|
@keyframes bounce { |
||||
|
|
||||
|
from, |
||||
|
20%, |
||||
|
53%, |
||||
|
to { |
||||
|
-webkit-animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); |
||||
|
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); |
||||
|
-webkit-transform: translate3d(0, 0, 0); |
||||
|
transform: translate3d(0, 0, 0); |
||||
|
} |
||||
|
|
||||
|
40%, |
||||
|
43% { |
||||
|
animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06); |
||||
|
transform: translate3d(0, -30px, 0) scaleY(1.1); |
||||
|
} |
||||
|
|
||||
|
70% { |
||||
|
animation-timing-function: cubic-bezier(0.755, 0.05, 0.855, 0.06); |
||||
|
transform: translate3d(0, -15px, 0) scaleY(1.05); |
||||
|
} |
||||
|
|
||||
|
80% { |
||||
|
transition-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); |
||||
|
transform: translate3d(0, 0, 0) scaleY(0.95); |
||||
|
} |
||||
|
|
||||
|
90% { |
||||
|
transform: translate3d(0, -4px, 0) scaleY(1.02); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.animate__bounce { |
||||
|
animation-name: bounce; |
||||
|
transform-origin: center bottom; |
||||
|
} |
||||
|
|
||||
|
@keyframes rubberBand { |
||||
|
from { |
||||
|
transform: scale3d(1, 1, 1); |
||||
|
} |
||||
|
|
||||
|
30% { |
||||
|
transform: scale3d(1.25, 0.75, 1); |
||||
|
} |
||||
|
|
||||
|
40% { |
||||
|
transform: scale3d(0.75, 1.25, 1); |
||||
|
} |
||||
|
|
||||
|
50% { |
||||
|
transform: scale3d(1.15, 0.85, 1); |
||||
|
} |
||||
|
|
||||
|
65% { |
||||
|
transform: scale3d(0.95, 1.05, 1); |
||||
|
} |
||||
|
|
||||
|
75% { |
||||
|
transform: scale3d(1.05, 0.95, 1); |
||||
|
} |
||||
|
|
||||
|
to { |
||||
|
transform: scale3d(1, 1, 1); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.animate__rubberBand { |
||||
|
animation-name: rubberBand; |
||||
|
} |
||||
|
|
||||
|
@keyframes bounceIn { |
||||
|
|
||||
|
from, |
||||
|
20%, |
||||
|
40%, |
||||
|
60%, |
||||
|
80%, |
||||
|
to { |
||||
|
animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); |
||||
|
} |
||||
|
|
||||
|
0% { |
||||
|
opacity: 0; |
||||
|
transform: scale3d(0.3, 0.3, 0.3); |
||||
|
} |
||||
|
|
||||
|
20% { |
||||
|
transform: scale3d(1.1, 1.1, 1.1); |
||||
|
} |
||||
|
|
||||
|
40% { |
||||
|
transform: scale3d(0.9, 0.9, 0.9); |
||||
|
} |
||||
|
|
||||
|
60% { |
||||
|
opacity: 1; |
||||
|
transform: scale3d(1.03, 1.03, 1.03); |
||||
|
} |
||||
|
|
||||
|
80% { |
||||
|
transform: scale3d(0.97, 0.97, 0.97); |
||||
|
} |
||||
|
|
||||
|
to { |
||||
|
opacity: 1; |
||||
|
transform: scale3d(1, 1, 1); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.animate__bounceIn { |
||||
|
animation-name: bounceIn; |
||||
|
} |
||||
|
|
||||
|
@keyframes fadeIn { |
||||
|
from { |
||||
|
opacity: 0; |
||||
|
} |
||||
|
|
||||
|
to { |
||||
|
opacity: 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.animate__fadeIn { |
||||
|
animation-name: fadeIn; |
||||
|
} |
||||
@ -0,0 +1,258 @@ |
|||||
|
<template> |
||||
|
<view ref="item" |
||||
|
:class="[ |
||||
|
'lb-tabbar-item', |
||||
|
'animate__animated', |
||||
|
isActive ? 'lb-tabbar-item--active' : '' |
||||
|
]" |
||||
|
:style="{ |
||||
|
paddingTop: paddingBT + raisedeHeight + 'px', |
||||
|
paddingBottom: paddingBT + 'px', |
||||
|
width: '100%' |
||||
|
}" |
||||
|
@tap="handleTap"> |
||||
|
<view :class="[ |
||||
|
'lb-tabbar-item__icon', |
||||
|
`lb-tabbar-item__icon--${raisede ? 'raisede' : 'default'}`, |
||||
|
isAnimate ? `animate__animated animate__${tabbarInfo.animate}` : '', |
||||
|
isActive ? 'lb-my-active' : '' |
||||
|
]" |
||||
|
:style="{ |
||||
|
height: tabbarInfo.iconSize, |
||||
|
lineHeight: tabbarInfo.iconSize, |
||||
|
transform: raisede ? `translateY(-${ty}px) scale(${tabbarInfo.raisedeScale})` : '' |
||||
|
}"> |
||||
|
<!-- 图片图标 --> |
||||
|
<image v-if="isImage" |
||||
|
class="lb-tabbar-item__image" |
||||
|
:src="icon" |
||||
|
:style="{ |
||||
|
width: tabbarInfo.iconSize, |
||||
|
height: tabbarInfo.iconSize |
||||
|
}"> |
||||
|
</image> |
||||
|
|
||||
|
<!-- icon图标 --> |
||||
|
<text v-else |
||||
|
:class="[ |
||||
|
'le', |
||||
|
iconPrefix, |
||||
|
`${iconPrefix}-${icon}`, |
||||
|
isActive ? 'lb-tabbar-item__icon--active' : '' |
||||
|
]" |
||||
|
:style="{ |
||||
|
width: tabbarInfo.iconSize, |
||||
|
height: tabbarInfo.iconSize, |
||||
|
lineHeight: tabbarInfo.iconSize, |
||||
|
fontSize: tabbarInfo.iconSize, |
||||
|
color: isActive ? tabbarInfo.activeColor : tabbarInfo.inactiveColor |
||||
|
}">{{ iconCode }}</text> |
||||
|
|
||||
|
<!-- 非nvue dot info显示 --> |
||||
|
<!-- #ifndef APP-NVUE --> |
||||
|
<text v-if="dot || hasInfo" |
||||
|
:class="[ |
||||
|
dot && !hasInfo ? 'lb-tabbar-item__dot' : '', |
||||
|
hasInfo ? 'lb-tabbar-item__dot--info' : '', |
||||
|
'lb-tabbar-item__dot--style' |
||||
|
]" |
||||
|
:style="{ |
||||
|
backgroundColor: tabbarInfo.dotColor |
||||
|
}">{{ hasInfo ? info : '' }}</text> |
||||
|
<!-- #endif --> |
||||
|
</view> |
||||
|
|
||||
|
<!-- nvue dot info 显示 --> |
||||
|
<!-- #ifdef APP-NVUE --> |
||||
|
<text v-if="dot || hasInfo" |
||||
|
:class="[ |
||||
|
'lb-tabbar-item__dot--nvue', |
||||
|
'lb-tabbar-item__dot--style', |
||||
|
dot && !hasInfo ? 'lb-tabbar-item__dot' : '', |
||||
|
hasInfo ? 'lb-tabbar-item__dot--info' : '', |
||||
|
hasInfo ? 'lb-tabbar-item__dot--info' : '', |
||||
|
nvueDotShow ? 'lb-tabbar-item__dot--show' : '' |
||||
|
]" |
||||
|
:style="{ |
||||
|
backgroundColor: tabbarInfo.dotColor, |
||||
|
top: paddingBT + raisedeHeight + 'px', |
||||
|
right: dotLeft + 'px' |
||||
|
}">{{ hasInfo ? info : '' }}</text> |
||||
|
<!-- #endif --> |
||||
|
|
||||
|
<!-- 非nvue图标文字 --> |
||||
|
<!-- #ifndef APP-NVUE --> |
||||
|
<view :class="[ |
||||
|
'lb-tabbar-item__text', |
||||
|
isActive ? 'lb-tabbar-item__text--active' : '' |
||||
|
]" |
||||
|
:style="{ |
||||
|
fontSize: tabbarInfo.textSize, |
||||
|
lineHeight: tabbarInfo.textSize, |
||||
|
maxHeight: tabbarInfo.textSize, |
||||
|
marginTop: tabbarInfo.textTop, |
||||
|
color: isActive |
||||
|
? tabbarInfo.activeTextColor || tabbarInfo.activeColor |
||||
|
: tabbarInfo.inactiveTextColor || tabbarInfo.inactiveColor |
||||
|
}"> |
||||
|
<slot></slot> |
||||
|
<view v-if="raisede" |
||||
|
class="lb-tabbar-item__text--block" |
||||
|
:style="{ |
||||
|
height: tabbarInfo.textSize |
||||
|
}"> |
||||
|
</view> |
||||
|
</view> |
||||
|
<!-- #endif --> |
||||
|
|
||||
|
<!-- nvue图标文字 --> |
||||
|
<!-- #ifdef APP-NVUE --> |
||||
|
<text v-if="text || raisede" |
||||
|
:class="[ |
||||
|
'lb-tabbar-item__text', |
||||
|
isActive ? 'lb-tabbar-item__text--active' : '' |
||||
|
]" |
||||
|
:style="{ |
||||
|
fontSize: tabbarInfo.textSize, |
||||
|
height: tabbarInfo.textSize, |
||||
|
lineHeight: tabbarInfo.textSize, |
||||
|
marginTop: tabbarInfo.textTop, |
||||
|
color: isActive |
||||
|
? tabbarInfo.activeTextColor || tabbarInfo.activeColor |
||||
|
: tabbarInfo.inactiveTextColor || tabbarInfo.inactiveColor |
||||
|
}">{{ text || '' }}</text> |
||||
|
<!-- #endif --> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
// #ifdef APP-NVUE |
||||
|
const dom = uni.requireNativePlugin('dom') |
||||
|
// #endif |
||||
|
import { getPx } from './utils' |
||||
|
export default { |
||||
|
props: { |
||||
|
name: [String, Number], |
||||
|
text: [String, Number], |
||||
|
icon: String, |
||||
|
iconPrefix: String, |
||||
|
dot: Boolean, |
||||
|
info: [String, Number], |
||||
|
raisede: Boolean |
||||
|
}, |
||||
|
inject: ['tabbar'], |
||||
|
data () { |
||||
|
return { |
||||
|
tabbarInfo: {}, |
||||
|
itemWidth: 0, |
||||
|
dotLeft: 0, |
||||
|
nvueDotShow: false |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
isImage () { |
||||
|
return this.icon && this.icon.indexOf('/') > -1 |
||||
|
}, |
||||
|
isActive () { |
||||
|
return this.tabbarInfo.value === this.name |
||||
|
}, |
||||
|
isAnimate () { |
||||
|
return ( |
||||
|
this.isActive && |
||||
|
this.tabbarInfo.animate && |
||||
|
!(this.raisede && this.tabbarInfo.closeAnimateOnRaisede) |
||||
|
) |
||||
|
}, |
||||
|
height () { |
||||
|
return getPx(this.tabbarInfo.height) |
||||
|
}, |
||||
|
iconHeight () { |
||||
|
return getPx(this.tabbarInfo.iconSize) |
||||
|
}, |
||||
|
textSize () { |
||||
|
return getPx(this.tabbarInfo.textSize) |
||||
|
}, |
||||
|
textTop () { |
||||
|
return getPx(this.tabbarInfo.textTop) |
||||
|
}, |
||||
|
ty () { |
||||
|
return this.height / 2 - (this.textSize + this.textTop) / 2 |
||||
|
}, |
||||
|
iconCode () { |
||||
|
let code = '' |
||||
|
// #ifdef APP-NVUE |
||||
|
code = this.icon |
||||
|
// #endif |
||||
|
return code |
||||
|
}, |
||||
|
hasInfo () { |
||||
|
return this.info || this.info === 0 |
||||
|
}, |
||||
|
paddingBT () { |
||||
|
return (this.height - this.iconHeight - this.textSize - this.textTop) / 2 |
||||
|
}, |
||||
|
hasRaisede () { |
||||
|
return this.tabbar.hasRaisede |
||||
|
}, |
||||
|
raisedeHeight () { |
||||
|
return this.hasRaisede ? this.iconHeight * this.tabbarInfo.raisedeScale / 2 : 0 // 凸起高度 |
||||
|
}, |
||||
|
infoLength () { |
||||
|
return this.hasInfo ? (this.info + '').length : 0 |
||||
|
} |
||||
|
}, |
||||
|
created () { |
||||
|
this.tabbarInfo = this.tabbar._props |
||||
|
this.tabbar.tabbarItems.push(this._props) |
||||
|
}, |
||||
|
mounted() { |
||||
|
// #ifdef APP-NVUE |
||||
|
this.setNvueDot() |
||||
|
// #endif |
||||
|
}, |
||||
|
methods: { |
||||
|
handleTap () { |
||||
|
this.tabbar.active = this.name |
||||
|
this.$emit('click', this._props) |
||||
|
}, |
||||
|
// #ifdef APP-NVUE |
||||
|
setNvueDot () { |
||||
|
if (this.dot || this.hasInfo) { |
||||
|
this.$nextTick(() => { |
||||
|
const $el = this.$refs.item |
||||
|
dom.getComponentRect($el, res => { |
||||
|
this.itemWidth = res.size.width |
||||
|
const halfWidth = this.itemWidth / 2 |
||||
|
if (this.dot) { |
||||
|
this.dotLeft = halfWidth - 8 |
||||
|
} |
||||
|
if (this.hasInfo) { |
||||
|
const length = this.infoLength > 1 ? this.infoLength -1 : this.infoLength |
||||
|
this.dotLeft = halfWidth - 8 * length |
||||
|
} |
||||
|
this.nvueDotShow = true |
||||
|
}) |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
// #endif |
||||
|
}, |
||||
|
// #ifdef APP-NVUE |
||||
|
watch: { |
||||
|
dot () { |
||||
|
this.setNvueDot() |
||||
|
}, |
||||
|
info () { |
||||
|
this.setNvueDot() |
||||
|
} |
||||
|
} |
||||
|
// #endif |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
@import "./style.scss"; |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
@import "./animate.scss"; |
||||
|
/* #endif */ |
||||
|
</style> |
||||
@ -0,0 +1,179 @@ |
|||||
|
<template> |
||||
|
<view class="lb-tabbar"> |
||||
|
<view :class="[ |
||||
|
'lb-tabbar-content', |
||||
|
fixed ? 'lb-tabbar--fixed' : '' |
||||
|
]" |
||||
|
:style="{ |
||||
|
backgroundColor: bgColor, |
||||
|
paddingBottom: `${safeAreaHeight}px` |
||||
|
}"> |
||||
|
<view v-if="border" |
||||
|
class="lb-tabbar-border" |
||||
|
:style="{ |
||||
|
backgroundColor: borderColor, |
||||
|
top: raisedeHeight + 'px' |
||||
|
}"> |
||||
|
</view> |
||||
|
<slot></slot> |
||||
|
</view> |
||||
|
<view v-if="placeholder" |
||||
|
class="lb-tabbar-placeholder" |
||||
|
:style="{ |
||||
|
height: `${tabbarHeight}px` |
||||
|
}"> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
// git https://github.com/liub1934/uni-lb-tabbar/tree/master/pages/demos |
||||
|
// demo https://github.liubing.me/uni-lb-tabbar/#/ |
||||
|
// fileDes https://ext.dcloud.net.cn/plugin?id=4124 |
||||
|
|
||||
|
const SAFE_AREA_INSET_BOTTOM = 34 |
||||
|
import { getPx } from './utils' |
||||
|
export default { |
||||
|
props: { |
||||
|
value: [String, Number], |
||||
|
height: { |
||||
|
type: String, |
||||
|
default: '50px' |
||||
|
}, |
||||
|
iconSize: { |
||||
|
type: String, |
||||
|
default: '22px' |
||||
|
}, |
||||
|
textSize: { |
||||
|
type: String, |
||||
|
default: '12px' |
||||
|
}, |
||||
|
textTop: { |
||||
|
type: String, |
||||
|
default: '5px' |
||||
|
}, |
||||
|
dotColor: { |
||||
|
type: String, |
||||
|
default: '#F56C6C' |
||||
|
}, |
||||
|
fixed: { |
||||
|
type: Boolean, |
||||
|
default: true |
||||
|
}, |
||||
|
placeholder: { |
||||
|
type: Boolean, |
||||
|
default: true |
||||
|
}, |
||||
|
animate: String, |
||||
|
closeAnimateOnRaisede: { |
||||
|
type: Boolean, |
||||
|
default: true |
||||
|
}, |
||||
|
border: { |
||||
|
type: Boolean, |
||||
|
default: true |
||||
|
}, |
||||
|
borderColor: { |
||||
|
type: String, |
||||
|
default: '#DCDFE6' |
||||
|
}, |
||||
|
bgColor: { |
||||
|
type: String, |
||||
|
default: '#FFF' |
||||
|
}, |
||||
|
inactiveColor: { |
||||
|
type: String, |
||||
|
default: '#909399' |
||||
|
}, |
||||
|
activeColor: { |
||||
|
type: String, |
||||
|
default: '#409EFF' |
||||
|
}, |
||||
|
inactiveTextColor: String, |
||||
|
activeTextColor: String, |
||||
|
safeAreaInsetBottom: { |
||||
|
type: Boolean, |
||||
|
default: true |
||||
|
}, |
||||
|
hideTabbar: { |
||||
|
type: Boolean, |
||||
|
default: true |
||||
|
}, |
||||
|
raisedeScale: { |
||||
|
type: Number, |
||||
|
default: 2 |
||||
|
} |
||||
|
}, |
||||
|
data () { |
||||
|
return { |
||||
|
active: this.value, |
||||
|
activeItem: {}, |
||||
|
tabbarItems: [], |
||||
|
hasRaisede: false, |
||||
|
isIphoneX: false |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
tabbarItemsLength () { |
||||
|
return this.tabbarItems.length |
||||
|
}, |
||||
|
safeAreaHeight () { |
||||
|
return this.isIphoneX && this.safeAreaInsetBottom ? SAFE_AREA_INSET_BOTTOM : 0 // 苹果X等机型安全区高度 |
||||
|
}, |
||||
|
iconHeight () { |
||||
|
return getPx(this.iconSize) |
||||
|
}, |
||||
|
raisedeHeight () { |
||||
|
return this.hasRaisede ? this.iconHeight * this.raisedeScale / 2 : 0 // 凸起高度 |
||||
|
}, |
||||
|
tabbarHeight () { |
||||
|
const height = getPx(this.height) // 默认高度 |
||||
|
const raisedeHeight = this.hasRaisede ? this.iconHeight * this.raisedeScale / 2 : 0 // 凸起高度 |
||||
|
const tabbarHeight = height + this.safeAreaHeight + raisedeHeight // 整体高度 |
||||
|
return tabbarHeight |
||||
|
} |
||||
|
}, |
||||
|
provide () { |
||||
|
return { |
||||
|
tabbar: this |
||||
|
} |
||||
|
}, |
||||
|
created () { |
||||
|
if (this.hideTabbar) { |
||||
|
uni.hideTabBar() |
||||
|
} |
||||
|
const res = uni.getSystemInfoSync() |
||||
|
const { model, statusBarHeight } = res |
||||
|
if ( |
||||
|
(model.indexOf('iPhone') > -1 && statusBarHeight > 20) || |
||||
|
model.indexOf('iPhone X') > -1 || |
||||
|
model.indexOf('iPhone 1') > -1 |
||||
|
) { |
||||
|
this.isIphoneX = true |
||||
|
} |
||||
|
this.getTabbarHeight() |
||||
|
}, |
||||
|
methods: { |
||||
|
getTabbarHeight () { |
||||
|
return this.tabbarHeight |
||||
|
} |
||||
|
}, |
||||
|
watch: { |
||||
|
value (newVal) { |
||||
|
this.active = newVal |
||||
|
}, |
||||
|
active (newVal) { |
||||
|
this.activeItem = this.tabbarItems.find(item => item.name === newVal) |
||||
|
this.$emit('input', newVal) |
||||
|
this.$emit('change', this.activeItem) |
||||
|
}, |
||||
|
tabbarItemsLength () { |
||||
|
this.hasRaisede = !!this.tabbarItems.find(item => item.raisede) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
@import "./style.scss"; |
||||
|
</style> |
||||
@ -0,0 +1,121 @@ |
|||||
|
.lb-tabbar-content { |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
display: flex; |
||||
|
/* #endif */ |
||||
|
flex-direction: row; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.lb-tabbar--fixed { |
||||
|
position: fixed; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
} |
||||
|
|
||||
|
.lb-tabbar-border { |
||||
|
height: 1px; |
||||
|
position: absolute; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
top: 0; |
||||
|
transform: scaleY(0.5); |
||||
|
} |
||||
|
|
||||
|
/* #ifndef APP-NVUE */ |
||||
|
lb-tabbar-item { |
||||
|
flex: 1; |
||||
|
} |
||||
|
|
||||
|
/* #endif */ |
||||
|
|
||||
|
.lb-tabbar-item { |
||||
|
flex: 1; |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
display: flex; |
||||
|
/* #endif */ |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
position: relative; |
||||
|
// justify-content: flex-end; |
||||
|
} |
||||
|
|
||||
|
.lb-tabbar-item__text { |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.lb-tabbar-item__text--active { |
||||
|
transition-property: color; |
||||
|
transition-duration: 0.3s; |
||||
|
} |
||||
|
|
||||
|
.lb-tabbar-item__icon { |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
.lb-tabbar-icon { |
||||
|
position: relative; |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
line-height: 1; |
||||
|
font-size: inherit; |
||||
|
text-rendering: auto; |
||||
|
-webkit-font-smoothing: antialiased; |
||||
|
/* #endif */ |
||||
|
} |
||||
|
|
||||
|
.lb-tabbar-item__icon--active { |
||||
|
transition-property: color; |
||||
|
transition-duration: 0.3s; |
||||
|
} |
||||
|
|
||||
|
.lb-my-active{ |
||||
|
transform: translateY(-16.5px) scale(2) !important; |
||||
|
color: yellow; |
||||
|
} |
||||
|
.lb-tabbar-item__icon--raisede { |
||||
|
background-color: #fff; |
||||
|
border-radius: 9999px; |
||||
|
position: relative; |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
z-index: 1; |
||||
|
/* #endif */ |
||||
|
} |
||||
|
|
||||
|
.lb-tabbar-item__dot--style { |
||||
|
border-radius: 9999px; |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
right: 0; |
||||
|
} |
||||
|
|
||||
|
.lb-tabbar-item__dot { |
||||
|
width: 8px; |
||||
|
height: 8px; |
||||
|
} |
||||
|
|
||||
|
.lb-tabbar-item__dot--nvue { |
||||
|
opacity: 0; |
||||
|
transition-property: opacity; |
||||
|
transition-duration: 0.1s; |
||||
|
} |
||||
|
|
||||
|
.lb-tabbar-item__dot--show { |
||||
|
opacity: 1; |
||||
|
} |
||||
|
|
||||
|
.lb-tabbar-item__dot--info { |
||||
|
height: 14px; |
||||
|
line-height: 14px; |
||||
|
/* #ifdef APP-NVUE */ |
||||
|
flex: 1; |
||||
|
/* #endif */ |
||||
|
margin-top: 5px; |
||||
|
font-size: 12px; |
||||
|
padding-left: 4px; |
||||
|
padding-right: 4px; |
||||
|
text-align: center; |
||||
|
color: #fff; |
||||
|
transform: translate(50%, -50%); |
||||
|
} |
||||
@ -0,0 +1,10 @@ |
|||||
|
export function getPx (val) { |
||||
|
if (!val) return 0 |
||||
|
if (val.indexOf('rpx') > -1) { |
||||
|
val = +val.replace('rpx', '') |
||||
|
val = uni.upx2px(val) |
||||
|
} else { |
||||
|
val = +val.replace('px', '') |
||||
|
} |
||||
|
return val |
||||
|
} |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue