Files
corner/src/components/Navigation.vue
T
2026-04-24 09:21:32 +00:00

230 lines
7.6 KiB
Vue

<template>
<div id="nav" class="fbox-v"
:class="(currentRoute) + ' ' + (menuOpen ? 'open' : '')">
<div id="menu" @click="showMenu"><i class="fas fa-bars"></i></div>
<div id="items" class="fbox-v">
<a class="router-link" :ref="setNavRef('others')" href="/others">更多</a>
<div class="dot">·</div>
<a class="router-link" :ref="setNavRef('photo')" href="/photo">相册</a>
<div class="dot">·</div>
<a class="router-link" :ref="setNavRef('blog')" href="/blog">记事本</a>
<div class="dot">·</div>
<a class="router-link" :ref="setNavRef('life')" href="/life">生活</a>
<div class="dot">·</div>
<a class="router-link" :ref="setNavRef('about')" href="/about">关于</a>
<div class="dot">·</div>
<a class="router-link" :ref="setNavRef('home')" href="/">
<svg focusable="false" data-prefix="fal" data-icon="house-night" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" class="svg-inline--fa fa-house-night fa-w-20"><path fill="currentColor" d="M112,224a111.5,111.5,0,0,0,87-41.45,20.51,20.51,0,0,0-19.75-33.08A59.2,59.2,0,0,1,138.84,39.85a20.3,20.3,0,0,0,10.07-21.27,20.26,20.26,0,0,0-16.47-16.7A136,136,0,0,0,112,0a112,112,0,0,0,0,224ZM97.78,33.27a91.21,91.21,0,0,0,54.47,147.9A80,80,0,1,1,97.78,33.27Zm97.15,35.51,39.72,16.56,16.56,39.72a5.33,5.33,0,0,0,9.55,0l16.56-39.72L317,68.78a5.33,5.33,0,0,0,0-9.54L277.32,42.68,260.76,3a5.33,5.33,0,0,0-9.55,0L234.65,42.68,194.93,59.24a5.34,5.34,0,0,0,0,9.54ZM157,379.24l-39.72-16.57L100.76,323a5.34,5.34,0,0,0-9.55,0L74.65,362.67,34.93,379.24a5.34,5.34,0,0,0,0,9.54l39.72,16.56,16.56,39.72a5.33,5.33,0,0,0,9.55,0l16.56-39.72L157,388.78a5.33,5.33,0,0,0,0-9.54Zm179-101.9v85.33A21.39,21.39,0,0,0,357.36,384h85.31A21.39,21.39,0,0,0,464,362.67V277.34A21.4,21.4,0,0,0,442.67,256H357.36A21.4,21.4,0,0,0,336,277.34ZM368,288H432v64H368Zm266.49,8L576,244.75V144a16,16,0,0,0-32,0v72.75L410.53,100a16,16,0,0,0-21.07,0l-224,196a16,16,0,0,0,21.07,24.09L224,287.28V464a48.05,48.05,0,0,0,48,48H528a48.06,48.06,0,0,0,48-48V287.28l37.46,32.78A16,16,0,0,0,634.53,296ZM544,464a16,16,0,0,1-16,16H272a16,16,0,0,1-16-16V264a15.94,15.94,0,0,0-.81-4L400,133.27l144,126Z" class=""></path></svg>
</a>
</div>
<div id="nav-bookmark" ref="bookmark" :style="bookmarkCss"></div>
<div id="nav-background"></div>
<img id="meru" src="/meru_256px.png" alt="">
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, nextTick, ComponentPublicInstance } from 'vue';
const props = defineProps<{
currentPath: string;
routeName: string;
navBookmark?: string;
}>();
const currentRoute = ref(props.navBookmark?.toLowerCase() || props.routeName?.toLowerCase() || 'home');
const bookmarkCss = ref('');
const lastTop = ref(0);
const menuOpen = ref(false);
const bookmarkUpdateIntervalId = ref<number | null>(null);
const navRefs = ref<Record<string, Element | ComponentPublicInstance | null>>({});
const showMenu = (): void => {
menuOpen.value = !menuOpen.value;
if (menuOpen.value) setTimeout(() => menuOpen.value = false, 2000);
}
const setNavRef = (name: string) => (el: Element | ComponentPublicInstance | null) => {
navRefs.value[name] = el;
}
const resolveNavElement = (target: Element | ComponentPublicInstance | null | undefined): Element | null => {
if (!target) return null;
if (target instanceof Element) return target;
return (target.$el as Element | undefined) ?? null;
}
const calculateBookmarkCss = (): void => {
const currentLink = resolveNavElement(navRefs.value[currentRoute.value]) as HTMLElement;
if (!currentLink) return;
const top = currentLink.offsetTop;
const h = currentLink.offsetHeight;
if (top === lastTop.value) return;
lastTop.value = top;
const width = Math.round(h / 2) + 8;
bookmarkCss.value = `top: ${top - 8}px;` +
`border-width: ${width}px 20px ${width}px 50px;`;
}
onMounted(() => {
calculateBookmarkCss();
window.addEventListener('resize', calculateBookmarkCss, true);
bookmarkUpdateIntervalId.value = window.setInterval(calculateBookmarkCss, 1000);
});
onUnmounted(() => {
window.removeEventListener('resize', calculateBookmarkCss, true);
if (bookmarkUpdateIntervalId.value !== null) {
window.clearInterval(bookmarkUpdateIntervalId.value);
}
});
</script>
<style lang="sass">
#nav
position: fixed
left: 0
height: 100%
font-size: 1.4em
align-items: flex-start
z-index: 100
pointer-events: none
#menu
padding: 5px 10px
margin: 15px 10px
background-color: rgba(255, 255, 255, 0.49)
border-radius: 10px
text-shadow: -1px -1px 1px rgba(255,255,255,.1), 1px 1px 1px rgba(0,0,0,.5)
filter: drop-shadow(0 0 30px rgb(255, 255, 255))
z-index: 100
pointer-events: auto
opacity: 0
#nav-bookmark
position: absolute
left: 0
width: 20px
height: 0
z-index: 5
--bookmark-color: rgb(255, 225, 230)
border-color: var(--bookmark-color) transparent var(--bookmark-color) var(--bookmark-color)
border-style: solid
border-width: 20px
#items
justify-content: flex-end
z-index: 10
.router-link
color: rgba(128, 112, 92, 0.71)
position: relative
z-index: 100
pointer-events: auto
.dot
content: '·'
margin: 20px 0
user-select: none
.router-link, .dot
text-decoration: none
writing-mode: vertical-rl
text-orientation: sideways
transform: scale(-1)
padding-right: 20px
#nav-background
position: absolute
height: 100%
width: 100px
left: 0
$nav-bg-color: #f9f2e0
border-left: 20px solid $nav-bg-color
background: linear-gradient(to right, $nav-bg-color, transparent)
z-index: 4
pointer-events: none
#meru
height: 160px
z-index: 100
pointer-events: auto
svg
display: inline-block
height: 1em
transform: rotate(180deg)
*
box-sizing: inherit
@media screen and (max-width: 800px)
#nav #menu
opacity: 1
#nav.home
#menu, #nav-background
opacity: 0
#nav:not(.home).open
#items, #nav-bookmark, #nav-background, #meru
opacity: 1
animation: fade-in-left .5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both
#nav:not(.home)
#items, #nav-bookmark, #nav-background, #meru
opacity: 0
animation: fade-out-left 1s 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both
@media screen and (max-width: 500px), (max-height: 660px)
#nav
#nav-bookmark
width: 10px
#meru
height: 120px
@media screen and (max-width: 370px)
#nav #menu
opacity: unset
#nav.home
#menu, #nav-background
opacity: unset
#nav.open
#items, #nav-bookmark, #nav-background, #meru
opacity: 1
animation: fade-in-left .5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both
#nav
#items, #nav-bookmark, #nav-background, #meru
opacity: 0
animation: fade-out-left 1s 0.5s cubic-bezier(0.250, 0.460, 0.450, 0.940) both
@media screen and (max-height: 600px)
#nav
.dot
margin: 10px 0
@media screen and (max-height: 500px)
#nav
.router-link
margin-bottom: 10px
.dot
display: none
</style>