feat(HomeHero): 添加小字文本向上浮现动画效果

为服务列表、描述文本、按钮和小圆点等元素添加统一的向上浮现动画效果
使用GSAP实现动画序列,与大标题动画协调
This commit is contained in:
2026-04-27 17:59:11 +08:00
parent 5390b70fa9
commit c4075db8ef
+65 -50
View File
@@ -33,10 +33,16 @@ const play = () => new Promise<void>((resolve) => {
const studioChars = gsap.utils.toArray('.hero-studio-char', studioNameRef.value) const studioChars = gsap.utils.toArray('.hero-studio-char', studioNameRef.value)
const tm = brandNameRef.value?.querySelector('.hero-brand-tm') const tm = brandNameRef.value?.querySelector('.hero-brand-tm')
// 初始隐藏底部文字:通过 autoAlpha (opacity + visibility) 控制,不仅下移而且完全不可见 // 获取所有需要统一从下浮现的其他文本节点(服务列表、描述、版权等)
const fadeUpItems = gsap.utils.toArray('.hero-fade-up', heroPanelRef.value)
// 初始隐藏底部大字文字
gsap.set([...brandChars, ...studioChars], { yPercent: 110, autoAlpha: 0 }) gsap.set([...brandChars, ...studioChars], { yPercent: 110, autoAlpha: 0 })
if (tm) gsap.set(tm, { autoAlpha: 0 }) if (tm) gsap.set(tm, { autoAlpha: 0 })
// 初始隐藏需要向上浮现的小字文本节点
gsap.set(fadeUpItems, { y: 20, autoAlpha: 0 })
// 执行入场动画:将 Hero 整体容器从屏幕下方(yPercent: 100)滑入屏幕(yPercent: 0 // 执行入场动画:将 Hero 整体容器从屏幕下方(yPercent: 100)滑入屏幕(yPercent: 0
gsap.to(heroRef.value, { gsap.to(heroRef.value, {
yPercent: 0, yPercent: 0,
@@ -62,6 +68,13 @@ const play = () => new Promise<void>((resolve) => {
duration: 0.6, duration: 0.6,
ease: 'power2.out' ease: 'power2.out'
}, '-=0.4') }, '-=0.4')
.to(fadeUpItems, {
y: 0,
autoAlpha: 1,
duration: 0.8,
ease: 'power3.out',
stagger: 0.04 // 稍微错开一点点时间,或者设为 0 完全同时
}, '-=0.6') // 让它们在大字动画快结束时出现
} }
}, },
onComplete: resolve, onComplete: resolve,
@@ -198,63 +211,65 @@ defineExpose({ play })
<div class="flex flex-col md:flex-row justify-between w-full"> <div class="flex flex-col md:flex-row justify-between w-full">
<!-- Top Left: Services List --> <!-- Top Left: Services List -->
<div class="flex flex-col gap-2 font-bold text-base md:text-[1rem] tracking-tight leading-[1.5]"> <div class="flex flex-col gap-2 font-bold text-base md:text-[1rem] tracking-tight leading-[1.5]">
<p>{{ t('hero.services.webDesign') }}</p> <p class="hero-fade-up will-change-transform">{{ t('hero.services.webDesign') }}</p>
<p>{{ t('hero.services.socialMedia') }}</p> <p class="hero-fade-up will-change-transform">{{ t('hero.services.socialMedia') }}</p>
<p>{{ t('hero.services.marketing') }}</p> <p class="hero-fade-up will-change-transform">{{ t('hero.services.marketing') }}</p>
<p>{{ t('hero.services.development') }}</p> <p class="hero-fade-up will-change-transform">{{ t('hero.services.development') }}</p>
<p>{{ t('hero.services.seo') }}</p> <p class="hero-fade-up will-change-transform">{{ t('hero.services.seo') }}</p>
</div> </div>
<!-- Top Right: Description and Button --> <!-- Top Right: Description and Button -->
<div class="flex flex-col gap-8 max-w-[22rem] mt-10 md:mt-0 text-left"> <div class="flex flex-col gap-8 max-w-[22rem] mt-10 md:mt-0 text-left">
<p class="font-bold text-[1rem] leading-[1.5] tracking-tight"> <p class="hero-fade-up will-change-transform font-bold text-[1rem] leading-[1.5] tracking-tight">
{{ t('hero.description') }} {{ t('hero.description') }}
</p> </p>
<div> <div class="hero-fade-up will-change-transform">
<button <NuxtLink to="/contact">
ref="talkBtnRef" <button
@mouseenter="onTalkHover" ref="talkBtnRef"
@mouseleave="onTalkLeave" @mouseenter="onTalkHover"
class="relative overflow-hidden bg-black text-white px-7 py-3.5 rounded-[2rem] font-semibold text-[1rem] shadow-[0_15px_30px_-5px_rgba(0,0,0,0.3)] flex items-center justify-center group" @mouseleave="onTalkLeave"
> class="relative overflow-hidden bg-black text-white px-7 py-3.5 rounded-[2rem] font-semibold text-[1rem] shadow-[0_15px_30px_-5px_rgba(0,0,0,0.3)] flex items-center justify-center group"
<!-- 默认黑色背景层防止文字动画时漏底 --> >
<div class="absolute inset-0 bg-black rounded-[2rem]"></div> <!-- 默认黑色背景层防止文字动画时漏底 -->
<div class="absolute inset-0 bg-black rounded-[2rem]"></div>
<!-- Hover 时的灰色背景色块层 --> <!-- Hover 时的灰色背景色块层 -->
<div <div
class="btn-bg-hover absolute inset-0 bg-[#333333] rounded-[2rem] will-change-transform" class="btn-bg-hover absolute inset-0 bg-[#333333] rounded-[2rem] will-change-transform"
></div> ></div>
<!-- 文本层 --> <!-- 文本层 -->
<span class="relative inline-flex overflow-hidden z-10"> <span class="relative inline-flex overflow-hidden z-10">
<span class="inline-flex"> <span class="inline-flex">
<span <span
v-for="(char, index) in t('hero.letsTalk').split('')" v-for="(char, index) in t('hero.letsTalk').split('')"
:key="`orig-${index}`" :key="`orig-${index}`"
class="btn-char-orig block will-change-transform" class="btn-char-orig block will-change-transform"
v-html="char === ' ' ? '&nbsp;' : char" v-html="char === ' ' ? '&nbsp;' : char"
></span> ></span>
</span> </span>
<span class="absolute inset-0 inline-flex" aria-hidden="true"> <span class="absolute inset-0 inline-flex" aria-hidden="true">
<span <span
v-for="(char, index) in t('hero.letsTalk').split('')" v-for="(char, index) in t('hero.letsTalk').split('')"
:key="`clone-${index}`" :key="`clone-${index}`"
class="btn-char-clone block will-change-transform" class="btn-char-clone block will-change-transform"
v-html="char === ' ' ? '&nbsp;' : char" v-html="char === ' ' ? '&nbsp;' : char"
></span> ></span>
</span> </span>
</span> </span>
</button> </button>
</div> </NuxtLink>
</div>
</div> </div>
</div> </div>
<!-- Middle Section: 4 small dots --> <!-- 中间 Section: 4 small dots -->
<div class="flex justify-between w-full items-start flex-1 mt-16 md:mt-24 mb-6 md:mb-0"> <div class="flex justify-between w-full items-start flex-1 mt-16 md:mt-24 mb-6 md:mb-0">
<div class="w-2.5 h-2.5 bg-black rounded-full"></div> <div class="hero-fade-up will-change-transform w-2.5 h-2.5 bg-black rounded-full"></div>
<div class="w-2.5 h-2.5 bg-black rounded-full"></div> <div class="hero-fade-up will-change-transform w-2.5 h-2.5 bg-black rounded-full"></div>
<div class="w-2.5 h-2.5 bg-black rounded-full"></div> <div class="hero-fade-up will-change-transform w-2.5 h-2.5 bg-black rounded-full"></div>
<div class="w-2.5 h-2.5 bg-black rounded-full"></div> <div class="hero-fade-up will-change-transform w-2.5 h-2.5 bg-black rounded-full"></div>
</div> </div>
<!-- Bottom Section --> <!-- Bottom Section -->
@@ -275,7 +290,7 @@ defineExpose({ play })
<!-- Bottom Right: Studio --> <!-- Bottom Right: Studio -->
<div class="flex flex-col items-start md:items-end mt-4 md:mt-0"> <div class="flex flex-col items-start md:items-end mt-4 md:mt-0">
<p class="text-xs md:text-sm font-semibold text-gray-500 mb-2 md:mb-6 mr-1">© {{ new Date().getFullYear() }} {{ t('app.brand') }} {{ t('nav.studio') }}</p> <p class="hero-fade-up will-change-transform text-xs md:text-sm font-semibold text-gray-500 mb-2 md:mb-6 mr-1">© {{ new Date().getFullYear() }} {{ t('app.brand') }} {{ t('nav.studio') }}</p>
<h2 ref="studioNameRef" class="text-[11vw] md:text-[8vw] font-bold leading-[0.8] tracking-tighter m-0 -mr-1 whitespace-nowrap pt-4 pb-2"> <h2 ref="studioNameRef" class="text-[11vw] md:text-[8vw] font-bold leading-[0.8] tracking-tighter m-0 -mr-1 whitespace-nowrap pt-4 pb-2">
<span class="relative inline-flex overflow-hidden"> <span class="relative inline-flex overflow-hidden">
<span <span