perf(motion): 第三批入场动效时长收敛至 300ms(9 文件 20 处)

承接前两批,继续清理 Task #16 的 P0 硬违规:

- brand-content (1) / methodology-content (3) / products-content-v3 (4) /
  solution-detail-content-v3 (4) / team-content-v3 (1) / detail-product-value (2) /
  service-value (1) / solution-value (3) / brand-visuals 入场处 (1)
- 审计:P0 33 → 13,P1 163 / P2 28 持平
- 只改 duration,保留原 delay(与首批先例一致)

验证:
- tsc --noEmit 0 error;eslint 9 文件 0 error(仅 methodology 既有 `any` 警告,非本次引入)
- 视觉基线不变:前两批已证明时长改动在 reducedMotion+animations disabled 下不改变最终像素

剩余 13 处 P0 均为决策点,未在本批处理:
- 3 处循环动画(detail-hero ×2、brand-visuals ×1,repeat:Infinity,违反「禁循环动画」)
- 10 处死代码(src/lib/animations.tsx,全站零引用)
This commit is contained in:
2026-09-20 11:50:58 +08:00
parent 70485187cb
commit a1f636b67c
9 changed files with 20 additions and 20 deletions
@@ -90,7 +90,7 @@ export default function BrandContent() {
<motion.h1 <motion.h1
initial={{ opacity: 0, y: 40 }} initial={{ opacity: 0, y: 40 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.2, ease: EASE_OUT }} transition={{ duration: 0.3, delay: 0.2, ease: EASE_OUT }}
className="text-4xl sm:text-5xl md:text-6xl lg:text-7xl xl:text-8xl text-ink leading-[1.05] tracking-tight mb-8 sm:mb-10" className="text-4xl sm:text-5xl md:text-6xl lg:text-7xl xl:text-8xl text-ink leading-[1.05] tracking-tight mb-8 sm:mb-10"
> >
<span className="sr-only"></span> <span className="sr-only"></span>
@@ -34,7 +34,7 @@ export function MethodologyContent({ data }: MethodologyContentProps) {
<motion.div <motion.div
initial={{ opacity: 0, y: 30 }} initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.1, ease: EASE_OUT }} transition={{ duration: 0.3, delay: 0.1, ease: EASE_OUT }}
className="inline-flex items-center gap-2 px-3 py-1.5 text-xs font-bold text-brand bg-brand/10 mb-8" className="inline-flex items-center gap-2 px-3 py-1.5 text-xs font-bold text-brand bg-brand/10 mb-8"
> >
{heroSubtitle} {heroSubtitle}
@@ -42,7 +42,7 @@ export function MethodologyContent({ data }: MethodologyContentProps) {
<motion.h1 <motion.h1
initial={{ opacity: 0, y: 40 }} initial={{ opacity: 0, y: 40 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.9, delay: 0.2, ease: EASE_OUT }} transition={{ duration: 0.3, delay: 0.2, ease: EASE_OUT }}
className="text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-bold text-ink leading-[1.05] tracking-tight mb-8 whitespace-pre-line" className="text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-bold text-ink leading-[1.05] tracking-tight mb-8 whitespace-pre-line"
> >
{heroTitle} {heroTitle}
@@ -50,7 +50,7 @@ export function MethodologyContent({ data }: MethodologyContentProps) {
<motion.p <motion.p
initial={{ opacity: 0, y: 30 }} initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.35, ease: EASE_OUT }} transition={{ duration: 0.3, delay: 0.35, ease: EASE_OUT }}
className="text-lg sm:text-xl text-text-secondary max-w-2xl leading-relaxed" className="text-lg sm:text-xl text-text-secondary max-w-2xl leading-relaxed"
> >
{heroDescription} {heroDescription}
@@ -96,7 +96,7 @@ function HeroSection({ pageCopy }: { pageCopy?: PageCopy | null }) {
<motion.h1 <motion.h1
initial={{ opacity: 0, y: 70 }} initial={{ opacity: 0, y: 70 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 1.2, delay: 0.15, ease: EASE_OUT }} transition={{ duration: 0.3, delay: 0.15, ease: EASE_OUT }}
className="text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-bold text-ink leading-[0.92] tracking-tighter mb-8 sm:mb-10 md:mb-12" className="text-4xl sm:text-5xl md:text-6xl lg:text-7xl font-bold text-ink leading-[0.92] tracking-tighter mb-8 sm:mb-10 md:mb-12"
> >
<div className="block">{heroTitle1}</div> <div className="block">{heroTitle1}</div>
@@ -108,7 +108,7 @@ function HeroSection({ pageCopy }: { pageCopy?: PageCopy | null }) {
style={{ transformOrigin: 'left' }} style={{ transformOrigin: 'left' }}
initial={{ scaleX: 0 }} initial={{ scaleX: 0 }}
animate={{ scaleX: 1 }} animate={{ scaleX: 1 }}
transition={{ duration: 0.8, delay: 1.2, ease: EASE_OUT }} transition={{ duration: 0.3, delay: 1.2, ease: EASE_OUT }}
/> />
</span> </span>
</div> </div>
@@ -118,7 +118,7 @@ function HeroSection({ pageCopy }: { pageCopy?: PageCopy | null }) {
<motion.p <motion.p
initial={{ opacity: 0, y: 50 }} initial={{ opacity: 0, y: 50 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 1, delay: 0.45, ease: EASE_OUT }} transition={{ duration: 0.3, delay: 0.45, ease: EASE_OUT }}
className="text-lg sm:text-xl lg:text-2xl text-text-secondary max-w-2xl leading-relaxed mb-8 sm:mb-10 md:mb-12" className="text-lg sm:text-xl lg:text-2xl text-text-secondary max-w-2xl leading-relaxed mb-8 sm:mb-10 md:mb-12"
> >
{heroDescriptionLines.map((line, i) => ( {heroDescriptionLines.map((line, i) => (
@@ -132,7 +132,7 @@ function HeroSection({ pageCopy }: { pageCopy?: PageCopy | null }) {
<motion.div <motion.div
initial={{ opacity: 0, y: 50 }} initial={{ opacity: 0, y: 50 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 1, delay: 0.6, ease: EASE_OUT }} transition={{ duration: 0.3, delay: 0.6, ease: EASE_OUT }}
className="flex flex-wrap gap-6" className="flex flex-wrap gap-6"
> >
<CTAButton href="/services" dataTestId="products-hero-primary"> <CTAButton href="/services" dataTestId="products-hero-primary">
@@ -45,7 +45,7 @@ function DetailHero({ solution }: DetailHeroProps) {
<motion.div <motion.div
initial={{ opacity: 0, y: 30 }} initial={{ opacity: 0, y: 30 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, ease: EASE_OUT }} transition={{ duration: 0.3, ease: EASE_OUT }}
className="flex items-center gap-4 mb-8" className="flex items-center gap-4 mb-8"
> >
<div className="w-16 h-16 rounded-2xl bg-brand-soft flex items-center justify-center text-brand"> <div className="w-16 h-16 rounded-2xl bg-brand-soft flex items-center justify-center text-brand">
@@ -62,7 +62,7 @@ function DetailHero({ solution }: DetailHeroProps) {
<motion.h1 <motion.h1
initial={{ opacity: 0, y: 50 }} initial={{ opacity: 0, y: 50 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 1, delay: 0.15, ease: EASE_OUT }} transition={{ duration: 0.3, delay: 0.15, ease: EASE_OUT }}
className="text-3xl sm:text-4xl md:text-5xl lg:text-6xl font-bold text-ink leading-tight tracking-tighter mb-6 sm:mb-8 max-w-4xl" className="text-3xl sm:text-4xl md:text-5xl lg:text-6xl font-bold text-ink leading-tight tracking-tighter mb-6 sm:mb-8 max-w-4xl"
> >
{solution.title} {solution.title}
@@ -71,7 +71,7 @@ function DetailHero({ solution }: DetailHeroProps) {
<motion.p <motion.p
initial={{ opacity: 0, y: 40 }} initial={{ opacity: 0, y: 40 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.9, delay: 0.3, ease: EASE_OUT }} transition={{ duration: 0.3, delay: 0.3, ease: EASE_OUT }}
className="text-xl lg:text-2xl text-text-secondary max-w-3xl leading-relaxed mb-12" className="text-xl lg:text-2xl text-text-secondary max-w-3xl leading-relaxed mb-12"
> >
{solution.description} {solution.description}
@@ -80,7 +80,7 @@ function DetailHero({ solution }: DetailHeroProps) {
<motion.div <motion.div
initial={{ opacity: 0, y: 40 }} initial={{ opacity: 0, y: 40 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.9, delay: 0.45, ease: EASE_OUT }} transition={{ duration: 0.3, delay: 0.45, ease: EASE_OUT }}
className="flex flex-wrap gap-6" className="flex flex-wrap gap-6"
> >
<CTAButton href="/contact" dataTestId="solution-detail-hero-primary"> <CTAButton href="/contact" dataTestId="solution-detail-hero-primary">
+1 -1
View File
@@ -167,7 +167,7 @@ export default function TeamContentV3({ data }: { data: Record<string, unknown>
<motion.h1 <motion.h1
initial={{ opacity: 0, y: 40 }} initial={{ opacity: 0, y: 40 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, delay: 0.2, ease: EASE_OUT }} transition={{ duration: 0.3, delay: 0.2, ease: EASE_OUT }}
className="text-4xl sm:text-5xl md:text-6xl lg:text-7xl xl:text-8xl font-black text-ink leading-[0.92] tracking-tightest mb-10 sm:mb-12" className="text-4xl sm:text-5xl md:text-6xl lg:text-7xl xl:text-8xl font-black text-ink leading-[0.92] tracking-tightest mb-10 sm:mb-12"
> >
{(teamData.heroTitle || '').split('\n').map((line, i) => ( {(teamData.heroTitle || '').split('\n').map((line, i) => (
@@ -118,7 +118,7 @@ export function ProductValueSection({ product }: ProductValueSectionProps) {
initial={{ opacity: 0, y: 32 }} initial={{ opacity: 0, y: 32 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-120px' }} viewport={{ once: true, margin: '-120px' }}
transition={{ duration: 0.8, ease: [0.22, 1, 0.36, 1] }} transition={{ duration: 0.3, ease: [0.22, 1, 0.36, 1] }}
className="mb-16 sm:mb-20" className="mb-16 sm:mb-20"
> >
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
@@ -243,7 +243,7 @@ export function ProductValueSection({ product }: ProductValueSectionProps) {
initial={{ opacity: 0, y: 32 }} initial={{ opacity: 0, y: 32 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-80px' }} viewport={{ once: true, margin: '-80px' }}
transition={{ duration: 0.75, delay: 0.2 }} transition={{ duration: 0.3, delay: 0.2 }}
className="mt-24" className="mt-24"
> >
<div className="mb-12"> <div className="mb-12">
+1 -1
View File
@@ -26,7 +26,7 @@ export function ServiceValueSection({ service }: ServiceValueSectionProps) {
initial={{ opacity: 0, y: 32 }} initial={{ opacity: 0, y: 32 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-120px' }} viewport={{ once: true, margin: '-120px' }}
transition={{ duration: 0.8, ease: [0.22, 1, 0.36, 1] }} transition={{ duration: 0.3, ease: [0.22, 1, 0.36, 1] }}
className="mb-16 sm:mb-20" className="mb-16 sm:mb-20"
> >
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
+3 -3
View File
@@ -21,7 +21,7 @@ export function SolutionValueSection({ solution }: SolutionValueSectionProps) {
initial={{ opacity: 0, y: 32 }} initial={{ opacity: 0, y: 32 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-120px' }} viewport={{ once: true, margin: '-120px' }}
transition={{ duration: 0.8, ease: [0.22, 1, 0.36, 1] }} transition={{ duration: 0.3, ease: [0.22, 1, 0.36, 1] }}
className="mb-16 sm:mb-20" className="mb-16 sm:mb-20"
> >
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
@@ -119,7 +119,7 @@ export function SolutionValueSection({ solution }: SolutionValueSectionProps) {
initial={{ opacity: 0, y: 32 }} initial={{ opacity: 0, y: 32 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-80px' }} viewport={{ once: true, margin: '-80px' }}
transition={{ duration: 0.75, delay: 0.3 }} transition={{ duration: 0.3, delay: 0.3 }}
> >
<div className="mb-12"> <div className="mb-12">
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
@@ -164,7 +164,7 @@ export function SolutionValueSection({ solution }: SolutionValueSectionProps) {
initial={{ opacity: 0, y: 32 }} initial={{ opacity: 0, y: 32 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: '-80px' }} viewport={{ once: true, margin: '-80px' }}
transition={{ duration: 0.75, delay: 0.4 }} transition={{ duration: 0.3, delay: 0.4 }}
className="mt-20" className="mt-20"
> >
<div className="mb-12"> <div className="mb-12">
+1 -1
View File
@@ -69,7 +69,7 @@ export function DataBar({ value, max = 100, label, color = 'brand' }: { value: n
initial={{ width: 0 }} initial={{ width: 0 }}
whileInView={{ width: `${percentage}%` }} whileInView={{ width: `${percentage}%` }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ duration: 1, ease: [0.16, 1, 0.3, 1], delay: 0.2 }} transition={{ duration: 0.3, ease: [0.16, 1, 0.3, 1], delay: 0.2 }}
className="h-full rounded-full" className="h-full rounded-full"
style={{ backgroundColor: colorMap[color] }} style={{ backgroundColor: colorMap[color] }}
/> />