Communitygithub.com

视频与原型动画提取(video-to-gsap)

当用户提供视频、GIF或原型动画(如Figma原型)时,将运动编排解构为时间、缓动曲线、空间轨迹和交错效果,并使用 @gsap/react 和 useGSAP 在 Next.js 16 中实现硬件加速、无泄漏的 GSAP 动画。

视频与原型动画提取(video-to-gsap) 是什么?

该技能专注于从视频录制、GIF 或 Figma 原型中提取运动编排,并将其转化为高性能的 GSAP 动画代码。它通过系统性地解构运动序列(包括触发动作、预备动作、主要动作及跟随效果),详细分析时间、缓动函数和交错偏移,并严格遵循仅动画合成属性(transform 和 opacity)的规则,以确保 60/120 FPS 的流畅帧率。该技能强制使用 @gsap/react 的 useGSAP 钩子进行作用域清理,防止内存泄漏,并支持 ScrollTrigger 驱动的滚动动画。它还包含了针对用户偏好减少运动的无障碍合规检查。目标平台是 Next.js 16,推荐使用 TypeScript 和函数式 React 组件,并提供了详细的代码蓝图,包括时间线创建、默认缓动、交错和 FOUC(未样式内容闪烁)消除(通过 autoAlpha)。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/billel-chami/nextjs-medusa-handbook/tree/HEAD/.agents/skills/video-to-gsap

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

视频与原型运动提取 (video-to-gsap)

1. 目的与触发条件

流畅的微交互和电影级运动使高端电商体验与众不同。然而,实现不当的Web动画会导致线程阻塞、丢帧(卡顿)、内存泄漏以及糟糕的交互到下一次绘制(INP)指标。此技能负责提取并实现丝滑的 60/120 FPS 动画。

触发条件:

  • 当用户提供视频片段、GIF 或 Figma 运动原型时。
  • 当需要动画化购物车滑出、页面过渡、手风琴展开、产品卡片悬停或结账完成庆祝时。
  • 当使用 ScrollTrigger 创建滚动驱动的动画时。

2. 运动解构协议

分析运动片段时,系统性地识别:

  1. 编排序列:
    • 舞台/触发: 什么动作开始运动(悬停、滚动、路由变化、点击)?
    • 预备动作: 主要运动之前是否有微妙的逆向运动?
    • 动作与行进: 哪些元素移动,朝什么方向,移动多远(px、rem 或 %)?
    • 静止与跟随: 会过冲(弹簧/弹性)还是平滑减速(power3.out、expo.out)?
  2. 时间分解:
    • 每个元素的持续时间(例如,抽屉进入:450ms,背景淡出:300ms)。
    • 交错子元素之间的交错偏移(例如,产品网格卡片:0.06s 交错)。
  3. 缓动特征:
    • 线性:严格用于连续循环(例如滚动字幕)。
    • 缓出(power2.out、expo.out):用于 UI 进入(快速进入,柔和停止)。
    • 缓入(power2.in):用于 UI 退出(加速离开屏幕)。
    • 缓入缓出(power3.inOut):用于保持在视图内的状态转换。

3. 性能与硬件加速标准

规则 1:仅动画合成属性

  • 允许: transform(x、y、xPercent、yPercent、scale、rotation、skew)和 opacity(或 GSAP autoAlpha)。
  • 严格禁止: 动画化布局或绘制属性(width、height、top、left、right、bottom、margin、padding、border-width)。这些会强制浏览器 CPU 每帧重新计算布局几何并重绘像素,导致 INP 降级和丢帧。

规则 2:通过作用域 useGSAP 实现零内存泄漏

  • 始终使用官方的 @gsap/react 包和 useGSAP 钩子。
  • 提供一个 scope 容器引用。内部的每个选择器将自动被作用域化,所有时间线、补间和 ScrollTrigger 实例在组件卸载时将被干净地恢复和销毁。
  • 这防止了单页应用和 React 19 快速刷新循环中的僵尸事件监听器和内存保留。

规则 3:消除 FOUC(未样式内容闪烁)

  • 使用 autoAlpha 代替 opacity。当不透明度为 0 时,autoAlpha 设置 visibility: hidden,防止幽灵点击和渲染闪烁在注水之前发生。
  • 或者,声明 CSS 默认隐藏状态,并使用 gsap.fromTo 揭示它们。

4. 生产实现蓝图

"use client";

import { useRef } from "react";
import gsap from "gsap";
import { useGSAP } from "@gsap/react";
import { ScrollTrigger } from "gsap/ScrollTrigger";

// 确保插件安全注册
if (typeof window !== "undefined") {
  gsap.registerPlugin(useGSAP, ScrollTrigger);
}

interface ProductShowcaseProps {
  readonly items: Array<{ id: string; title: string; price: string }>;
}

export function ProductShowcase({ items }: ProductShowcaseProps) {
  const containerRef = useRef<HTMLDivElement | null>(null);

  useGSAP(
    () => {
      // 硬件加速的进入时间线
      const tl = gsap.timeline({
        defaults: {
          ease: "expo.out",
          duration: 0.8,
        },
      });

      tl.from(".showcase-header", {
        autoAlpha: 0,
        y: 30,
        clearProps: "all", // 动画后清理内联样式
      }).from(
        ".product-card",
        {
          autoAlpha: 0,
          y: 40,
          scale: 0.96,
          stagger: 0.08,
          duration: 0.7,
          clearProps: "transform,opacity",
        },
        "-=0.4" // 无缝重叠
      );
    },
    { scope: containerRef } // 作用域清理边界
  );

  return (
    <section ref={containerRef} className="w-full py-16 px-6">
      <div className="showcase-header max-w-4xl mx-auto text-center mb-12">
        <h2 className="text-h1 font-bold text-content-primary">Featured Collection</h2>
      </div>

      <div className="grid grid-cols-1 md:grid-cols-3 gap-8 max-w-7xl mx-auto">
        {items.map((item) => (
          <article
            key={item.id}
            className="product-card will-change-transform bg-surface-elevated rounded-lg p-6 border border-border-subtle"
          >
            <h3 className="text-h3 font-semibold text-content-primary">{item.title}</h3>
            <p className="text-content-secondary mt-2">{item.price}</p>
          </article>
        ))}
      </div>
    </section>
  );
}

5. 减少运动合规性

始终尊重偏好减少运动的用户:

const { contextSafe } = useGSAP({ scope: containerRef });

// 检查无障碍偏好
const prefersReducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;

if (prefersReducedMotion) {
  // 立即渲染最终静止状态,不带动画
  gsap.set([".showcase-header", ".product-card"], { autoAlpha: 1, y: 0, scale: 1 });
  return;
}

相关技能