c9cf644ac5
- 添加 build-and-push.sh 脚本用于自动化构建和推送 Docker 镜像 - 更新 .dockerignore 排除 pnpm-workspace.yaml - 更新项目依赖和配置文件
122 lines
3.1 KiB
Vue
Executable File
122 lines
3.1 KiB
Vue
Executable File
<script setup lang="ts">
|
|
import { ref, onMounted, onBeforeUnmount } from 'vue'
|
|
import { useRouter } from 'vue-router'
|
|
import { useI18n } from 'vue-i18n'
|
|
import gsap from 'gsap'
|
|
import { ScrollTrigger } from 'gsap/ScrollTrigger'
|
|
|
|
gsap.registerPlugin(ScrollTrigger)
|
|
|
|
const props = withDefaults(defineProps<{ showText?: boolean; compact?: boolean; clickAction?: 'home' | 'top' }>(), {
|
|
showText: true,
|
|
compact: false,
|
|
clickAction: 'home',
|
|
})
|
|
|
|
const { t } = useI18n()
|
|
const router = useRouter()
|
|
|
|
const logoRef = ref<HTMLElement | null>(null)
|
|
let logoObserver: ReturnType<typeof ScrollTrigger.observe> | null = null
|
|
|
|
const handleClick = () => {
|
|
if (props.clickAction === 'home') {
|
|
router.push('/')
|
|
return
|
|
}
|
|
|
|
if (props.clickAction === 'top' && typeof window !== 'undefined') {
|
|
window.scrollTo({ top: 0, behavior: 'smooth' })
|
|
}
|
|
}
|
|
|
|
const logoHover = () => {
|
|
if (!props.showText) return
|
|
if (!logoRef.value) return
|
|
|
|
logoObserver?.kill()
|
|
|
|
// Ensure children exist before attempting to animate them
|
|
const firstChild = logoRef.value.children[0] as HTMLElement
|
|
const secondChild = logoRef.value.children[1] as HTMLElement
|
|
|
|
if (!firstChild || !secondChild) return
|
|
|
|
gsap.set(secondChild, {
|
|
opacity: 0,
|
|
visibility: 'hidden',
|
|
y: 0,
|
|
})
|
|
|
|
logoObserver = ScrollTrigger.observe({
|
|
target: logoRef.value,
|
|
onHover: (self) => {
|
|
gsap.to(firstChild, {
|
|
y: -10,
|
|
duration: 0.3,
|
|
ease: 'power2.out',
|
|
})
|
|
gsap.to(secondChild, {
|
|
y: 10,
|
|
duration: 0.3,
|
|
ease: 'power2.out',
|
|
opacity: 1,
|
|
visibility: 'visible',
|
|
})
|
|
},
|
|
onHoverEnd: (self) => {
|
|
gsap.to(firstChild, {
|
|
y: 0,
|
|
duration: 0.3,
|
|
ease: 'power2.out',
|
|
})
|
|
gsap.to(secondChild, {
|
|
y: 0,
|
|
duration: 0.3,
|
|
ease: 'power2.out',
|
|
opacity: 0,
|
|
visibility: 'hidden',
|
|
})
|
|
},
|
|
})
|
|
}
|
|
|
|
onMounted(() => {
|
|
logoHover()
|
|
})
|
|
|
|
onBeforeUnmount(() => {
|
|
logoObserver?.kill()
|
|
if (logoRef.value) {
|
|
gsap.killTweensOf([logoRef.value.children[0], logoRef.value.children[1]])
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div class="flex items-center justify-center cursor-pointer gap-2" @click="handleClick">
|
|
<div class="logo-icon flex h-[1.8rem] w-[1.8rem] items-center justify-center rounded-full bg-black overflow-hidden">
|
|
<video
|
|
src="/assets/logo.mp4"
|
|
class="h-[220%] w-[220%] object-cover pointer-events-none"
|
|
autoplay
|
|
muted
|
|
loop
|
|
playsinline
|
|
disablepictureinpicture
|
|
disableremoteplayback
|
|
controlslist="nodownload noplaybackrate noremoteplayback"
|
|
/>
|
|
</div>
|
|
<div
|
|
v-if="props.showText"
|
|
ref="logoRef"
|
|
class="relative flex items-center overflow-hidden min-w-[70px] md:min-w-[100px]"
|
|
:class="props.compact ? 'h-[1.8rem]' : 'h-10'"
|
|
>
|
|
<span class="absolute left-0 text-base font-bold leading-none tracking-wider text-black">{{ t('app.brand') }}</span>
|
|
<span class="absolute left-0 text-xs leading-none text-gray-500 opacity-0 invisible">{{ t('app.name') }}</span>
|
|
</div>
|
|
</div>
|
|
</template>
|