`StickyStack` 中的关键双针布局冲突以及 `HorizontalPan` 中的减少运动内容锁定

作者: codeCraft-Ritik创建于 2026年9月12日更新于 2026年9月12日

1. CSS position: sticky vs. GSAP pin: true Collision (StickyStack)

StickyStack 中,JSX 元素具有 className="stack-card sticky top-0 ...",而 useEffect 配置如下:

ScrollTrigger.create({
  trigger: card,
  start: "top top",
  endTrigger: cardEls[cardEls.length - 1],
  end: "top top",
  pin: true,
  pinSpacing: false,
});
  • 问题: ScrollTrigger 通过应用 position: fixed 实现锁定。将 CSS position: stickysticky top-0)与 ScrollTrigger 的 pin: true 相混合会导致浏览器的原生合成器和 GSAP 的滚动引擎在每次滚动时竞争元素的定位。
  • 官方 GSAP 指导: GreenSock 明确指出: "不要将 CSS position: sticky 套用到正在由 ScrollTrigger 锁定的元素上。"
  • 症状: 严重的滚动抖动、视觉抖动和逆向滚动时的坐标计算错误。

2. 减少运动永久性内容不可访问性 (HorizontalPan)

HorizontalPan 中:

export function HorizontalPan({ children }: { children: React.ReactNode }) {
  const wrap = useRef<HTMLDivElement>(null);
  const track = useRef<HTMLDivElement>(null);
  const reduce = useReducedMotion();

  useEffect(() => {
    if (reduce || !wrap.current || !track.current) return;
    // GSAP ScrollTrigger 设置...
  }, [reduce]);

  return (
    <section ref={wrap} className="relative overflow-hidden">
      <div ref={track} className="flex h-[100dvh] items-center">
        {children}
      </div>
    </section>
  );
}
  • 问题: 当用户在操作系统级别启用 prefers-reduced-motion: reduce 时,useReducedMotion() 会返回 true 并跳过 GSAP 初始化。然而,<section> 会无条件保持 overflow-hidden
  • 症状(WCAG 2.1 失败): 没有滚动条或 CSS 替代方案。所有超出初始屏幕宽度的卡片/面板永远被切断,无法访问。

3. 过时的闭包和破坏的调整 (HorizontalPan)

  • const distance = track.current!.scrollWidth - window.innerWidth; 仅在初始安装时被求值一次。
  • x: -distanceend: () => +=${distance} 绑定该静态整数。
  • 在窗口大小调整或字体/资源加载时,invalidateOnRefresh: true

内容来源: Leonxlnx/taste-skill