15 changed files with 197 additions and 785 deletions
-
72common/styles/iconfont.css
-
149components/lf-tabbar/animate.scss
-
182components/lf-tabbar/lf-tabbar-box.vue
-
259components/lf-tabbar/lf-tabbar-item.vue
-
128components/lf-tabbar/lf-tabbar.vue
-
121components/lf-tabbar/style.scss
-
10components/lf-tabbar/utils.js
-
16pages.json
-
5pages/discover/discover.vue
-
5pages/index/index/index.vue
-
5pages/store/cart/cart.vue
-
8pages/user/my/center.vue
-
1pages/user/my/my.vue
-
5pages/user/personal/personal.vue
-
16store/index.js
@ -1,149 +0,0 @@ |
|||
.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; |
|||
} |
|||
@ -1,182 +0,0 @@ |
|||
<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: { |
|||
type: String, |
|||
default: 'rubberBand' |
|||
}, |
|||
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: '#186c6b' |
|||
}, |
|||
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> |
|||
@ -1,259 +0,0 @@ |
|||
<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="[ |
|||
'lf-iconfont', |
|||
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, |
|||
path: String |
|||
}, |
|||
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> |
|||
@ -1,121 +0,0 @@ |
|||
.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%); |
|||
} |
|||
@ -1,10 +0,0 @@ |
|||
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