156 lines
4.5 KiB
Vue
156 lines
4.5 KiB
Vue
<!-- Ported from legacy henza_pwa/src/components/ui/navigation.vue -->
|
|
<template>
|
|
<nav class="fixed bottom-0 w-full z-[10]" v-show="nav.showNav">
|
|
<div class="flex flex-row-reverse justify-around items-center absolute w-full h-[50px] z-[2] bottom-0">
|
|
<template v-for="item in nav.navBarRoutes" :key="item.index">
|
|
<router-link :to="{ name: item.name }" class="relative h-[50px]" @click="setLastPositionX(item)">
|
|
<div class="nav-item">
|
|
<Icon
|
|
:icon="item.icon"
|
|
class="cursor-pointer"
|
|
:style="{ transform: nav.activeLink === item.index ? `translateY(50px)` : undefined }"
|
|
size="25"
|
|
/>
|
|
</div>
|
|
</router-link>
|
|
</template>
|
|
</div>
|
|
<div class="active-nav-item absolute" :style="{ top: output[0] + 'px', left: output[1] + 'px' }">
|
|
<Icon v-if="activeItem?.icon === 'vuesax-linear:user'" icon="vuesax-linear:user" class="cursor-pointer" size="25" />
|
|
<Icon
|
|
v-if="activeItem?.icon === 'vuesax-linear:bag-happy'"
|
|
icon="vuesax-linear:bag-happy"
|
|
class="cursor-pointer"
|
|
size="25"
|
|
/>
|
|
<Icon
|
|
v-if="activeItem?.icon === 'vuesax-linear:search-normal-1'"
|
|
icon="vuesax-linear:search-normal-1"
|
|
class="cursor-pointer"
|
|
size="25"
|
|
/>
|
|
<Icon
|
|
v-if="activeItem?.icon === 'vuesax-linear:home-2'"
|
|
icon="vuesax-linear:home-2"
|
|
class="cursor-pointer"
|
|
size="25"
|
|
/>
|
|
</div>
|
|
|
|
<svg class="h-[50px] w-full">
|
|
<path id="nav" class="from-path" :d="shape" />
|
|
</svg>
|
|
</nav>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, ref, watch } from 'vue'
|
|
import { gsap } from 'gsap'
|
|
import { TransitionPresets, useTransition } from '@vueuse/core'
|
|
import Icon from '../icon/Icon.vue'
|
|
import { useNavStore } from '../../stores/navStore'
|
|
import { useWindowDataStore } from '../../stores/windowDataStore'
|
|
|
|
const nav = useNavStore()
|
|
const windowData = useWindowDataStore()
|
|
|
|
const shape = ref('')
|
|
|
|
const centershape = [95, 45]
|
|
const width = computed(() => windowData.width)
|
|
const centerOfCentershape = computed(() => {
|
|
return (width.value / nav.navBarRoutes.length) * nav.activeLink - width.value / (nav.navBarRoutes.length * 2)
|
|
})
|
|
|
|
const draw = () => {
|
|
const drowData = {
|
|
cornnerCurve: 10,
|
|
height: 50,
|
|
centerTopCurve: 20,
|
|
centerBottpmCurve: 32,
|
|
centershapeWidth: centershape[0],
|
|
centershapeHeight: centershape[1],
|
|
startOfCentershape: centerOfCentershape.value - centershape[0] / 2,
|
|
endOfCentershape: centerOfCentershape.value + centershape[0] / 2,
|
|
}
|
|
|
|
return `
|
|
M0,0
|
|
H${drowData.startOfCentershape}
|
|
C${drowData.startOfCentershape + drowData.centerTopCurve}, 0, ${centerOfCentershape.value - drowData.centerBottpmCurve}, ${drowData.centershapeHeight}, ${centerOfCentershape.value}, ${drowData.centershapeHeight}
|
|
C${centerOfCentershape.value + drowData.centerBottpmCurve},${drowData.centershapeHeight},${drowData.endOfCentershape - drowData.centerTopCurve},0,${drowData.endOfCentershape},0
|
|
H${width.value}
|
|
V50
|
|
H0
|
|
Z`
|
|
}
|
|
|
|
watch(width, (val) => {
|
|
if (val > 0) {
|
|
shape.value = draw()
|
|
}
|
|
})
|
|
watch(
|
|
() => nav.$state,
|
|
() => {
|
|
if (!shape.value) {
|
|
shape.value = draw()
|
|
} else {
|
|
gsap.to('.from-path', { duration: 0.3, attr: { d: draw() } })
|
|
}
|
|
},
|
|
{ deep: true },
|
|
)
|
|
shape.value = draw()
|
|
|
|
let lastPositionX = width.value
|
|
const activeItem = computed(() => {
|
|
const item = nav.navBarRoutes.find((x) => x.index === nav.activeLink)
|
|
setActiveIcon()
|
|
return item
|
|
})
|
|
|
|
// Legacy called this with an `item` argument that the function body never
|
|
// used (plain JS silently ignores extra args); kept as an unused optional
|
|
// param so behavior stays identical under TypeScript.
|
|
const setLastPositionX = (_item?: unknown) => {
|
|
lastPositionX = centerOfCentershape.value - centershape[0] / 2 + 25
|
|
}
|
|
const setActiveIcon = () => {
|
|
const positionX = centerOfCentershape.value - centershape[0] / 2 + 25
|
|
baseVector.value = [50, positionX + (lastPositionX - positionX) / 2]
|
|
setTimeout(() => {
|
|
baseVector.value = [-20, positionX]
|
|
}, 150)
|
|
}
|
|
|
|
const baseVector = ref([0, 0])
|
|
|
|
const output = useTransition(baseVector, {
|
|
duration: 150,
|
|
transition: TransitionPresets.easeInOutCubic,
|
|
})
|
|
</script>
|
|
|
|
<style scoped lang="less">
|
|
#nav {
|
|
opacity: 0.4;
|
|
fill: #dddddd;
|
|
}
|
|
|
|
.nav-item {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
width: 45px;
|
|
height: 45px;
|
|
}
|
|
|
|
.active-nav-item {
|
|
.nav-item;
|
|
background: @primary;
|
|
color: @white;
|
|
border-radius: 100%;
|
|
}
|
|
</style>
|