feat: 添加图标库依赖并优化首页按钮动画
添加@iconify-json/ph图标库依赖 在nuxt.config.ts中配置vite优化依赖 更新社交媒体图标为ph:x-logo 实现首页"Talk"按钮的悬停动画效果
This commit is contained in:
@@ -22,6 +22,57 @@ const studioNameRef = ref<HTMLElement | null>(null)
|
||||
const talkBtnRef = ref<HTMLElement | null>(null)
|
||||
|
||||
let cleanupHeroScroll: (() => void) | null = null
|
||||
let talkBtnTl: gsap.core.Timeline | null = null
|
||||
|
||||
const initTalkButton = () => {
|
||||
if (!talkBtnRef.value) return
|
||||
|
||||
const hoverBg = talkBtnRef.value.querySelector<HTMLElement>('.btn-bg-hover')
|
||||
const origChars = gsap.utils.toArray<HTMLElement>('.btn-char-orig', talkBtnRef.value)
|
||||
const cloneChars = gsap.utils.toArray<HTMLElement>('.btn-char-clone', talkBtnRef.value)
|
||||
|
||||
if (!hoverBg || !origChars.length || !cloneChars.length) return
|
||||
|
||||
gsap.set(hoverBg, { yPercent: 110 })
|
||||
gsap.set(origChars, { yPercent: 0 })
|
||||
gsap.set(cloneChars, { yPercent: 110 })
|
||||
}
|
||||
|
||||
const onTalkHover = () => {
|
||||
if (!talkBtnRef.value) return
|
||||
|
||||
const hoverBg = talkBtnRef.value.querySelector<HTMLElement>('.btn-bg-hover')
|
||||
const origChars = gsap.utils.toArray<HTMLElement>('.btn-char-orig', talkBtnRef.value)
|
||||
const cloneChars = gsap.utils.toArray<HTMLElement>('.btn-char-clone', talkBtnRef.value)
|
||||
|
||||
if (!hoverBg || !origChars.length || !cloneChars.length) return
|
||||
|
||||
talkBtnTl?.kill()
|
||||
talkBtnTl = gsap.timeline({ defaults: { ease: 'power3.out' } })
|
||||
|
||||
talkBtnTl
|
||||
.to(hoverBg, { yPercent: 0, duration: 0.42 }, 0)
|
||||
.to(origChars, { yPercent: -115, duration: 0.42, stagger: 0.018 }, 0)
|
||||
.to(cloneChars, { yPercent: 0, duration: 0.42, stagger: 0.018 }, 0)
|
||||
}
|
||||
|
||||
const onTalkLeave = () => {
|
||||
if (!talkBtnRef.value) return
|
||||
|
||||
const hoverBg = talkBtnRef.value.querySelector<HTMLElement>('.btn-bg-hover')
|
||||
const origChars = gsap.utils.toArray<HTMLElement>('.btn-char-orig', talkBtnRef.value)
|
||||
const cloneChars = gsap.utils.toArray<HTMLElement>('.btn-char-clone', talkBtnRef.value)
|
||||
|
||||
if (!hoverBg || !origChars.length || !cloneChars.length) return
|
||||
|
||||
talkBtnTl?.kill()
|
||||
talkBtnTl = gsap.timeline({ defaults: { ease: 'power3.out' } })
|
||||
|
||||
talkBtnTl
|
||||
.to(hoverBg, { yPercent: 110, duration: 0.42 }, 0)
|
||||
.to(origChars, { yPercent: 0, duration: 0.42, stagger: 0.018 }, 0)
|
||||
.to(cloneChars, { yPercent: 110, duration: 0.42, stagger: 0.018 }, 0)
|
||||
}
|
||||
|
||||
// 暴露供外部(如 index.vue)使用的异步播放方法
|
||||
const play = () => new Promise<void>((resolve) => {
|
||||
@@ -89,6 +140,7 @@ onMounted(() => {
|
||||
if (heroRef.value) gsap.set(heroRef.value, { yPercent: 100 })
|
||||
// 初始化滚动监听逻辑
|
||||
setupHeroScroll()
|
||||
initTalkButton()
|
||||
})
|
||||
const setupHeroScroll = () => {
|
||||
if (!heroStageRef.value || !heroPanelRef.value) return
|
||||
@@ -138,6 +190,7 @@ const setupHeroScroll = () => {
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
cleanupHeroScroll?.()
|
||||
talkBtnTl?.kill()
|
||||
})
|
||||
|
||||
defineExpose({ play })
|
||||
|
||||
@@ -167,7 +167,7 @@ onUnmounted(() => {
|
||||
<Icon name="ph:instagram-logo" class="w-4 h-4" />
|
||||
</a>
|
||||
<a href="#" class="w-10 h-10 rounded-full bg-[#1f1f1f] hover:bg-[#2f2f2f] flex items-center justify-center transition-colors text-white">
|
||||
<Icon name="pajamas:twitter-x" class="w-3.5 h-3.5" />
|
||||
<Icon name="ph:x-logo" class="w-4 h-4" />
|
||||
</a>
|
||||
<a href="#" class="w-10 h-10 rounded-full bg-[#1f1f1f] hover:bg-[#2f2f2f] flex items-center justify-center transition-colors text-white">
|
||||
<Icon name="ph:diamond-fill" class="w-4 h-4" />
|
||||
|
||||
@@ -6,6 +6,15 @@ export default defineNuxtConfig({
|
||||
viewport: 'width=device-width, initial-scale=1, maximum-scale=1',
|
||||
},
|
||||
},
|
||||
vite: {
|
||||
optimizeDeps: {
|
||||
include: [
|
||||
'lenis',
|
||||
'gsap',
|
||||
'gsap/ScrollTrigger',
|
||||
],
|
||||
},
|
||||
},
|
||||
compatibilityDate: '2025-07-15',
|
||||
devtools: { enabled: true },
|
||||
css: ['~/assets/css/tailwind.css'],
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
"vue-router": "^5.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@iconify-json/ph": "1.2.2",
|
||||
"@nuxtjs/tailwindcss": "6.14.0"
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+10
@@ -39,6 +39,9 @@ importers:
|
||||
specifier: ^5.0.4
|
||||
version: 5.0.6(@vue/compiler-sfc@3.5.33)(pinia@3.0.4(typescript@6.0.3)(vue@3.5.33(typescript@6.0.3)))(vue@3.5.33(typescript@6.0.3))
|
||||
devDependencies:
|
||||
'@iconify-json/ph':
|
||||
specifier: 1.2.2
|
||||
version: 1.2.2
|
||||
'@nuxtjs/tailwindcss':
|
||||
specifier: 6.14.0
|
||||
version: 6.14.0(magicast@0.5.2)(yaml@2.8.3)
|
||||
@@ -595,6 +598,9 @@ packages:
|
||||
resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
|
||||
engines: {node: '>=18.18'}
|
||||
|
||||
'@iconify-json/ph@1.2.2':
|
||||
resolution: {integrity: sha512-PgkEZNtqa8hBGjHXQa4pMwZa93hmfu8FUSjs/nv4oUU6yLsgv+gh9nu28Kqi8Fz9CCVu4hj1MZs9/60J57IzFw==}
|
||||
|
||||
'@iconify/collections@1.0.676':
|
||||
resolution: {integrity: sha512-dtV8s0QtP0fjsIZoCA4gURBp2DWGs7vJDz+Kh5+vpeNinBbtbNz+MABDz61ikcFGE27mdvNFmA3ygprBXHZJ4A==}
|
||||
|
||||
@@ -5348,6 +5354,10 @@ snapshots:
|
||||
|
||||
'@humanwhocodes/retry@0.4.3': {}
|
||||
|
||||
'@iconify-json/ph@1.2.2':
|
||||
dependencies:
|
||||
'@iconify/types': 2.0.0
|
||||
|
||||
'@iconify/collections@1.0.676':
|
||||
dependencies:
|
||||
'@iconify/types': 2.0.0
|
||||
|
||||
Reference in New Issue
Block a user