init repo

This commit is contained in:
2026-04-25 23:10:25 +08:00
commit 1e76eb536c
14 changed files with 3640 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
.DS_Store
node_modules/
repos/
*.log

3
.memory/worklog.json Normal file
View File

@@ -0,0 +1,3 @@
{
"entries": []
}

30
.project.json Normal file
View File

@@ -0,0 +1,30 @@
{
"name": "Vibe Motion",
"description": "Remotion 动效视频项目 — 用 AI 写代码生成专业动画视频",
"status": "active",
"kind": "app",
"created": "2026-03-19",
"ports": [
{
"port": 4062,
"label": "dev",
"fixed": true
}
],
"stack": [
"Remotion",
"React",
"TypeScript"
],
"urls": [
{
"url": "http://localhost:4062",
"type": "app",
"label": "local"
}
],
"worklog": {
"path": ".memory/worklog.json",
"auto": true
}
}

21
AGENTS.md Normal file
View File

@@ -0,0 +1,21 @@
# Vibe Motion Agent Rules
## Must Read First
- `.project.json` 是机器真源:公网链接、快捷登录、凭证引用都以它为准
- `RULES.md` 是人工规则和部署事实:启动命令、平台、域名、注意事项都写这里
- 不允许编造不存在的域名、账号、密码;未知就保持空白并明确标记待补充
## Deployment Metadata Contract
- 任何任务只要新增、删除或修改公网地址,必须在同一次任务里更新 `.project.json`
- `urls[]` 推荐显式写 `type``app``backend``docs``admin``repo`
- 项目专属的网页登录信息,如果允许放进仓库,就写 `.project.json.quick_login`
- 不能直接入库的敏感登录,不要伪造 `quick_login`,改为写 `.project.json.credentials` 引用
- 数据库密码、API Key、服务器 root 密码,不属于 `quick_login`
## Completion Gate
- 部署完成后,不允许在 `.project.json` 缺少最新公网链接的状态下结束任务
- 部署完成后,必须同步更新 `RULES.md` 的部署事实
- 如果只更新了代码但没回写部署元数据,这个任务不算完成

21
CLAUDE.md Normal file
View File

@@ -0,0 +1,21 @@
# Vibe Motion Agent Rules
## Must Read First
- `.project.json` 是机器真源:公网链接、快捷登录、凭证引用都以它为准
- `RULES.md` 是人工规则和部署事实:启动命令、平台、域名、注意事项都写这里
- 不允许编造不存在的域名、账号、密码;未知就保持空白并明确标记待补充
## Deployment Metadata Contract
- 任何任务只要新增、删除或修改公网地址,必须在同一次任务里更新 `.project.json`
- `urls[]` 推荐显式写 `type``app``backend``docs``admin``repo`
- 项目专属的网页登录信息,如果允许放进仓库,就写 `.project.json.quick_login`
- 不能直接入库的敏感登录,不要伪造 `quick_login`,改为写 `.project.json.credentials` 引用
- 数据库密码、API Key、服务器 root 密码,不属于 `quick_login`
## Completion Gate
- 部署完成后,不允许在 `.project.json` 缺少最新公网链接的状态下结束任务
- 部署完成后,必须同步更新 `RULES.md` 的部署事实
- 如果只更新了代码但没回写部署元数据,这个任务不算完成

37
RULES.md Normal file
View File

@@ -0,0 +1,37 @@
# Vibe Motion
## 启动
- `dev` — 端口 4062
## 部署事实
- 平台:待定
- 发布状态:已部署
- 主站 / 前端http://localhost:4062
- API / 后端:待定
- 文档 / 解析:待定
- 管理后台:待定
- 代码仓:待定
## 快捷登录
- 登录地址:待补充
- 用户名:待补充
- 密码:待补充
- 说明这里只写项目专属网页登录数据库密码、API Key、服务器 root 密码不要写这里
## 元数据回写清单
- 新增或变更公网地址后,必须同步更新 `.project.json.urls`
- 如果有网页后台登录:
- 可直接入库:写 `.project.json.quick_login`
- 不应入库:写 `.project.json.credentials` 引用
- 部署完成后,`RULES.md``.project.json` 必须同一次任务一起更新
## 环境变量
- 待补充
## 规则
- 不允许编造不存在的部署域名、账号、密码
- 没有公网地址时,`.project.json.urls` 保持空数组
- 任何部署或域名变化,都要先改元数据,再视为任务完成
## 注意事项
- 待补充

857
index.html Normal file
View File

@@ -0,0 +1,857 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vibe Motion — AI 动效革命深度解析</title>
<style>
/* ===== Reset & Base ===== */
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
:root {
--bg: #08090d;
--bg2: #0d0f15;
--surface: rgba(255,255,255,0.04);
--surface2: rgba(255,255,255,0.07);
--border: rgba(255,255,255,0.08);
--text: #e8e8ec;
--text2: rgba(255,255,255,0.55);
--accent: #00ff8c;
--accent2: #00ccff;
--purple: #a855f7;
--pink: #ff44aa;
--orange: #ff9500;
--glow: 0 0 60px rgba(0,255,140,0.15);
}
html { scroll-behavior: smooth; }
body {
font-family: -apple-system, 'SF Pro Display', 'Inter', system-ui, sans-serif;
background: var(--bg);
color: var(--text);
line-height: 1.7;
overflow-x: hidden;
}
a { color: var(--accent); text-decoration: none; transition: opacity .2s; }
a:hover { opacity: .8; }
/* ===== Ambient Background ===== */
.ambient {
position: fixed; inset: 0; z-index: 0; pointer-events: none;
background:
radial-gradient(ellipse 800px 600px at 20% 30%, rgba(0,255,140,0.04) 0%, transparent 70%),
radial-gradient(ellipse 600px 500px at 80% 60%, rgba(0,200,255,0.03) 0%, transparent 70%),
radial-gradient(ellipse 500px 400px at 50% 80%, rgba(168,85,247,0.03) 0%, transparent 70%);
}
.grid-bg {
position: fixed; inset: 0; z-index: 0; pointer-events: none;
background-image:
linear-gradient(rgba(255,255,255,0.02) 1px, transparent 1px),
linear-gradient(90deg, rgba(255,255,255,0.02) 1px, transparent 1px);
background-size: 60px 60px;
mask-image: radial-gradient(ellipse 80% 60% at 50% 40%, black, transparent);
}
/* ===== Layout ===== */
.container { max-width: 1200px; margin: 0 auto; padding: 0 24px; position: relative; z-index: 1; }
section { padding: 100px 0; }
/* ===== Hero ===== */
.hero {
min-height: 100vh; display: flex; flex-direction: column;
align-items: center; justify-content: center; text-align: center;
position: relative;
}
.hero-badge {
display: inline-flex; align-items: center; gap: 8px;
padding: 6px 16px; border-radius: 100px;
background: rgba(0,255,140,0.08); border: 1px solid rgba(0,255,140,0.2);
font-size: 13px; color: var(--accent); letter-spacing: 2px;
text-transform: uppercase; margin-bottom: 32px;
animation: fadeInUp .8s ease both;
}
.hero-badge::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: var(--accent); animation: pulse 2s infinite; }
.hero h1 {
font-size: clamp(48px, 8vw, 96px); font-weight: 800;
letter-spacing: -3px; line-height: 1.05;
background: linear-gradient(135deg, #fff 0%, var(--accent) 50%, var(--accent2) 100%);
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
animation: fadeInUp .8s .1s ease both;
}
.hero p {
font-size: clamp(16px, 2vw, 22px); color: var(--text2);
max-width: 680px; margin: 24px auto 0;
animation: fadeInUp .8s .2s ease both;
}
.hero-cta {
display: flex; gap: 16px; margin-top: 48px;
animation: fadeInUp .8s .3s ease both;
}
.btn {
padding: 14px 32px; border-radius: 12px; font-size: 15px; font-weight: 600;
cursor: pointer; border: none; transition: all .3s;
}
.btn-primary {
background: var(--accent); color: #000;
box-shadow: 0 0 30px rgba(0,255,140,0.3);
}
.btn-primary:hover { box-shadow: 0 0 50px rgba(0,255,140,0.5); transform: translateY(-2px); }
.btn-ghost {
background: var(--surface2); color: var(--text);
border: 1px solid var(--border);
}
.btn-ghost:hover { background: rgba(255,255,255,0.1); transform: translateY(-2px); }
.scroll-hint {
position: absolute; bottom: 40px;
display: flex; flex-direction: column; align-items: center; gap: 8px;
color: var(--text2); font-size: 12px; letter-spacing: 2px;
animation: bounce 2s infinite;
}
.scroll-hint svg { width: 20px; height: 20px; stroke: var(--text2); }
/* ===== Section Headers ===== */
.section-tag {
display: inline-flex; align-items: center; gap: 8px;
font-size: 12px; letter-spacing: 3px; text-transform: uppercase;
color: var(--accent); margin-bottom: 16px;
}
.section-tag::before { content: ''; width: 24px; height: 1px; background: var(--accent); }
.section-title {
font-size: clamp(32px, 4vw, 52px); font-weight: 800;
letter-spacing: -1.5px; margin-bottom: 16px;
}
.section-desc { color: var(--text2); font-size: 17px; max-width: 640px; margin-bottom: 48px; }
/* ===== Paradigm Shift Section ===== */
.paradigm-grid {
display: grid; grid-template-columns: 1fr 80px 1fr; gap: 24px; align-items: stretch;
}
.paradigm-card {
background: var(--surface); border: 1px solid var(--border);
border-radius: 20px; padding: 36px; position: relative; overflow: hidden;
}
.paradigm-card::before {
content: ''; position: absolute; inset: 0; opacity: 0;
transition: opacity .4s;
}
.paradigm-card:hover::before { opacity: 1; }
.paradigm-card.old::before { background: radial-gradient(circle at 50% 0, rgba(255,68,68,0.06), transparent 60%); }
.paradigm-card.new::before { background: radial-gradient(circle at 50% 0, rgba(0,255,140,0.08), transparent 60%); }
.paradigm-card h3 { font-size: 22px; margin-bottom: 20px; display: flex; align-items: center; gap: 12px; }
.paradigm-card ul { list-style: none; }
.paradigm-card li {
padding: 10px 0; border-bottom: 1px solid var(--border);
font-size: 14px; color: var(--text2); display: flex; align-items: center; gap: 10px;
}
.paradigm-card li:last-child { border-bottom: none; }
.paradigm-card.old li::before { content: '✕'; color: #ff4444; font-size: 12px; font-weight: 700; }
.paradigm-card.new li::before { content: '✓'; color: var(--accent); font-size: 14px; font-weight: 700; }
.vs-divider {
display: flex; align-items: center; justify-content: center;
font-size: 18px; font-weight: 900; color: var(--text2);
background: var(--surface); border-radius: 50%;
width: 56px; height: 56px; margin: auto;
border: 1px solid var(--border);
}
/* ===== How It Works ===== */
.flow-steps {
display: grid; grid-template-columns: repeat(4, 1fr); gap: 2px;
}
.flow-step {
background: var(--surface); padding: 32px 24px; position: relative;
border: 1px solid var(--border); overflow: hidden;
transition: all .3s;
}
.flow-step:first-child { border-radius: 16px 0 0 16px; }
.flow-step:last-child { border-radius: 0 16px 16px 0; }
.flow-step:hover { background: var(--surface2); transform: translateY(-4px); border-color: rgba(0,255,140,0.2); }
.flow-step .step-num {
font-size: 48px; font-weight: 900;
background: linear-gradient(135deg, var(--accent), var(--accent2));
-webkit-background-clip: text; -webkit-text-fill-color: transparent;
opacity: .3; position: absolute; top: 16px; right: 16px;
}
.flow-step h4 { font-size: 16px; margin-bottom: 8px; }
.flow-step p { font-size: 13px; color: var(--text2); line-height: 1.6; }
.flow-arrow { color: var(--accent); font-size: 20px; margin: 0 -8px; z-index: 1; align-self: center; }
/* ===== Ecosystem ===== */
.eco-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
.eco-card {
background: var(--surface); border: 1px solid var(--border);
border-radius: 16px; padding: 28px; transition: all .4s;
position: relative; overflow: hidden;
}
.eco-card:hover {
transform: translateY(-6px);
border-color: rgba(0,255,140,0.25);
box-shadow: var(--glow);
}
.eco-card .eco-icon {
width: 48px; height: 48px; border-radius: 12px;
display: flex; align-items: center; justify-content: center;
font-size: 22px; margin-bottom: 16px;
}
.eco-card h3 { font-size: 18px; margin-bottom: 4px; }
.eco-card .eco-tag {
font-size: 11px; color: var(--accent); letter-spacing: 1px;
text-transform: uppercase; margin-bottom: 12px; display: block;
}
.eco-card p { font-size: 13px; color: var(--text2); line-height: 1.6; margin-bottom: 16px; }
.eco-card .eco-meta {
display: flex; gap: 16px; font-size: 12px; color: var(--text2);
border-top: 1px solid var(--border); padding-top: 12px;
}
.eco-card .eco-meta span { display: flex; align-items: center; gap: 4px; }
.eco-card .eco-meta .stars { color: var(--orange); }
/* ===== Comparison Table ===== */
.compare-table {
width: 100%; border-collapse: separate; border-spacing: 0;
background: var(--surface); border-radius: 16px; overflow: hidden;
border: 1px solid var(--border);
}
.compare-table th, .compare-table td {
padding: 16px 20px; text-align: left; border-bottom: 1px solid var(--border);
}
.compare-table th {
font-size: 12px; text-transform: uppercase; letter-spacing: 2px;
color: var(--text2); background: rgba(255,255,255,0.02);
}
.compare-table td { font-size: 14px; color: var(--text2); }
.compare-table tr:last-child td { border-bottom: none; }
.compare-table td:first-child { color: var(--text); font-weight: 600; }
.compare-table .check { color: var(--accent); }
.compare-table .cross { color: rgba(255,255,255,0.2); }
.highlight-row { background: rgba(0,255,140,0.03); }
/* ===== Use Cases ===== */
.use-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; }
.use-card {
background: var(--surface); border: 1px solid var(--border);
border-radius: 16px; padding: 28px; transition: all .3s;
display: flex; gap: 20px;
}
.use-card:hover { border-color: rgba(0,255,140,0.2); transform: translateY(-3px); }
.use-icon {
width: 52px; height: 52px; border-radius: 14px; flex-shrink: 0;
display: flex; align-items: center; justify-content: center;
font-size: 24px;
}
.use-card h4 { font-size: 16px; margin-bottom: 6px; }
.use-card p { font-size: 13px; color: var(--text2); }
/* ===== Timeline ===== */
.timeline { position: relative; padding-left: 40px; }
.timeline::before {
content: ''; position: absolute; left: 14px; top: 0; bottom: 0;
width: 2px; background: linear-gradient(to bottom, var(--accent), var(--purple), transparent);
}
.timeline-item {
position: relative; margin-bottom: 36px; padding: 20px 24px;
background: var(--surface); border: 1px solid var(--border);
border-radius: 14px;
}
.timeline-item::before {
content: ''; position: absolute; left: -33px; top: 24px;
width: 10px; height: 10px; border-radius: 50%;
background: var(--accent); box-shadow: 0 0 12px rgba(0,255,140,0.5);
}
.timeline-item .date {
font-size: 12px; color: var(--accent); letter-spacing: 1px;
text-transform: uppercase; margin-bottom: 4px;
}
.timeline-item h4 { font-size: 16px; margin-bottom: 6px; }
.timeline-item p { font-size: 13px; color: var(--text2); }
/* ===== Code Demo ===== */
.code-block {
background: #0a0c12; border: 1px solid var(--border);
border-radius: 14px; overflow: hidden; margin: 32px 0;
}
.code-header {
display: flex; align-items: center; gap: 8px; padding: 14px 20px;
background: rgba(255,255,255,0.02); border-bottom: 1px solid var(--border);
}
.code-dot { width: 12px; height: 12px; border-radius: 50%; }
.code-dot.r { background: #ff5f57; }
.code-dot.y { background: #febc2e; }
.code-dot.g { background: #28c840; }
.code-filename { margin-left: 12px; font-size: 13px; color: var(--text2); font-family: 'SF Mono', monospace; }
.code-content {
padding: 20px 24px; font-family: 'SF Mono', 'Fira Code', monospace;
font-size: 13px; line-height: 1.8; overflow-x: auto;
color: #abb2bf;
}
.code-content .kw { color: #c678dd; }
.code-content .fn { color: #61afef; }
.code-content .str { color: #98c379; }
.code-content .num { color: #d19a66; }
.code-content .cm { color: #5c6370; font-style: italic; }
.code-content .tag { color: #e06c75; }
.code-content .attr { color: #d19a66; }
/* ===== Quick Start ===== */
.quickstart-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; }
.qs-card {
background: var(--surface); border: 1px solid var(--border);
border-radius: 16px; padding: 28px; text-align: center;
transition: all .3s;
}
.qs-card:hover { border-color: rgba(0,255,140,0.25); transform: translateY(-4px); }
.qs-card .qs-num {
width: 40px; height: 40px; border-radius: 50%;
background: rgba(0,255,140,0.1); border: 1px solid rgba(0,255,140,0.2);
display: flex; align-items: center; justify-content: center;
font-size: 16px; font-weight: 800; color: var(--accent);
margin: 0 auto 16px;
}
.qs-card h4 { font-size: 16px; margin-bottom: 8px; }
.qs-card code {
display: inline-block; padding: 8px 16px; border-radius: 8px;
background: rgba(0,0,0,0.4); border: 1px solid var(--border);
font-family: 'SF Mono', monospace; font-size: 13px; color: var(--accent);
margin-top: 12px;
}
/* ===== Footer ===== */
footer {
border-top: 1px solid var(--border); padding: 48px 0;
text-align: center; color: var(--text2); font-size: 13px;
}
footer .footer-links { display: flex; justify-content: center; gap: 32px; margin-bottom: 24px; }
/* ===== Animations ===== */
@keyframes fadeInUp {
from { opacity: 0; transform: translateY(30px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: .4; } }
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(8px); }
}
/* IntersectionObserver reveal */
.reveal { opacity: 0; transform: translateY(40px); transition: all .7s cubic-bezier(.22,1,.36,1); }
.reveal.visible { opacity: 1; transform: translateY(0); }
/* ===== Responsive ===== */
@media (max-width: 768px) {
.paradigm-grid { grid-template-columns: 1fr; }
.vs-divider { margin: -8px auto; }
.flow-steps { grid-template-columns: 1fr; }
.flow-step { border-radius: 12px !important; }
.eco-grid, .quickstart-grid { grid-template-columns: 1fr; }
.use-grid { grid-template-columns: 1fr; }
.compare-table { font-size: 12px; }
.compare-table th, .compare-table td { padding: 10px 12px; }
section { padding: 60px 0; }
}
/* ===== Floating Lizard ===== */
.float-lizard {
position: fixed; bottom: 24px; right: 24px; z-index: 100;
width: 60px; height: 60px; border-radius: 50%;
background: rgba(0,255,140,0.1); border: 1px solid rgba(0,255,140,0.3);
display: flex; align-items: center; justify-content: center;
font-size: 28px; cursor: pointer; transition: all .3s;
backdrop-filter: blur(12px);
}
.float-lizard:hover {
transform: scale(1.1); box-shadow: 0 0 30px rgba(0,255,140,0.3);
}
</style>
</head>
<body>
<div class="ambient"></div>
<div class="grid-bg"></div>
<!-- ===== HERO ===== -->
<section class="hero">
<div class="hero-badge">2026 新范式</div>
<h1>Vibe Motion</h1>
<p>不再逐帧调关键帧,不再等 AI 生成模糊像素。用自然语言描述动画意图AI 生成确定性代码,渲染出 4K/60fps 专业级动效视频。</p>
<div class="hero-cta">
<a href="#ecosystem" class="btn btn-primary">开源生态 ↓</a>
<a href="#quickstart" class="btn btn-ghost">快速开始</a>
</div>
<div class="scroll-hint">
<svg viewBox="0 0 24 24" fill="none" stroke-width="2"><path d="M12 5v14M19 12l-7 7-7-7"/></svg>
SCROLL
</div>
</section>
<!-- ===== PARADIGM SHIFT ===== -->
<section id="paradigm">
<div class="container">
<div class="section-tag">范式转移</div>
<h2 class="section-title reveal">从像素预测到代码生成</h2>
<p class="section-desc reveal">传统 AI 视频逐帧预测像素,文字会糊、元素会飘、不可编辑。<br>Vibe Motion 让 AI 生成 Remotion 代码——数学定义运动,确定性渲染,每一帧都精确。</p>
<div class="paradigm-grid reveal">
<!-- Old Way -->
<div class="paradigm-card old">
<h3><span style="font-size:28px">🎬</span> 传统方式</h3>
<ul>
<li>After Effects 手动调关键帧,学习曲线陡峭</li>
<li>AI 视频生成器Sora/Kling预测像素文字变形</li>
<li>输出不可编辑,修改需重新生成</li>
<li>分辨率受限,放大即模糊</li>
<li>数万元 + 数周制作周期</li>
<li>品牌一致性难以保证</li>
</ul>
</div>
<div class="vs-divider">VS</div>
<!-- New Way -->
<div class="paradigm-card new">
<h3><span style="font-size:28px"></span> Vibe Motion</h3>
<ul>
<li>自然语言描述 → AI 生成 React 动画代码</li>
<li>确定性渲染,文字永远清晰锐利</li>
<li>代码可编辑,迭代只需追加对话</li>
<li>原生支持 4K UHD / 60fps</li>
<li>10 分钟完成,成本趋近于零</li>
<li>代码即品牌规范100% 一致</li>
</ul>
</div>
</div>
</div>
</section>
<!-- ===== HOW IT WORKS ===== -->
<section id="howitworks" style="background: var(--bg2);">
<div class="container">
<div class="section-tag">工作原理</div>
<h2 class="section-title reveal">四步生成专业动效</h2>
<p class="section-desc reveal">从自然语言到 MP4 视频,中间是可审查、可编辑、可版本控制的代码。</p>
<div class="flow-steps reveal">
<div class="flow-step">
<div class="step-num">01</div>
<h4>描述意图</h4>
<p>用自然语言描述你想要的动画效果。"一只绿色蜥蜴从左侧滑入,尾巴摆动,舌头弹射捕捉萤火虫"</p>
</div>
<div class="flow-step">
<div class="step-num">02</div>
<h4>AI 生成代码</h4>
<p>Claude / GPT 将创意意图翻译为 Remotion React 组件代码,包含 spring 动画、插值、序列编排</p>
</div>
<div class="flow-step">
<div class="step-num">03</div>
<h4>实时预览</h4>
<p>Remotion Studio 在浏览器中实时预览,可拖拽时间轴、逐帧检查、热更新代码修改</p>
</div>
<div class="flow-step">
<div class="step-num">04</div>
<h4>渲染输出</h4>
<p>一行命令渲染为 MP4/GIF/WebM支持 4K 分辨率、60fps、透明通道可批量生成万级变体</p>
</div>
</div>
</div>
</section>
<!-- ===== CODE DEMO ===== -->
<section id="code">
<div class="container">
<div class="section-tag">代码即动画</div>
<h2 class="section-title reveal">一段对话,一个动画</h2>
<p class="section-desc reveal">你说"蜥蜴摆尾"AI 生成这样的代码——数学精确、完全可控。</p>
<div class="code-block reveal">
<div class="code-header">
<span class="code-dot r"></span>
<span class="code-dot y"></span>
<span class="code-dot g"></span>
<span class="code-filename">LizardScene.tsx</span>
</div>
<div class="code-content">
<span class="cm">// 弹簧物理入场</span>
<span class="kw">const</span> scaleSpring = <span class="fn">spring</span>({
fps, frame: localFrame,
config: { damping: <span class="num">12</span>, stiffness: <span class="num">80</span> }
});
<span class="cm">// 尾巴正弦摆动</span>
<span class="kw">const</span> tailWag = Math.<span class="fn">sin</span>(localFrame * <span class="num">0.15</span>) * <span class="num">8</span>;
<span class="cm">// 舌头弹射 — 每80帧触发一次</span>
<span class="kw">const</span> tonguePhase = (localFrame % <span class="num">80</span>) / <span class="num">80</span>;
<span class="kw">const</span> tongueOut = tonguePhase > <span class="num">0.3</span> && tonguePhase < <span class="num">0.5</span>
? <span class="fn">interpolate</span>(tonguePhase, [<span class="num">0.3</span>, <span class="num">0.4</span>, <span class="num">0.5</span>], [<span class="num">0</span>, <span class="num">1</span>, <span class="num">0</span>])
: <span class="num">0</span>;
<span class="cm">// SVG 路径 + 渐变 + 发光</span>
&lt;<span class="tag">path</span> <span class="attr">d</span>=<span class="str">{LIZARD_TAIL}</span>
<span class="attr">transform</span>=<span class="str">{`rotate(${tailWag}, 200, 33)`}</span>
<span class="attr">filter</span>=<span class="str">{`drop-shadow(0 0 ${glow}px ${color})`}</span> /&gt;
</div>
</div>
</div>
</section>
<!-- ===== ECOSYSTEM ===== -->
<section id="ecosystem" style="background: var(--bg2);">
<div class="container">
<div class="section-tag">开源生态</div>
<h2 class="section-title reveal">六大核心项目</h2>
<p class="section-desc reveal">Vibe Motion 不是一个工具,而是一个由开源项目构成的创作范式。以下是你需要关注的核心项目。</p>
<div class="eco-grid">
<!-- Remotion -->
<div class="eco-card reveal">
<div class="eco-icon" style="background: rgba(0,255,140,0.1); color: var(--accent);">🎥</div>
<h3>Remotion</h3>
<span class="eco-tag">核心引擎 · Vibe Motion 底层</span>
<p>用 React 编程式生成视频。支持 CSS/Canvas/SVG/WebGL确定性逐帧渲染可本地或云端批量生成。Higgsfield Vibe Motion 就是基于它。</p>
<div class="eco-meta">
<span class="stars">★ 25,000+</span>
<span>TypeScript</span>
<span><a href="https://github.com/remotion-dev/remotion">GitHub ↗</a></span>
</div>
</div>
<!-- Motion -->
<div class="eco-card reveal">
<div class="eco-icon" style="background: rgba(168,85,247,0.1); color: var(--purple);"></div>
<h3>Motion (Framer Motion)</h3>
<span class="eco-tag">网页动画之王</span>
<p>npm 月下载 3000万+,最主流的 React 动画库。已官方支持 AI Vibe Coding 工作流Motion Studio 插件可在 VS Code 中实时编辑动画。</p>
<div class="eco-meta">
<span class="stars">★ 26,000+</span>
<span>TypeScript</span>
<span><a href="https://github.com/motiondivision/motion">GitHub ↗</a></span>
</div>
</div>
<!-- Motion Canvas -->
<div class="eco-card reveal">
<div class="eco-icon" style="background: rgba(0,200,255,0.1); color: var(--accent2);">🎨</div>
<h3>Motion Canvas</h3>
<span class="eco-tag">TypeScript 版 Manim</span>
<p>用 TypeScript 创建教育动画和解释视频,类似 3Blue1Brown 的 Manim 但基于 Web 技术栈。内置可视化编辑器和时间轴。</p>
<div class="eco-meta">
<span class="stars">★ 16,000+</span>
<span>TypeScript</span>
<span><a href="https://github.com/motion-canvas/motion-canvas">GitHub ↗</a></span>
</div>
</div>
<!-- Open-Sora -->
<div class="eco-card reveal">
<div class="eco-icon" style="background: rgba(255,149,0,0.1); color: var(--orange);">🎞️</div>
<h3>Open-Sora</h3>
<span class="eco-tag">像素级 AI 视频生成</span>
<p>开源复现 OpenAI Sora11B 参数,支持 text/image/video-to-video。与 Vibe Motion 代码生成互补——适合真实场景视频,不适合精确图形动画。</p>
<div class="eco-meta">
<span class="stars">★ 25,000+</span>
<span>Python</span>
<span><a href="https://github.com/hpcaitech/Open-Sora">GitHub ↗</a></span>
</div>
</div>
<!-- Manim -->
<div class="eco-card reveal">
<div class="eco-icon" style="background: rgba(255,68,170,0.1); color: var(--pink);"></div>
<h3>Manim</h3>
<span class="eco-tag">数学动画传奇</span>
<p>3Blue1Brown 创造的数学动画引擎Python 编写。适合数学公式推导、数据可视化、教育内容。社区版 MIT 协议。</p>
<div class="eco-meta">
<span class="stars">★ 70,000+</span>
<span>Python</span>
<span><a href="https://github.com/3b1b/manim">GitHub ↗</a></span>
</div>
</div>
<!-- Rive -->
<div class="eco-card reveal">
<div class="eco-icon" style="background: rgba(255,255,255,0.06); color: #fff;"></div>
<h3>Rive</h3>
<span class="eco-tag">交互式状态机动画</span>
<p>可视化编辑器 + 状态机驱动,适合做 App 内交互动画(按钮微动效、加载动画、游戏 UI。运行时开源编辑器免费使用。</p>
<div class="eco-meta">
<span>跨平台</span>
<span><a href="https://rive.app">rive.app ↗</a></span>
</div>
</div>
</div>
</div>
</section>
<!-- ===== COMPARISON ===== -->
<section id="compare">
<div class="container">
<div class="section-tag">对比分析</div>
<h2 class="section-title reveal">选择适合你的工具</h2>
<p class="section-desc reveal">不同场景用不同工具,以下是核心维度对比。</p>
<div class="reveal" style="overflow-x: auto;">
<table class="compare-table">
<thead>
<tr>
<th>维度</th>
<th>Remotion</th>
<th>Motion Canvas</th>
<th>Motion (Framer)</th>
<th>Open-Sora</th>
<th>After Effects</th>
</tr>
</thead>
<tbody>
<tr class="highlight-row">
<td>AI Vibe Coding</td>
<td><span class="check">★★★</span></td>
<td><span class="check">★★☆</span></td>
<td><span class="check">★★★</span></td>
<td><span class="check">★☆☆</span></td>
<td><span class="cross"></span></td>
</tr>
<tr>
<td>输出格式</td>
<td>MP4/GIF/WebM</td>
<td>MP4</td>
<td>网页动画</td>
<td>MP4</td>
<td>全格式</td>
</tr>
<tr>
<td>分辨率</td>
<td>无限制 (4K+)</td>
<td>无限制</td>
<td>N/A (实时)</td>
<td>720p</td>
<td>无限制</td>
</tr>
<tr class="highlight-row">
<td>文字精度</td>
<td><span class="check">像素级精确</span></td>
<td><span class="check">像素级精确</span></td>
<td><span class="check">像素级精确</span></td>
<td><span class="cross">模糊/变形</span></td>
<td><span class="check">精确</span></td>
</tr>
<tr>
<td>学习曲线</td>
<td>React 基础</td>
<td>TypeScript</td>
<td>React 基础</td>
<td>Python/GPU</td>
<td>数月~年</td>
</tr>
<tr>
<td>批量生成</td>
<td><span class="check">✓ 参数化</span></td>
<td><span class="check"></span></td>
<td><span class="cross"></span></td>
<td><span class="check"></span></td>
<td><span class="cross">手动</span></td>
</tr>
<tr class="highlight-row">
<td>GPU 需求</td>
<td>不需要</td>
<td>不需要</td>
<td>不需要</td>
<td>A100 级</td>
<td>推荐有</td>
</tr>
<tr>
<td>开源协议</td>
<td>个人免费</td>
<td>MIT</td>
<td>MIT</td>
<td>Apache 2.0</td>
<td>商业</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- ===== USE CASES ===== -->
<section id="usecases" style="background: var(--bg2);">
<div class="container">
<div class="section-tag">应用场景</div>
<h2 class="section-title reveal">从营销视频到教育内容</h2>
<p class="section-desc reveal">Vibe Motion 的代码生成模式在以下场景尤其强大:</p>
<div class="use-grid">
<div class="use-card reveal">
<div class="use-icon" style="background: rgba(0,255,140,0.1);">📱</div>
<div>
<h4>社交媒体营销</h4>
<p>10 分钟生成品牌一致的动效视频,支持批量生成万级个性化变体。传统制作需数万元 + 数周。</p>
</div>
</div>
<div class="use-card reveal">
<div class="use-icon" style="background: rgba(168,85,247,0.1);">🎓</div>
<div>
<h4>教育解释视频</h4>
<p>类似 3Blue1Brown 的数学动画,用 Remotion/Manim + AI 快速生成算法可视化、物理模拟、概念讲解。</p>
</div>
</div>
<div class="use-card reveal">
<div class="use-icon" style="background: rgba(0,200,255,0.1);">📊</div>
<div>
<h4>数据可视化</h4>
<p>将报表数据转化为动态图表视频,支持 API 驱动自动更新。年报、季度汇报、实时仪表盘录制。</p>
</div>
</div>
<div class="use-card reveal">
<div class="use-icon" style="background: rgba(255,149,0,0.1);">🚀</div>
<div>
<h4>产品发布视频</h4>
<p>Replit 等公司已用 Vibe Motion 制作产品 launch 视频。终端打字动画、代码高亮、截图飞入——10 分钟搞定。</p>
</div>
</div>
<div class="use-card reveal">
<div class="use-icon" style="background: rgba(255,68,170,0.1);">🎮</div>
<div>
<h4>游戏 UI & App 微动效</h4>
<p>Rive 状态机 + Motion 弹簧动画,做按钮反馈、加载指示器、成就解锁动画。运行时开源跨平台。</p>
</div>
</div>
<div class="use-card reveal">
<div class="use-icon" style="background: rgba(255,255,255,0.06);">🔄</div>
<div>
<h4>CI/CD 自动化视频</h4>
<p>代码即视频——纳入 Git 版本控制PR review 审查动画变更CI 自动渲染输出。视频生产变成 git commit。</p>
</div>
</div>
</div>
</div>
</section>
<!-- ===== TIMELINE ===== -->
<section id="timeline">
<div class="container">
<div class="section-tag">发展脉络</div>
<h2 class="section-title reveal">Vibe Motion 简史</h2>
<div class="timeline reveal">
<div class="timeline-item">
<div class="date">2021</div>
<h4>Remotion 1.0 发布</h4>
<p>Jonny Burger 在苏黎世创建 Remotion提出"用 React 写视频"的理念。开源社区开始关注代码生成视频的可能性。</p>
</div>
<div class="timeline-item">
<div class="date">2023</div>
<h4>Motion Canvas 开源</h4>
<p>TypeScript 版的 Manim 问世,填补了 Web 开发者制作教育动画的空白。</p>
</div>
<div class="timeline-item">
<div class="date">2025.02</div>
<h4>Andrej Karpathy 提出 "Vibe Coding"</h4>
<p>OpenAI 联合创始人定义了这个概念:用自然语言和 AI 协作写代码,而不是手动逐行编写。</p>
</div>
<div class="timeline-item">
<div class="date">2025.12</div>
<h4>Remotion Claude Skill 爆发</h4>
<p>Remotion Agent Skill 上线 8 周达到 15 万安装量,成为 skills.sh 上 #1 视频技能。视频制作变成了 "git commit"。</p>
</div>
<div class="timeline-item">
<div class="date">2026.02</div>
<h4>Higgsfield 发布 Vibe Motion</h4>
<p>将 Claude + Remotion 包装成无代码产品,$17.4/月起。正式定义了 "Vibe Motion" 这个品类——AI 驱动的代码生成动效。</p>
</div>
<div class="timeline-item">
<div class="date">2026.03</div>
<h4>生态爆发</h4>
<p>Replit 推出 Vibe Code Videos、Motion.dev 集成 AI Vibe Coding、Cursor 内置 Motion Studio。代码生成动效从小众走向主流。</p>
</div>
</div>
</div>
</section>
<!-- ===== QUICK START ===== -->
<section id="quickstart" style="background: var(--bg2);">
<div class="container">
<div class="section-tag">快速开始</div>
<h2 class="section-title reveal">三步在你的电脑上运行</h2>
<p class="section-desc reveal">不需要 GPU不需要 After Effects只需要 Node.js + 你的想象力。</p>
<div class="quickstart-grid reveal">
<div class="qs-card">
<div class="qs-num">1</div>
<h4>创建项目</h4>
<p style="color: var(--text2); font-size: 13px;">一行命令初始化 Remotion 项目</p>
<code>pnpm create video@latest</code>
</div>
<div class="qs-card">
<div class="qs-num">2</div>
<h4>让 AI 写动画</h4>
<p style="color: var(--text2); font-size: 13px;">用 Claude Code 描述你想要的效果</p>
<code>"做一只蜥蜴摆尾动画"</code>
</div>
<div class="qs-card">
<div class="qs-num">3</div>
<h4>预览 & 渲染</h4>
<p style="color: var(--text2); font-size: 13px;">浏览器预览,一键导出 MP4</p>
<code>pnpm build → out/video.mp4</code>
</div>
</div>
<div class="code-block reveal" style="margin-top: 40px;">
<div class="code-header">
<span class="code-dot r"></span>
<span class="code-dot y"></span>
<span class="code-dot g"></span>
<span class="code-filename">Terminal</span>
</div>
<div class="code-content">
<span class="cm"># 安装依赖</span>
pnpm add remotion @remotion/cli @remotion/player react react-dom
<span class="cm"># 启动 Studio浏览器实时预览</span>
pnpm remotion studio src/index.ts
<span class="cm"># 渲染为 MP4</span>
pnpm remotion render src/index.ts MyAnimation out/video.mp4
<span class="cm"># 渲染为 GIF</span>
pnpm remotion render src/index.ts MyAnimation out/video.gif --image-format png
</div>
</div>
</div>
</section>
<!-- ===== FOOTER ===== -->
<footer>
<div class="container">
<div class="footer-links">
<a href="https://github.com/remotion-dev/remotion">Remotion</a>
<a href="https://github.com/motiondivision/motion">Motion</a>
<a href="https://github.com/motion-canvas/motion-canvas">Motion Canvas</a>
<a href="https://higgsfield.ai/vibe-motion">Higgsfield</a>
<a href="https://www.remotion.dev/docs/ai/claude-code">Remotion × Claude</a>
</div>
<p>Built with Remotion + Claude Code · 2026</p>
<p style="margin-top: 8px; opacity: .4;">源码已下载至 repos/ — Remotion / Motion / Motion Canvas</p>
</div>
</footer>
<!-- Floating lizard -->
<a href="#" class="float-lizard" title="回到顶部">🦎</a>
<script>
// IntersectionObserver for reveal animations
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('visible');
}
});
}, { threshold: 0.1, rootMargin: '0px 0px -40px 0px' });
document.querySelectorAll('.reveal').forEach(el => observer.observe(el));
// Smooth scroll for float lizard
document.querySelector('.float-lizard').addEventListener('click', (e) => {
e.preventDefault();
window.scrollTo({ top: 0, behavior: 'smooth' });
});
</script>
</body>
</html>

26
package.json Normal file
View File

@@ -0,0 +1,26 @@
{
"name": "20260319-vibe-motion",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"dev": "remotion studio src/index.ts",
"build": "remotion render src/index.ts LizardAnimation out/lizard.mp4",
"preview": "remotion preview src/index.ts"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"@remotion/cli": "^4.0.437",
"@remotion/player": "^4.0.437",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"remotion": "^4.0.437"
},
"devDependencies": {
"@types/react": "^19.2.14",
"@types/react-dom": "^19.2.3",
"typescript": "^5.9.3"
}
}

2195
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

3
remotion.config.ts Normal file
View File

@@ -0,0 +1,3 @@
import { Config } from "@remotion/cli/config";
Config.setPort(4062);

407
src/LizardScene.tsx Normal file
View File

@@ -0,0 +1,407 @@
import {
AbsoluteFill,
interpolate,
spring,
useCurrentFrame,
useVideoConfig,
Sequence,
} from "remotion";
// SVG path for a stylized lizard silhouette
const LIZARD_BODY =
"M60,25 Q70,10 85,15 Q100,18 110,25 L140,28 Q160,30 175,25 Q190,20 200,25 Q210,35 200,42 Q195,48 185,45 L170,40 Q160,38 150,42 L130,50 Q120,55 110,50 L100,42 Q90,38 80,42 Q70,48 60,42 Q50,35 60,25Z";
const LIZARD_TAIL =
"M200,33 Q230,28 260,35 Q290,45 310,35 Q330,22 350,30 Q365,38 360,45";
const LIZARD_EYE = "M78,28 A4,4 0 1,1 78,28.01";
const LIZARD_FRONT_LEG_L = "M95,42 Q85,58 75,68 Q70,72 65,68";
const LIZARD_FRONT_LEG_R = "M95,25 Q85,12 78,5 Q74,0 70,4";
const LIZARD_BACK_LEG_L = "M165,42 Q175,58 185,65 Q190,70 195,66";
const LIZARD_BACK_LEG_R = "M165,25 Q175,10 182,3 Q186,-2 190,2";
const TONGUE = "M60,32 Q40,30 25,38 Q20,42 22,35";
// Particle burst
const Particle: React.FC<{
x: number;
y: number;
delay: number;
color: string;
size: number;
angle: number;
}> = ({ x, y, delay, color, size, angle }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const life = Math.max(0, frame - delay);
const progress = Math.min(life / 40, 1);
const dist = interpolate(progress, [0, 1], [0, 200 + Math.random() * 100]);
const opacity = interpolate(progress, [0, 0.2, 0.8, 1], [0, 1, 1, 0]);
const rad = (angle * Math.PI) / 180;
return (
<div
style={{
position: "absolute",
left: x + Math.cos(rad) * dist,
top: y + Math.sin(rad) * dist,
width: size,
height: size,
borderRadius: "50%",
background: color,
opacity,
filter: `blur(${size * 0.3}px)`,
}}
/>
);
};
// Animated grid background
const GridBG: React.FC = () => {
const frame = useCurrentFrame();
const offset = (frame * 0.5) % 60;
return (
<AbsoluteFill>
<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern
id="grid"
width="60"
height="60"
patternUnits="userSpaceOnUse"
patternTransform={`translate(${offset}, ${offset})`}
>
<path
d="M 60 0 L 0 0 0 60"
fill="none"
stroke="rgba(0,255,140,0.08)"
strokeWidth="1"
/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#grid)" />
</svg>
</AbsoluteFill>
);
};
// Animated lizard component
const Lizard: React.FC<{
enterFrame: number;
x: number;
y: number;
scale: number;
hue: number;
}> = ({ enterFrame, x, y, scale, hue }) => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const localFrame = frame - enterFrame;
// Spring entrance
const scaleSpring = spring({
fps,
frame: localFrame,
config: { damping: 12, stiffness: 80 },
});
// Breathing / idle animation
const breathe = Math.sin(localFrame * 0.08) * 2;
// Tail wag
const tailWag = Math.sin(localFrame * 0.15) * 8;
// Tongue flick (every ~60 frames)
const tonguePhase = (localFrame % 80) / 80;
const tongueOut =
tonguePhase > 0.3 && tonguePhase < 0.5
? interpolate(tonguePhase, [0.3, 0.4, 0.5], [0, 1, 0])
: 0;
// Leg walk cycle
const legCycle = Math.sin(localFrame * 0.12) * 6;
// Glow pulse
const glowIntensity = interpolate(
Math.sin(localFrame * 0.06),
[-1, 1],
[10, 25]
);
const color1 = `hsl(${hue}, 90%, 55%)`;
const color2 = `hsl(${hue + 30}, 85%, 45%)`;
const glowColor = `hsla(${hue}, 100%, 60%, 0.6)`;
return (
<div
style={{
position: "absolute",
left: x,
top: y + breathe,
transform: `scale(${scale * scaleSpring})`,
transformOrigin: "center center",
filter: `drop-shadow(0 0 ${glowIntensity}px ${glowColor})`,
}}
>
<svg
width="400"
height="80"
viewBox="0 0 400 80"
xmlns="http://www.w3.org/2000/svg"
>
<defs>
<linearGradient id={`grad-${hue}`} x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stopColor={color1} />
<stop offset="100%" stopColor={color2} />
</linearGradient>
</defs>
{/* Legs with walk cycle */}
<g>
<path
d={LIZARD_FRONT_LEG_L}
stroke={color2}
strokeWidth="4"
fill="none"
strokeLinecap="round"
transform={`rotate(${legCycle}, 95, 42)`}
/>
<path
d={LIZARD_FRONT_LEG_R}
stroke={color2}
strokeWidth="4"
fill="none"
strokeLinecap="round"
transform={`rotate(${-legCycle}, 95, 25)`}
/>
<path
d={LIZARD_BACK_LEG_L}
stroke={color2}
strokeWidth="4"
fill="none"
strokeLinecap="round"
transform={`rotate(${-legCycle}, 165, 42)`}
/>
<path
d={LIZARD_BACK_LEG_R}
stroke={color2}
strokeWidth="4"
fill="none"
strokeLinecap="round"
transform={`rotate(${legCycle}, 165, 25)`}
/>
</g>
{/* Body */}
<path
d={LIZARD_BODY}
fill={`url(#grad-${hue})`}
stroke={color1}
strokeWidth="1.5"
/>
{/* Scales pattern */}
{Array.from({ length: 8 }).map((_, i) => (
<circle
key={i}
cx={90 + i * 14}
cy={33 + Math.sin(i * 1.2 + localFrame * 0.05) * 3}
r={2.5}
fill={`hsla(${hue + 60}, 80%, 70%, 0.5)`}
/>
))}
{/* Tail with wag */}
<path
d={LIZARD_TAIL}
stroke={`url(#grad-${hue})`}
strokeWidth="6"
fill="none"
strokeLinecap="round"
transform={`rotate(${tailWag}, 200, 33)`}
/>
{/* Eye */}
<circle cx={78} cy={28} r={5} fill="white" />
<circle cx={76} cy={28} r={3} fill="#111" />
<circle cx={75} cy={27} r={1} fill="white" />
{/* Tongue */}
<path
d={TONGUE}
stroke="#ff4466"
strokeWidth="2.5"
fill="none"
strokeLinecap="round"
opacity={tongueOut}
/>
</svg>
</div>
);
};
// Title text with reveal
const Title: React.FC = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const titleSpring = spring({
fps,
frame: frame - 20,
config: { damping: 15, stiffness: 60 },
});
const subtitleSpring = spring({
fps,
frame: frame - 45,
config: { damping: 15, stiffness: 60 },
});
return (
<div
style={{
position: "absolute",
top: 80,
left: 0,
right: 0,
textAlign: "center",
}}
>
<div
style={{
fontSize: 72,
fontWeight: 900,
fontFamily: "system-ui, -apple-system, sans-serif",
color: "white",
letterSpacing: -2,
opacity: titleSpring,
transform: `translateY(${interpolate(titleSpring, [0, 1], [40, 0])}px)`,
textShadow: "0 0 40px rgba(0,255,140,0.5), 0 0 80px rgba(0,255,140,0.2)",
}}
>
VIBE MOTION
</div>
<div
style={{
fontSize: 24,
fontWeight: 400,
fontFamily: "system-ui, -apple-system, sans-serif",
color: "rgba(0,255,140,0.8)",
letterSpacing: 8,
marginTop: 12,
opacity: subtitleSpring,
transform: `translateY(${interpolate(subtitleSpring, [0, 1], [20, 0])}px)`,
}}
>
AI-POWERED ANIMATION
</div>
</div>
);
};
// Main scene composition
export const LizardScene: React.FC = () => {
const frame = useCurrentFrame();
const { fps, width, height } = useVideoConfig();
// Background color shift
const bgHue = interpolate(frame, [0, 300], [220, 260]);
// Particle colors
const particleColors = ["#00ff8c", "#00ccff", "#ff44aa", "#ffcc00", "#8844ff"];
return (
<AbsoluteFill
style={{
background: `radial-gradient(ellipse at 50% 50%, hsl(${bgHue}, 30%, 12%) 0%, hsl(${bgHue}, 40%, 4%) 100%)`,
}}
>
{/* Animated grid */}
<GridBG />
{/* Title */}
<Sequence from={10}>
<Title />
</Sequence>
{/* Main lizard - center stage */}
<Sequence from={30}>
<Lizard enterFrame={30} x={700} y={420} scale={2.8} hue={140} />
</Sequence>
{/* Second lizard - smaller, different color */}
<Sequence from={80}>
<Lizard enterFrame={80} x={200} y={600} scale={1.8} hue={280} />
</Sequence>
{/* Third lizard */}
<Sequence from={120}>
<Lizard enterFrame={120} x={1200} y={550} scale={2.0} hue={30} />
</Sequence>
{/* Particle burst at entrance */}
<Sequence from={30}>
{Array.from({ length: 30 }).map((_, i) => (
<Particle
key={i}
x={width / 2}
y={height / 2}
delay={30 + i * 0.5}
color={particleColors[i % particleColors.length]}
size={4 + Math.random() * 6}
angle={(360 / 30) * i}
/>
))}
</Sequence>
{/* Bottom text */}
<Sequence from={150}>
<BottomBar />
</Sequence>
</AbsoluteFill>
);
};
const BottomBar: React.FC = () => {
const frame = useCurrentFrame();
const { fps } = useVideoConfig();
const barSpring = spring({
fps,
frame: frame - 150,
config: { damping: 20, stiffness: 80 },
});
const lineWidth = interpolate(barSpring, [0, 1], [0, 600]);
return (
<div
style={{
position: "absolute",
bottom: 80,
left: 0,
right: 0,
textAlign: "center",
}}
>
{/* Decorative line */}
<div
style={{
width: lineWidth,
height: 2,
background: "linear-gradient(90deg, transparent, #00ff8c, transparent)",
margin: "0 auto 20px",
}}
/>
<div
style={{
fontSize: 18,
fontFamily: "system-ui, -apple-system, sans-serif",
color: "rgba(255,255,255,0.6)",
letterSpacing: 4,
opacity: barSpring,
}}
>
BUILT WITH REMOTION + CLAUDE CODE
</div>
</div>
);
};

17
src/Root.tsx Normal file
View File

@@ -0,0 +1,17 @@
import { Composition } from "remotion";
import { LizardScene } from "./LizardScene";
export const RemotionRoot: React.FC = () => {
return (
<>
<Composition
id="LizardAnimation"
component={LizardScene}
durationInFrames={300}
fps={30}
width={1920}
height={1080}
/>
</>
);
};

4
src/index.ts Normal file
View File

@@ -0,0 +1,4 @@
import { registerRoot } from "remotion";
import { RemotionRoot } from "./Root";
registerRoot(RemotionRoot);

15
tsconfig.json Normal file
View File

@@ -0,0 +1,15 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ES2022",
"moduleResolution": "bundler",
"jsx": "react-jsx",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"outDir": "./dist",
"rootDir": "."
},
"include": ["src/**/*.ts", "src/**/*.tsx", "remotion.config.ts"]
}