关于 css-animations
面向 FrameVideo 的 CSS 动画技能,可在视频合成中定位关键帧状态。适用于装饰效果和简单的有限动画;需要精确编排多个元素时则使用 GSAP。
主要功能
- 01定位 CSS 关键帧动画
- 02制作有限循环装饰效果
- 03添加独立元素入场动画
- 04与 GSAP 配合使用
- 05检查时序与渲染
适用场景
- 01为合成添加微光或辉光
- 02制作徽标脉冲动画
- 03创建元素独立入场效果
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ chanjing-ai/ framevideo --skill css-animations安装后在 Claude Code 中输入/css-animations确认已加载。
详细介绍
FrameVideo 的 CSS 动画
FrameVideo 可以通过其 css 运行时适配器为 CSS 关键帧动画定位。可将其用于简单的重复图案、背景动效、闪烁、发光、遮罩和非序列化装饰。
对于场景编排,GSAP 通常更清晰。CSS 动画最适合应用于动效只涉及单个元素且时长固定的情况。
何时使用
在以下情况下使用 CSS 动画:
- 装饰层 — 背景图案、闪烁效果、颗粒、无需精确计时的细微视差
- 简单循环 — 重复次数已知的脉冲、发光、呼吸效果
- 单元素动效 — 每个元素独立播放的入场动画
- 零依赖动效 — 希望实现动画但无需加载 GSAP 或其他库时
- GPU 加速效果 — 浏览器会自动优化的变换和不透明度动画
不要使用
以下情况应避免使用 CSS 动画:
- 场景编排 — 需要精确计时的多元素序列 → 使用
gsap - 复杂时间线 — 任何需要暂停/恢复控制或动态时长的内容 → 使用
gsap - 无限循环 — 除非已确认浏览器会公开可定位的 WAAPI 句柄(在无头渲染中很少见)
- 交互式动画 — 悬停、滚动或用户触发的动效无法确定性地渲染
- 布局动画 — 为
top、left、width、height设置动画会导致重排 → 改用变换
约定
- 在运行时初始化完成前,将动画元素放入 DOM。
- 为定时元素设置
data-start值,使局部动画时间与片段时间一致。 - 使用有限的
animation-duration和animation-iteration-count,因为在没有 WAAPI 支持的 CSS 动画的环境中,负延迟回退方案无法表示无界时长。 - 优先使用
animation-fill-mode: both,使定位后的状态在活动动效开始前和结束后保持不变。 - 避免使用墙上时钟 JavaScript、悬停触发的状态,以及依赖用户事件的类切换。
适配器会查找计算样式中具有 animation-name 的元素,在可用时定位其浏览器 Animation 句柄,否则会通过负 animation-delay 暂停动画。
基本模式
<div
id="pulse-ring"
class="clip pulse-ring"
data-start="0"
data-duration="4"
data-track-index="2"
></div>
<style>
.pulse-ring {
width: 280px;
height: 280px;
border: 4px solid rgba(255, 255, 255, 0.7);
border-radius: 50%;
animation-name: pulse-ring;
animation-duration: 1200ms;
animation-timing-function: cubic-bezier(0.2, 0, 0, 1);
animation-iteration-count: 3;
animation-fill-mode: both;
}
@keyframes pulse-ring {
from {
opacity: 0;
transform: scale(0.82);
}
35% {
opacity: 1;
}
to {
opacity: 0;
transform: scale(1.18);
}
}
</style>
交错模式
使用 CSS 自定义属性,避免重复定义关键帧:
<div class="clip dots" data-start="1" data-duration="3" data-track-index="3">
<span style="--i: 0"></span>
<span style="--i: 1"></span>
<span style="--i: 2"></span>
</div>
<style>
.dots span {
display: inline-block;
width: 18px;
height: 18px;
margin-right: 10px;
border-radius: 50%;
background: currentColor;
animation: dot-pop 900ms ease-out both;
animation-delay: calc(var(--i) * 120ms);
}
@keyframes dot-pop {
from {
opacity: 0;
transform: translateY(18px) scale(0.75);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
</style>
适用场景
- 重复次数已知的装饰性循环。
- 遮罩、发光、闪烁、颗粒和细微视差层。
- 简单的单元素入场动画,无需使用完整的 JS 时间线。
与 GSAP 结合使用
CSS 动画和 GSAP 时间线可以在同一合成中共存。两者的分工如下:
CSS 负责装饰效果:
- 背景图案、颗粒、噪点
- 环境光晕、闪烁、脉冲效果
- 遮罩显现、暗角
- 细微的环境动效(浮动粒子、缓慢漂移)
GSAP 负责场景编排:
- 字幕计时和排序
- 场景过渡和交叉淡化
- 与音频协调的 UI 元素入场
- 可精确控制的多元素交错动画
同步模式
CSS 动画计时与片段的 data-start 对齐:
<div class="clip bg-shimmer" data-start="0" data-duration="10">
</div>
<script>
const tl = gsap.timeline();
tl.to("#caption-1", { opacity: 1, duration: 0.5 }, 2);
tl.to("#caption-2", { opacity: 1, duration: 0.5 }, 5);
// GSAP controls captions, CSS handles background
</script>
CSS 适配器和 GSAP 适配器独立运行,但两者都会遵循 FrameVideo 的全局时间线,因此在预览和渲染时能保持同步。
高级模式
使用自定义属性实现复杂交错
对于基于数据属性或计算值实现的交错动画:
<div class="grid">
<div class="cell" style="--row: 0; --col: 0"></div>
<div class="cell" style="--row: 0; --col: 1"></div>
</div>
<style>
.cell {
animation: cell-reveal 600ms ease-out both;
animation-delay: calc((var(--row) * 3 + var(--col)) * 80ms);
}
@keyframes cell-reveal {
from { opacity: 0; transform: scale(0.8); }
to { opacity: 1; transform: scale(1); }
}
</style>
用有限循环实现无限图案
当图案看起来是无限循环,但渲染时必须设置有限循环次数时:
.background-drift {
/* Appears infinite but is actually 50 iterations over 100s */
animation: drift 2000ms linear 50;
}
@keyframes drift {
from { transform: translateX(0); }
to { transform: translateX(-100px); }
}
规则: 迭代次数 = 片段时长 / 动画时长,向上取整。对于时长为 30s、动画时长为 2s 的片段:animation-iteration-count: 15。
动画链
在一个元素上依次运行多个动画:
.logo {
animation:
fade-in 800ms ease-out 0s both,
pulse 1200ms ease-in-out 1s 3 both,
fade-out 800ms ease-in 4.6s both;
}
@keyframes fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.05); } }
@keyframes fade-out { from { opacity: 1; } to { opacity: 0; } }
延迟会依次累加:淡入在 0s 开始,脉冲在 1s 开始,淡出在 4.6s 开始。
性能优化
.optimized {
/* Tell browser to promote to GPU layer */
will-change: transform, opacity;
/* Prefer transform over position */
animation: slide-in 600ms ease-out both;
}
@keyframes slide-in {
/* Good: GPU-accelerated */
from { transform: translateX(-100px); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
/* Avoid: causes reflow */
/* from { left: -100px; } to { left: 0; } */
}
尽可能只为 transform 和 opacity 设置动画。避免使用 width、height、top、left、margin、padding。
性能对比
| 指标 | CSS 动画 | GSAP | WAAPI | Anime.js |
|---|---|---|---|---|
| 包体积 | 0 KB | 45 KB | 0 KB | 17 KB |
| 初始化时间 | 0 ms | 8 ms | 0 ms | 4 ms |
| 跳转性能 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| GPU 加速 | 自动 | 手动 | 自动 | 手动 |
| 最适用于 | 装饰效果 | 序列编排 | 轻量场景 | 精简场景 |
CSS 更快的情况:
- 简单循环(脉冲、发光、旋转)
- 仅使用 GPU 加速属性(transform、opacity)
- 无需时间轴控制
GSAP 更快的情况:
- 包含 10+ 个补间动画的复杂序列
- 精确控制时间和标签
- 动态设置时长或播放速度
- 嵌套时间轴
避免
- 避免使用无限 CSS 动画,除非已确认浏览器会公开可跳转的、由 WAAPI 支持的 CSS 动画句柄。优先使用足以覆盖可见时长的有限迭代次数。
- 当 transform 可实现效果时,避免对
top、left、width或height等布局属性设置动画。 - 避免依赖悬停、焦点、滚动或媒体查询来触发渲染关键动画。
- 启动后避免更改动画类,除非有其他确定性的时间轴控制此更改。
验证
编辑 CSS 动画组合后:
npx framevideo lint # Check timing contract
npx framevideo validate # Check runtime errors
手动检查:
- 跳转测试 — 拖动预览时间轴,确认动画在每一帧都能保持
- 迭代次数 — 确认
animation-iteration-count覆盖片段时长(结尾没有空白帧) - 填充模式 — 确认设置了
animation-fill-mode: both,使跳转后的状态在前后保持 - GPU 图层 — 打开 DevTools Performance,录制渲染过程,确认没有布局抖动
实际示例
示例 1:背景微光
使用场景: 无需加载动画库,为产品卡片背景添加细微的微光效果。
<div class="clip product-card" data-start="0" data-duration="5">
<div class="shimmer-layer"></div>
<h2>Product Name</h2>
</div>
<style>
.shimmer-layer {
position: absolute;
inset: 0;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
animation: shimmer 2s linear infinite;
}
@keyframes shimmer {
from { transform: translateX(-100%); }
to { transform: translateX(100%); }
}
</style>
结果: 流畅的微光效果,额外开销为 0 KB,并由 GPU 加速。
示例 2:加载圆点
使用场景: 在组合构建阶段显示动画加载指示器。
<div class="dots">
<span style="--i: 0"></span>
<span style="--i: 1"></span>
<span style="--i: 2"></span>
</div>
<style>
.dots span {
animation: dot-bounce 1.4s ease-in-out infinite both;
animation-delay: calc(var(--i) * 160ms);
}
@keyframes dot-bounce {
0%, 80%, 100% { transform: scale(0); }
40% { transform: scale(1); }
}
</style>
结果: 仅使用 CSS 实现经典的三圆点错峰动画。
示例 3:Logo 脉冲环
使用场景: 通过脉冲环效果吸引用户注意 Logo。
<div class="clip logo-intro" data-start="0" data-duration="4">
<div class="logo-container">
<img src="logo.svg" alt="Logo">
<div class="pulse-ring"></div>
</div>
</div>
<style>
.pulse-ring {
position: absolute;
inset: -20px;
border: 3px solid rgba(255,255,255,0.6);
border-radius: 50%;
animation: pulse 1.5s cubic-bezier(0.2, 0, 0, 1) 2 both;
}
@keyframes pulse {
from { opacity: 0; transform: scale(0.8); }
35% { opacity: 1; }
to { opacity: 0; transform: scale(1.2); }
}
</style>
结果: Logo 周围出现两个扩展环,动画总时长为 3s。
致谢与参考资料
- FrameVideo 适配器源码:
packages/core/src/runtime/adapters/css.ts。 - MDN CSS 动画文档:https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/animation
- MDN
animation-fill-mode:https://developer.mozilla.org/en-US/docs/Web/CSS/animation-fill-mode