26 changed files with 552 additions and 23 deletions
@ -0,0 +1,214 @@ |
|||||
|
<template> |
||||
|
<view> |
||||
|
<view class="overlay" v-if="showMenu" @click="onCloseMenu"></view> |
||||
|
<view class="fixed-nav" :class="{'hidden': showMenu}" @click="onShowMenu"> |
||||
|
<image class="nav-icon" :class="{'rotated': iconRotated, 'bounce-back': iconBounceBack}" :src="navIconSrc" |
||||
|
mode="aspectFill"></image> |
||||
|
</view> |
||||
|
<view class="nav-menu" :class="{'show': showMenu}"> |
||||
|
<view class="nav-item" :class="{'item-active': isItemActive(item)}" v-for="item in menuItems" |
||||
|
:key="item.targetIndex" @click="() => onJumpToPage(item)"> |
||||
|
<view v-if="item.text.includes('#Chapter')" class="chapter-text"> |
||||
|
<text class="chapter-title">#Chapter</text> |
||||
|
<text :class="{'active': isItemActive(item)}" class="chapter-number"> |
||||
|
{{ item.text.replace('#Chapter', '') }} |
||||
|
</text> |
||||
|
</view> |
||||
|
<text v-else :class="{'active': isItemActive(item)}">{{ item.text }}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
props: { |
||||
|
// 当前激活的索引 |
||||
|
navIndex: { |
||||
|
type: Number, |
||||
|
required: true |
||||
|
}, |
||||
|
// 导航图标地址 |
||||
|
navIconSrc: { |
||||
|
type: String, |
||||
|
default: 'https://static.ticket.sz-trip.com/epicSoul/taozi/nav-icon.png' |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
showMenu: false, |
||||
|
iconRotated: false, |
||||
|
iconBounceBack: false, |
||||
|
menuItems: [ |
||||
|
{ |
||||
|
text: '购物车', |
||||
|
targetIndex: 7, |
||||
|
path: "/subPackages/user/gwc" |
||||
|
} |
||||
|
], |
||||
|
}; |
||||
|
}, |
||||
|
watch: { |
||||
|
navIndex(newVal) { |
||||
|
console.log(newVal) |
||||
|
if(newVal) this.navIndex = newVal |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
onShowMenu() { |
||||
|
this.iconRotated = true; |
||||
|
setTimeout(() => { |
||||
|
this.showMenu = true; |
||||
|
this.$emit('menu-show'); |
||||
|
}, 300); |
||||
|
}, |
||||
|
onCloseMenu() { |
||||
|
this.showMenu = false; |
||||
|
setTimeout(() => { |
||||
|
this.iconBounceBack = true; |
||||
|
this.iconRotated = false; |
||||
|
setTimeout(() => { |
||||
|
this.iconBounceBack = false; |
||||
|
}, 500); |
||||
|
}, 300); |
||||
|
this.$emit('menu-hide'); |
||||
|
}, |
||||
|
onJumpToPage(item) { |
||||
|
console.log(this.navIndex) |
||||
|
if(item.path && item.targetIndex != this.navIndex) this.gotoPath(item.path) |
||||
|
this.onCloseMenu(); |
||||
|
}, |
||||
|
isItemActive(item) { |
||||
|
return this.navIndex === item.targetIndex; |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.overlay { |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
background-color: rgba(0, 0, 0, 0.3); |
||||
|
z-index: 100; |
||||
|
} |
||||
|
|
||||
|
.fixed-nav { |
||||
|
width: 80rpx; |
||||
|
height: 80rpx; |
||||
|
background-color: rgb(0 0 0 / 0.7); |
||||
|
border-radius: 10rpx 0 0 10rpx; |
||||
|
position: fixed; |
||||
|
right: 0; |
||||
|
top: 0; |
||||
|
bottom: 0; |
||||
|
margin: auto 0; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
z-index: 9; |
||||
|
transition: transform 0.3s ease, opacity 0.3s ease; |
||||
|
} |
||||
|
|
||||
|
.fixed-nav.hidden { |
||||
|
transform: translateX(100%); |
||||
|
opacity: 0; |
||||
|
pointer-events: none; |
||||
|
} |
||||
|
|
||||
|
.nav-icon { |
||||
|
width: 35rpx; |
||||
|
height: 35rpx; |
||||
|
transition: transform 0.3s ease; |
||||
|
} |
||||
|
|
||||
|
.nav-icon.rotated { |
||||
|
transform: rotate(180deg); |
||||
|
} |
||||
|
|
||||
|
.nav-icon.bounce-back { |
||||
|
animation: bounceRotation 0.5s ease; |
||||
|
} |
||||
|
|
||||
|
@keyframes bounceRotation { |
||||
|
0% { |
||||
|
transform: rotate(180deg); |
||||
|
} |
||||
|
|
||||
|
50% { |
||||
|
transform: rotate(-20deg); |
||||
|
} |
||||
|
|
||||
|
75% { |
||||
|
transform: rotate(10deg); |
||||
|
} |
||||
|
|
||||
|
100% { |
||||
|
transform: rotate(0deg); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.nav-menu { |
||||
|
position: fixed; |
||||
|
top: 50%; |
||||
|
right: 0; |
||||
|
transform: translate(100%, -50%); |
||||
|
z-index: 999; |
||||
|
background-color: rgba(255, 255, 255, 0.95); |
||||
|
border-radius: 16rpx 0 0 16rpx; |
||||
|
box-shadow: -4px 0 15px rgba(0, 0, 0, 0.1); |
||||
|
transition: transform 0.3s ease; |
||||
|
} |
||||
|
|
||||
|
.nav-menu.show { |
||||
|
transform: translate(0, -50%); |
||||
|
} |
||||
|
|
||||
|
.nav-item { |
||||
|
padding: 20rpx; |
||||
|
text-align: center; |
||||
|
|
||||
|
text { |
||||
|
color: #333; |
||||
|
opacity: 0.7; |
||||
|
font-size: 28rpx; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.item-active { |
||||
|
background-color: rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
.nav-item .active { |
||||
|
color: #333; |
||||
|
opacity: 1; |
||||
|
} |
||||
|
|
||||
|
.chapter-text { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
line-height: 1.3; |
||||
|
} |
||||
|
|
||||
|
.chapter-title { |
||||
|
color: #fff; |
||||
|
opacity: 0.7; |
||||
|
font-size: 24rpx; |
||||
|
} |
||||
|
|
||||
|
.chapter-number { |
||||
|
color: #fff; |
||||
|
opacity: 0.7; |
||||
|
font-size: 28rpx; |
||||
|
margin-top: 8rpx; |
||||
|
} |
||||
|
|
||||
|
.item-active .chapter-title, |
||||
|
.item-active .chapter-number.active { |
||||
|
opacity: 1; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,214 @@ |
|||||
|
<template> |
||||
|
<view> |
||||
|
<view class="overlay" v-if="showMenu" @click="onCloseMenu"></view> |
||||
|
<view class="fixed-nav" :class="{'hidden': showMenu}" @click="onShowMenu"> |
||||
|
<image class="nav-icon" :class="{'rotated': iconRotated, 'bounce-back': iconBounceBack}" :src="navIconSrc" |
||||
|
mode="aspectFill"></image> |
||||
|
</view> |
||||
|
<view class="nav-menu" :class="{'show': showMenu}"> |
||||
|
<view class="nav-item" :class="{'item-active': isItemActive(item)}" v-for="item in menuItems" |
||||
|
:key="item.targetIndex" @click="() => onJumpToPage(item)"> |
||||
|
<view v-if="item.text.includes('#Chapter')" class="chapter-text"> |
||||
|
<text class="chapter-title">#Chapter</text> |
||||
|
<text :class="{'active': isItemActive(item)}" class="chapter-number"> |
||||
|
{{ item.text.replace('#Chapter', '') }} |
||||
|
</text> |
||||
|
</view> |
||||
|
<text v-else :class="{'active': isItemActive(item)}">{{ item.text }}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
props: { |
||||
|
// 当前激活的索引 |
||||
|
navIndex: { |
||||
|
type: Number, |
||||
|
required: true |
||||
|
}, |
||||
|
// 导航图标地址 |
||||
|
navIconSrc: { |
||||
|
type: String, |
||||
|
default: 'https://static.ticket.sz-trip.com/epicSoul/taozi/nav-icon.png' |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
showMenu: false, |
||||
|
iconRotated: false, |
||||
|
iconBounceBack: false, |
||||
|
menuItems: [ |
||||
|
{ |
||||
|
text: '购物车', |
||||
|
targetIndex: 7, |
||||
|
path: "/subPackages/user/gwc" |
||||
|
} |
||||
|
], |
||||
|
}; |
||||
|
}, |
||||
|
watch: { |
||||
|
navIndex(newVal) { |
||||
|
console.log(newVal) |
||||
|
if(newVal) this.navIndex = newVal |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
onShowMenu() { |
||||
|
this.iconRotated = true; |
||||
|
setTimeout(() => { |
||||
|
this.showMenu = true; |
||||
|
this.$emit('menu-show'); |
||||
|
}, 300); |
||||
|
}, |
||||
|
onCloseMenu() { |
||||
|
this.showMenu = false; |
||||
|
setTimeout(() => { |
||||
|
this.iconBounceBack = true; |
||||
|
this.iconRotated = false; |
||||
|
setTimeout(() => { |
||||
|
this.iconBounceBack = false; |
||||
|
}, 500); |
||||
|
}, 300); |
||||
|
this.$emit('menu-hide'); |
||||
|
}, |
||||
|
onJumpToPage(item) { |
||||
|
console.log(this.navIndex) |
||||
|
if(item.path && item.targetIndex != this.navIndex) this.gotoPath(item.path) |
||||
|
this.onCloseMenu(); |
||||
|
}, |
||||
|
isItemActive(item) { |
||||
|
return this.navIndex === item.targetIndex; |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.overlay { |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
background-color: rgba(0, 0, 0, 0.3); |
||||
|
z-index: 100; |
||||
|
} |
||||
|
|
||||
|
.fixed-nav { |
||||
|
width: 80rpx; |
||||
|
height: 80rpx; |
||||
|
background-color: rgb(0 0 0 / 0.7); |
||||
|
border-radius: 10rpx 0 0 10rpx; |
||||
|
position: fixed; |
||||
|
right: 0; |
||||
|
top: 0; |
||||
|
bottom: 0; |
||||
|
margin: auto 0; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
z-index: 9; |
||||
|
transition: transform 0.3s ease, opacity 0.3s ease; |
||||
|
} |
||||
|
|
||||
|
.fixed-nav.hidden { |
||||
|
transform: translateX(100%); |
||||
|
opacity: 0; |
||||
|
pointer-events: none; |
||||
|
} |
||||
|
|
||||
|
.nav-icon { |
||||
|
width: 35rpx; |
||||
|
height: 35rpx; |
||||
|
transition: transform 0.3s ease; |
||||
|
} |
||||
|
|
||||
|
.nav-icon.rotated { |
||||
|
transform: rotate(180deg); |
||||
|
} |
||||
|
|
||||
|
.nav-icon.bounce-back { |
||||
|
animation: bounceRotation 0.5s ease; |
||||
|
} |
||||
|
|
||||
|
@keyframes bounceRotation { |
||||
|
0% { |
||||
|
transform: rotate(180deg); |
||||
|
} |
||||
|
|
||||
|
50% { |
||||
|
transform: rotate(-20deg); |
||||
|
} |
||||
|
|
||||
|
75% { |
||||
|
transform: rotate(10deg); |
||||
|
} |
||||
|
|
||||
|
100% { |
||||
|
transform: rotate(0deg); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.nav-menu { |
||||
|
position: fixed; |
||||
|
top: 50%; |
||||
|
right: 0; |
||||
|
transform: translate(100%, -50%); |
||||
|
z-index: 999; |
||||
|
background-color: rgba(255, 255, 255, 0.95); |
||||
|
border-radius: 16rpx 0 0 16rpx; |
||||
|
box-shadow: -4px 0 15px rgba(0, 0, 0, 0.1); |
||||
|
transition: transform 0.3s ease; |
||||
|
} |
||||
|
|
||||
|
.nav-menu.show { |
||||
|
transform: translate(0, -50%); |
||||
|
} |
||||
|
|
||||
|
.nav-item { |
||||
|
padding: 20rpx; |
||||
|
text-align: center; |
||||
|
|
||||
|
text { |
||||
|
color: #333; |
||||
|
opacity: 0.7; |
||||
|
font-size: 28rpx; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.item-active { |
||||
|
background-color: rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
.nav-item .active { |
||||
|
color: #333; |
||||
|
opacity: 1; |
||||
|
} |
||||
|
|
||||
|
.chapter-text { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
line-height: 1.3; |
||||
|
} |
||||
|
|
||||
|
.chapter-title { |
||||
|
color: #fff; |
||||
|
opacity: 0.7; |
||||
|
font-size: 24rpx; |
||||
|
} |
||||
|
|
||||
|
.chapter-number { |
||||
|
color: #fff; |
||||
|
opacity: 0.7; |
||||
|
font-size: 28rpx; |
||||
|
margin-top: 8rpx; |
||||
|
} |
||||
|
|
||||
|
.item-active .chapter-title, |
||||
|
.item-active .chapter-number.active { |
||||
|
opacity: 1; |
||||
|
} |
||||
|
</style> |
Loading…
Reference in new issue