[teamai] Push 87 resource(s) from XingfenD
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
# EXAMPLES —— 完整章节 / 题材 anchor
|
||||
|
||||
> ## ⚠️ 这是**结构示意**,不是抄袭模板
|
||||
>
|
||||
> 这些 example **不是给你照抄的**。它们的角色是"看一个完整章节大概
|
||||
> 什么形状、动画怎么分层、CSS 用了哪些 token、outline 长什么样"。
|
||||
>
|
||||
> **正确使用流程**:
|
||||
>
|
||||
> 1. 走完 [`../CHAPTER-CRAFT.md`](../CHAPTER-CRAFT.md) Part 0 五问
|
||||
> 2. 实在卡壳"我这一章的整体结构应该是什么"才翻 EXAMPLES
|
||||
> 3. **保留它的"形"**(step 切分逻辑、字号关系、布局原则),**按本
|
||||
> 项目的主题 + 内容换动作选型**
|
||||
>
|
||||
> 倒过来——先翻 EXAMPLES 选一个照搬到底 = [`../CHAPTER-CRAFT.md`](../CHAPTER-CRAFT.md)
|
||||
> Part 5 第 8 条「整章只用一种入场动画」同质化反模式(每个用户的视频
|
||||
> 看起来像同一个模板的 N 个变奏)。
|
||||
|
||||
两类参考资源,让 agent 在写章节时**有具体形状可参考**,不用从零设计。
|
||||
|
||||
> **不是必须按这个写**。卡壳时翻一翻;用力发挥时大胆偏离。
|
||||
|
||||
## 目录
|
||||
|
||||
### A. 章节结构 anchor(与题材无关)
|
||||
|
||||
| 例子 | 适用场景 | 文件 |
|
||||
|---|---|---|
|
||||
| [`hook-chapter/`](hook-chapter/) | **钩子型开场** —— 多张图片逐张揭示后 hero takeover | `chapter.tsx` + `chapter.css` |
|
||||
| [`list-reveal/`](list-reveal/) | **列举型** —— 口播说"三件事 / N 个特性",每项 1 step | `chapter.tsx` + `chapter.css` |
|
||||
|
||||
每个 example 都是**完整章节**:**内容驱动主导动作** + 必要的伴随动作
|
||||
(**不强求挂持续微动**,按 [`../CHAPTER-CRAFT.md`](../CHAPTER-CRAFT.md)
|
||||
Part 0 原则 7 节制使用)、真素材(不是占位卡)、字号狠对比、绑了
|
||||
`newsroom` 主题作为示范。
|
||||
|
||||
### B. 题材 case anchor(与题材相关)
|
||||
|
||||
| 例子 | 题材 | 文件 |
|
||||
|---|---|---|
|
||||
| [`case-tech-review/`](case-tech-review/) | 科技测评 / 实测对比 / 跑分类视频 | README + outline 节选 |
|
||||
|
||||
> 题材 case 展示**真实 outline 的样子**(含 article 补字段如何填、
|
||||
> 章节切分如何决策)。拿到与某个 case 题材相似的需求时,先翻它再
|
||||
> 写自己的 outline。
|
||||
|
||||
## 怎么用
|
||||
|
||||
### 写章节卡壳时
|
||||
|
||||
1. 看哪个 anchor 跟你这一章**结构最像**(钩子型 vs 列举型 vs 其它)
|
||||
2. 翻 `README.md` 看这个例子的设计思路 + 节奏
|
||||
3. 翻 `chapter.tsx` 看实现:JSX 结构、`step` 切分、用了哪些组件 / 类名
|
||||
4. 翻 `chapter.css` 看动画用了哪些 keyframes、token、`infinite` 持续
|
||||
微动写在哪
|
||||
5. 写自己这一章时**保留 anchor 的"形",按本章内容 + 本主题气质换动画选型**
|
||||
|
||||
### 切换主题时
|
||||
|
||||
每个 example 的 README 末尾有"切到其它主题怎么换"的提示 —— 通常只需要
|
||||
**换主导动作的形式**(newsroom 印章砸下 → terminal 打字机 → chalk
|
||||
粉笔自绘),**结构、step 切分、字号关系不动**。
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 这两个 anchor 是"地板",不是"天花板"
|
||||
|
||||
这两个例子已经引入印章砸下、stagger、accent 红条 —— 但**仍然是相对克
|
||||
制的版本**。**鼓励你做得更狂、更"视频感"**:
|
||||
|
||||
### 进阶玩法(任选搭配)
|
||||
|
||||
| 维度 | 这俩 anchor 给的(地板) | 可以升级到(无上限) |
|
||||
|---|---|---|
|
||||
| 背景层 | 纯色 surface | + SVG turbulence filter 纸纹永不停斜向漂移 |
|
||||
| 主导动作 | mask reveal + 印章砸下 | + Canvas 粒子从屏幕外汇聚成 hero 字 |
|
||||
| 伴随动作 | accent 红条 scaleX | + SVG path stroke-dashoffset 自绘下划线 / 装饰花纹 |
|
||||
| 持续微动 | accent 光晕呼吸 | + 多层粒子漂移 / scanline / ken burns 缓推 |
|
||||
| 数字 hero | 直接显示 | + JS 数字滚动(`requestAnimationFrame` + easeOutQuart) |
|
||||
| 流程 / 架构 | 仅文字列 | + SVG path 自绘流程图(每条线 stroke-dashoffset 错峰) |
|
||||
| 对比图 | 两段文字 | + SVG 双柱图自绘 + 差值数字滚动 |
|
||||
| 转场 | 章节边界硬切 | + clip-path inset 横向擦除转场 |
|
||||
|
||||
→ 详细工具箱见 [`../CHAPTER-CRAFT.md`](../CHAPTER-CRAFT.md) Part 2
|
||||
"视觉手段全栈工具箱"(CSS / SVG / Canvas / JS 四层)。
|
||||
|
||||
### 实测原则
|
||||
|
||||
写章节时,**先实现 anchor 同等的地板版本**(按 [`../CHAPTER-CRAFT.md`](../CHAPTER-CRAFT.md)
|
||||
Part 0 五问选好主导动作),跑起来确认气质对,**再决定要不要加伴随
|
||||
动作 / 持续微动**。
|
||||
|
||||
**判断标准**:
|
||||
- 如果不同 step 的主导动作够多样(PPT 警报通过 [`../CHAPTER-CRAFT.md`](../CHAPTER-CRAFT.md)
|
||||
Part 0 原则 7 自检)= 不需要再加持续微动
|
||||
- 如果整章主导动作太单一 = 不要靠"加持续微动"补救,**回 [`../CHAPTER-CRAFT.md`](../CHAPTER-CRAFT.md)
|
||||
Part 1 五问换主导动作**才是正解(参 Part 5 第 8 条「整章只用一种入场动画」)
|
||||
|
||||
## 不在 EXAMPLES 里出现的章节类型
|
||||
|
||||
- **数字型 hero**("+47%" → "几乎快了一倍")
|
||||
- **对比型**(前后对照 / 双柱图)
|
||||
- **链接卡片收尾**
|
||||
|
||||
这些场景的视觉原语已经在 [`../CHAPTER-CRAFT.md`](../CHAPTER-CRAFT.md)
|
||||
Part 3 视觉工具箱(CSS / SVG / Canvas / JS 全栈)里覆盖了;按 anchor
|
||||
的"形"组合即可。
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
# Case: 科技测评类(tech review)
|
||||
|
||||
一篇 AI / 工具 / 产品**实测对比**类文章 → 7 章 36 步、6 分 30 秒视频
|
||||
的真实案例。
|
||||
|
||||
> ## ⚠️ 这是结构示意 / 历史案例,不是抄袭模板
|
||||
>
|
||||
> 这个目录的角色是让 agent 看:"**测评类视频章节怎么切、信息池怎么
|
||||
> 抽、章长怎么定**"。**它包含的具体动画描述(如"慢速 blur clear
|
||||
> 1.5s ease-out / 打字机每字 80~100ms")属于历史版本** —— 新版
|
||||
> outline 已经**不写动画 / 不写时长**(见 [`../../OUTLINE-FORMAT.md`](../../OUTLINE-FORMAT.md))。
|
||||
> 新写 outline 时只写"屏幕内容 + 关系名前缀 + 章节级信息池",动画
|
||||
> 选型留给章节实现阶段按 [`../../CHAPTER-CRAFT.md`](../../CHAPTER-CRAFT.md)
|
||||
> Part 0 五问决定。
|
||||
>
|
||||
> **看这个 case 学的应该是**:
|
||||
> 1. 测评类怎么切 7 章(钩子 → 优点 → 场景 → 进阶 → 收束)
|
||||
> 2. 章长怎么定(每章 4~6 step 防疲劳)
|
||||
> 3. 双源原则怎么落地(hero 来自 script / 数据角标来自 article)
|
||||
>
|
||||
> **不应该学**:动画选型、CSS 实现、时长数值(这些已下放到 chapter
|
||||
> 阶段)。
|
||||
|
||||
## 适用场景
|
||||
|
||||
- AI 模型 / 产品 / 工具的实测体验文
|
||||
- 多家产品对比(A vs B vs C)
|
||||
- 跑分 / benchmark / 用户投票数据驱动的内容
|
||||
- "强在哪 / 怎么用 / 怎么用得好"型结构
|
||||
|
||||
## 关键决策
|
||||
|
||||
| 维度 | 这个案例的选择 | 通用启发 |
|
||||
|---|---|---|
|
||||
| 主题 | `midnight-press`(电影感慢镜、blur clear、暖橙 accent、scanline) | 科技测评类适合"克制、有重量"的暗色调;避开俏皮 / 糖果色 |
|
||||
| 章节切分 | 7 章:开场悬念 / 强在哪 / 哪能用 / 怎么用好 / Skill 介绍 / Skill 模式 / 收尾 | 测评类的标准结构:钩子 → 优点 → 场景 → 进阶 → 收束 |
|
||||
| 章长 | 每章 4~6 step | 测评类信息密度高,每章不超过 6 step 防止观众疲劳 |
|
||||
| 双源应用 | hero 标语来自 script、画面密度(具体分数 / 投票数 / 时间戳)来自 article | 测评类 article 数据极多 —— 用 mono cue / 角标 / 数据浮层挂出来 |
|
||||
| 动画风格 | 慢速 blur clear / 打字机 / ken burns 缓推 | midnight-press 暗色印刷气质,章节实现时按主题氛围自由发挥 |
|
||||
|
||||
## 文件
|
||||
|
||||
- [`outline-snippet.md`](outline-snippet.md) —— 前 2 章完整节选(5 + 5 step),
|
||||
展示双源原则在 outline 里怎么落地
|
||||
|
||||
> 完整 7 章 outline 在调用此 Skill 的具体项目里(`gpt-image2-video/outline.md`),
|
||||
> 不放进 Skill 仓库 —— 避免 Skill spec 被某一个项目内容污染。
|
||||
|
||||
## 不在这个 case 出现的情形
|
||||
|
||||
测评类**通常不需要**:
|
||||
- 慢节奏长镜头(电影感片头 / 旅行 vlog 才需要)
|
||||
- 手写温暖感(教育 / 亲子 / 食谱才需要)
|
||||
- 大量插画(设计稿 / 工艺品类才需要)
|
||||
|
||||
→ 选别的 case anchor 或自由发挥。
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
# Outline 节选 · 科技测评类 case
|
||||
|
||||
> **节选**:前 2 章(10 step),用来展示 outline 在科技测评题材里的
|
||||
> 形状。完整版 7 章 36 步在调用此 Skill 的具体项目里,不进 spec。
|
||||
|
||||
> **主题**:`midnight-press`(电影感慢镜、blur clear、暖橙 accent、
|
||||
> scanline;克制有重量。**禁**砸下 shake / 弹簧 / emoji)
|
||||
>
|
||||
> **总时长**:约 6 分 30 秒
|
||||
|
||||
---
|
||||
|
||||
## 1. coldopen — 登顶悬念(5 steps · ~30s)
|
||||
|
||||
- **step 1** (~5s) — 暗场远景粒子云 + 钩子字幕"我刷到一张图,愣了三秒"
|
||||
· 动画:屏幕从纯黑慢速 fade 到暗暖底(1.5s ease-out)→ 远景光尘粒子云慢漂浮入(1.0s 错峰)→ 字幕 mono 打字机逐字打出(每字 100ms);持续微动:粒子云永不停 brownian 漂移 + 暖橙暗角光晕 6s 周期慢呼吸 + ken burns 缓推
|
||||
· 手段:CSS background 慢 fade + Canvas 粒子云慢漂 + JS typewriter + filter: drop-shadow 暖橙呼吸 + transform: scale 永动 ken burns
|
||||
|
||||
- **step 2** (~7s) — 排行榜慢镜景深聚焦 + 主分数 hero 数字 blur clear 浮出
|
||||
· 动画:截图从 blur(12px) + scale(1.05) 慢速景深聚焦(1.5s ease-out)→ 主分数从 blur(20px) 慢慢锐化(1.2s 错峰 400ms)→ 王冠 SVG 沿数字外圈慢速 mask reveal(1.5s);持续微动:主分数暖橙光晕 5s 呼吸 + ken burns 缓推
|
||||
· 手段:filter: blur 反向 + transform scale 慢推 + clip-path 沿 path mask reveal + filter: drop-shadow 呼吸
|
||||
· article 补:主分数(来自 article §1,具体数字)+ 测评窗口("X 月 N 日 ~ Y 日")+ 投票数(mono cue 角标)—— 口播只说"换榜首",画面把"领先多少 / 多少票投出来的"全挂上
|
||||
|
||||
- **step 3** (~6s) — 第 2 名对比横条 + "+差距分"慢浮锐化
|
||||
· 动画:第一条从左向右慢速 mask reveal 拉到 100%(1.2s ease-out)→ 第二条同向 mask reveal 但只到 ~70%(错峰 600ms)→ 中间空缺区差距分从 blur(15px) 慢慢锐化进场(错峰);持续微动:差距区暖橙光晕慢呼吸 + accent 横线 8s 缓延展永动 + scanline 极淡 overlay 慢移
|
||||
· 手段:clip-path inset 慢 reveal + filter: blur 反向 + linear-gradient 暖光晕 + linear-gradient scanline 永动
|
||||
· article 补:第 2 名具体名字(article §1)+ 差距分(具体数字 vs 模糊"低很多")+ 趋势注释("过去 N 周首次反超")
|
||||
|
||||
- **step 4** (~6s) — 官方原话 pull-quote 慢镜入场(电影感引文)
|
||||
· 动画:左右两枚巨大引号 SVG 从 opacity 0 + blur(15px) 慢速锐化进场(1.0s 错峰 200ms,**无砸下**)→ 引文文字 mono 打字机逐字打出(每字 80ms)→ 落款慢速 blur clear 浮出(0.8s);持续微动:引号暖橙慢光晕呼吸 + 镜头 ken burns 缓推
|
||||
· 手段:filter: blur 反向锐化 + JS typewriter + transform translateY 慢推 + filter: drop-shadow 呼吸
|
||||
· article 补:原话直引(来自 article §1,1~2 句)+ 落款来源("— 出处.AI")—— 引文是 article 里口播完全省略的"权威背书"
|
||||
|
||||
- **step 5** (~5s) — 主持人介绍 + 4 件事预告速览
|
||||
· 动画:第一行自我介绍 blur clear 慢入场(1.2s ease-out)→ 4 张占位卡分别从 blur(15px) 慢速景深聚焦 stagger 出现(每张 250ms 错峰,每张 1.0s 慢镜),卡内 mono 数字 01/02/03/04 + 关键词;持续微动:每张卡暖橙边线慢光晕呼吸(错峰 400ms)+ 远景粒子永漂
|
||||
· 手段:filter: blur 反向 + opacity 慢 fade + transform scale 慢推 + filter: drop-shadow 多 instance 错峰呼吸
|
||||
· article 补:4 件事的关键词(来自 article 章节标题,简化)
|
||||
|
||||
口播节选:
|
||||
> 我刷到一张图,愣了三秒……今天讲清楚四件事。
|
||||
|
||||
---
|
||||
|
||||
## 2. why-strong — 强在哪(5 steps · ~80s)
|
||||
|
||||
- **step 1** (~6s) — hero"强在哪 · 四个方向" + 4 个 ghost 占位卡
|
||||
· 动画:hero 字符整体从 blur(20px) + opacity 0 慢速景深聚焦(1.5s ease-out)→ 下方暖橙长横线从中心向两侧慢延展(0.8s)→ 4 张 ghost 卡片同步从 blur 慢镜出现(保持 opacity 0.3 占位状态);持续微动:暖橙下划线 8s 周期慢光晕脉冲 + ghost 卡片暖暗边线慢闪
|
||||
· 手段:filter: blur 反向 + transform scaleX 慢延展 + opacity 阶梯填充 + filter: drop-shadow 永动呼吸
|
||||
· article 补:4 个方向各自的关键词(mono cue 标签,"01 X / 02 Y / 03 Z / 04 W")
|
||||
|
||||
- **step 2** (~16s) — 第 1/4 项填实 + 大图慢镜 takeover
|
||||
· 动画:卡片 1 从 ghost 状态慢速 mask 填实(0.8s 暖暗底色 + 边线慢光晕亮起)→ 中央 hero 大图从 blur(15px) 慢速景深聚焦(1.5s ease-out)→ mono cue 标签从暗角慢速 blur clear 入场(0.8s)→ 副标打字机逐字打出(每字 80ms);持续微动:暖橙 accent 高亮条永动呼吸 + 大图 ken burns 缓推(0.5% scale 12s 周期)+ scanline 慢移
|
||||
· 手段:filter: blur 反向 + clip-path 慢 reveal + JS typewriter + transform scale 永动 ken burns + linear-gradient scanline 永动
|
||||
· article 补:本项的具体表现(article §2 抽 1~2 个数据点 / 案例标签)—— 口播只说"它强在 X",画面挂"具体强到 N% / 跑赢 M / 测评分数 K"
|
||||
|
||||
- **step 3** (~16s) — 第 2/4 项填实 + 列表/演示
|
||||
· 动画:卡片 2 慢速 mask 填实(0.8s)→ mono cue 标签 blur clear 慢入场(0.8s)→ 4 行具体细则 typewriter 逐行打出(每行 0.8s 错峰 350ms)→ 每行末尾 mono 光标闪烁后追加暖橙对勾 SVG path stroke 慢绘制;持续微动:mono 光标永闪烁(800ms blink)+ scanline 慢移
|
||||
· 手段:JS typewriter + opacity blink 光标 + SVG path stroke-dashoffset 慢绘 + linear-gradient scanline overlay 永动
|
||||
· article 补:4 行细则的具体内容(来自 article §2 第 N 段子列表)—— 口播只说"指令遵循好",画面把 article 列出来的 4 个具体维度"主体放哪 / 背景怎么搭 / ..."逐行打出来
|
||||
|
||||
- **step 4** (~16s) — 第 3/4 项填实 + before/after 慢镜对照 + 永动 cross-fade
|
||||
- **step 5** (~16s) — 第 4/4 项填实 + 多参数预览 + redacted 注释
|
||||
|
||||
口播节选:
|
||||
> 实测下来强在四个方向 ……
|
||||
|
||||
---
|
||||
|
||||
> **观察**:每个 step 的画面都做到"口播说一件事,画面挂多件事"。比如
|
||||
> step 2 口播只是"第一项很强",画面同时呈现:本项关键词 / 大图实例 /
|
||||
> 具体数据点 / 副标补充 —— 这是双源原则的具象落地。
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
# Anchor: hook-chapter(钩子型开场)
|
||||
|
||||
> ⚠️ **这是结构示意,不是抄袭模板**。先走 [`../../CHAPTER-CRAFT.md`](../../CHAPTER-CRAFT.md)
|
||||
> Part 0 五问。本 anchor 给的是"钩子型开场的结构骨架"——你要保留它的
|
||||
> step 切分逻辑、字号关系、布局原则,**按本项目的主题 + 内容换动作
|
||||
> 选型**。倒过来照抄 = [`../../CHAPTER-CRAFT.md`](../../CHAPTER-CRAFT.md)
|
||||
> Part 5 第 8 条「整章只用一种入场动画」同质化反模式。
|
||||
|
||||
## 定位
|
||||
|
||||
视频开头最常用的章节类型:**抛 N 张可疑图 / 反例 / 截图 → 引出主题 →
|
||||
切大字 hero takeover**。
|
||||
|
||||
## 适用场景
|
||||
|
||||
- 悬念型开头:先甩 3~4 张让人怀疑 / 困惑的图,再揭示原因
|
||||
- "今天聊聊 X 的几个翻车现场":先看翻车,再切主题
|
||||
- 产品发布的"问题感"开场:先看痛点截图,再揭示新功能
|
||||
|
||||
## 假设的 outline.md 章节段(抽象)
|
||||
|
||||
```markdown
|
||||
## 2. hook — <章节标题>(6 steps)
|
||||
|
||||
- **step 1** (~4s) — N 张可疑图片占位(虚线 ghost 卡片)
|
||||
- **step 2** (~5s) — 第 1 张露出:<反例 1 描述>(独占视觉)
|
||||
- **step 3** (~5s) — 第 2 张露出:<反例 2 描述>(独占视觉)
|
||||
- **step 4** (~5s) — 第 3 张露出:<反例 3 描述>(独占视觉)
|
||||
- **step 5** (~4s) — 三张图同时缩入侧栏,中间出 <主题大字> takeover
|
||||
- **step 6** (~3s) — 切到下一句钩子(被 brush 划掉)
|
||||
```
|
||||
|
||||
## 关键节奏决策
|
||||
|
||||
| step | 节奏意图 | 视觉 |
|
||||
|---|---|---|
|
||||
| 1 | 抛悬念 —— N 张未知 | 虚线 ghost 卡片,1/3 屏一张 |
|
||||
| 2-N | **每张图独占视觉** —— 重点不是"凑数",是让观众盯着每张图想"这是真的吗" | 大图占据 ~70% 屏幕,旁边小字标注图源 |
|
||||
| N+1 | takeover —— 揭示主题 | 三张缩成左侧迷你卡,中间巨字 |
|
||||
| 末 | 钩子收束 | brush 划掉旧概念,引下一章 |
|
||||
|
||||
## 为什么 2-N 不能 stagger 同时上
|
||||
|
||||
口播会**逐个念出来** —— 必须 1 项 = 1 step([CHAPTER-CRAFT.md Part 0 原则 8](../../CHAPTER-CRAFT.md#8-多点内容必须逐个揭示绝不同时上))。
|
||||
同时 stagger 上 = 观众扫一眼看完,讲者还在念第一张 = PPT 直觉。
|
||||
|
||||
## 文件结构
|
||||
|
||||
```
|
||||
hook-chapter/
|
||||
├── README.md ← 本文件
|
||||
├── chapter.tsx ← 完整章节示例 —— 默认绑 newsroom 主题
|
||||
└── chapter.css
|
||||
```
|
||||
|
||||
## 关键手段(地板线)
|
||||
|
||||
| 维度 | 这个 anchor 怎么实现 |
|
||||
|---|---|
|
||||
| 素材 | `<img src="/hook/<asset>.png" />` 真截图 |
|
||||
| 字号 | hero = 144px serif (`var(--t-display-1)`) |
|
||||
| 主导动作 | brush-stroke + 印章砸下(newsroom 气质) |
|
||||
| 伴随动作 | accent 红条 scaleX + 副标 stagger 200ms |
|
||||
| 持续微动 | accent 红条光晕 `infinite` 呼吸;图片 ken burns 缓推 |
|
||||
| 卡片样式 | drop-shadow + 微旋转 1deg |
|
||||
| takeover | 三张图缩入 + hero 巨字爆出 + accent 红条贯穿 |
|
||||
|
||||
> **新写章节时**:抄结构和字号关系,按本章内容 + 本主题气质自由
|
||||
> 设计动画形式。**持续微动按需挂**,不强求 —— 详见
|
||||
> [`../../CHAPTER-CRAFT.md`](../../CHAPTER-CRAFT.md)「避免 AI 味」一节
|
||||
> 关于「每步都挂 ken burns / 持续闪烁」的反模式。
|
||||
|
||||
## 切到其它主题时
|
||||
|
||||
- `bauhaus-bold` → brush 划掉换 hard-cut 大色块;hero 字体换 Archivo Black
|
||||
- `terminal-green` → 三张图换"FILE_001/002/003"占位框;hero 用打字机
|
||||
- `chalk-garden` → 粉笔感虚线 + 慢速 wiggle 入场
|
||||
- `midnight-press` → blur clear 慢镜入场 + ken burns + scanline;
|
||||
takeover 改"主标 blur 锐化 + 暖橙光晕呼吸"
|
||||
|
||||
**结构(N+2 步、独占节奏、takeover、收束)保持不变。**
|
||||
|
||||
## 想看具象题材应用
|
||||
|
||||
- 科技测评 / 实测对比类视频用这个 anchor 开场长什么样 →
|
||||
[`../case-tech-review/`](../case-tech-review/)
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
/* ─────────────────────────────────────────────────────────────────
|
||||
* hook-chapter · 完整章节示例样式
|
||||
* 默认绑 newsroom 主题。所有视觉属性走语义 token,零硬编码。
|
||||
* ───────────────────────────────────────────────────────────────── */
|
||||
|
||||
.hk-scene {
|
||||
color: var(--text);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* ── kicker ── */
|
||||
.hk-kicker {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
margin-bottom: var(--space-6);
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--t-cue);
|
||||
color: var(--accent);
|
||||
letter-spacing: 0.12em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.hk-kicker-line {
|
||||
width: 64px;
|
||||
height: 2px;
|
||||
background: var(--accent);
|
||||
}
|
||||
|
||||
/* ── step 1 三 ghost ── */
|
||||
.hk-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: var(--space-5);
|
||||
align-items: center;
|
||||
}
|
||||
.hk-ghost {
|
||||
aspect-ratio: 16 / 9;
|
||||
border: var(--rule-w) dashed var(--rule);
|
||||
border-radius: var(--r-card);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
align-items: stretch;
|
||||
padding: var(--space-4);
|
||||
background: var(--surface);
|
||||
}
|
||||
.hk-ghost-num {
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--t-h2);
|
||||
color: var(--text-mute);
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
.hk-ghost-label {
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--t-cue);
|
||||
color: var(--text-faint);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.2em;
|
||||
align-self: end;
|
||||
}
|
||||
|
||||
/* ── step 2-4 单图独占 ── */
|
||||
.hk-solo-frame {
|
||||
width: 78%;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-4);
|
||||
}
|
||||
.hk-solo-img-wrap {
|
||||
position: relative;
|
||||
aspect-ratio: 16 / 9;
|
||||
border-radius: var(--r-card);
|
||||
overflow: hidden;
|
||||
box-shadow: var(--shadow-card);
|
||||
background: var(--surface-2);
|
||||
}
|
||||
.hk-solo-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
.hk-stamp {
|
||||
position: absolute;
|
||||
top: var(--space-4);
|
||||
right: var(--space-4);
|
||||
padding: var(--space-2) var(--space-3);
|
||||
border: 3px solid var(--accent);
|
||||
color: var(--accent);
|
||||
font-family: var(--font-display-en);
|
||||
font-size: var(--t-h3);
|
||||
font-weight: 900;
|
||||
letter-spacing: 0.1em;
|
||||
transform: rotate(-8deg);
|
||||
background: color-mix(in oklch, var(--surface) 60%, transparent);
|
||||
animation: hk-stamp-drop var(--dur-base) var(--ease-quart) backwards;
|
||||
animation-delay: 600ms;
|
||||
}
|
||||
@keyframes hk-stamp-drop {
|
||||
0% { transform: rotate(-8deg) scale(2.4); opacity: 0; }
|
||||
60% { transform: rotate(-8deg) scale(0.92); opacity: 1; }
|
||||
100% { transform: rotate(-8deg) scale(1); }
|
||||
}
|
||||
.hk-solo-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--t-cue);
|
||||
color: var(--text-2);
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.hk-solo-label { color: var(--accent); }
|
||||
.hk-solo-caption {
|
||||
font-family: var(--font-display-cn);
|
||||
font-size: var(--t-body);
|
||||
text-transform: none;
|
||||
letter-spacing: 0;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
/* ── step 5 takeover ── */
|
||||
.hk-takeover {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: var(--space-6);
|
||||
justify-content: center;
|
||||
}
|
||||
.hk-mini-row {
|
||||
display: flex;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
.hk-mini {
|
||||
width: 140px;
|
||||
aspect-ratio: 16 / 9;
|
||||
object-fit: cover;
|
||||
border-radius: calc(var(--r-card) * 0.5);
|
||||
box-shadow: var(--shadow-card);
|
||||
animation: hk-mini-shrink var(--dur-base) var(--ease-quart) backwards;
|
||||
}
|
||||
@keyframes hk-mini-shrink {
|
||||
from { transform: scale(2.4); opacity: 0; }
|
||||
to { transform: scale(1); opacity: 1; }
|
||||
}
|
||||
.hk-accent-bar {
|
||||
width: 60%;
|
||||
height: 4px;
|
||||
background: var(--accent);
|
||||
animation: hk-bar-grow 700ms var(--ease-quart) 250ms backwards;
|
||||
}
|
||||
@keyframes hk-bar-grow {
|
||||
from { transform: scaleX(0); }
|
||||
to { transform: scaleX(1); }
|
||||
}
|
||||
.hk-hero {
|
||||
margin: 0;
|
||||
font-family: var(--font-display-en);
|
||||
font-size: var(--t-display-1);
|
||||
letter-spacing: -0.025em;
|
||||
color: var(--text);
|
||||
line-height: 0.95;
|
||||
}
|
||||
|
||||
/* ── step 6 close + brush ── */
|
||||
.hk-close {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
.hk-quote-wrap {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
.hk-quote {
|
||||
margin: 0;
|
||||
font-family: var(--font-display-cn);
|
||||
font-size: var(--t-display-2);
|
||||
color: var(--text);
|
||||
}
|
||||
.hk-brush {
|
||||
position: absolute;
|
||||
left: -4%;
|
||||
right: -4%;
|
||||
top: 50%;
|
||||
height: 0.18em;
|
||||
background: var(--accent);
|
||||
transform-origin: left center;
|
||||
transform: scaleX(0) translateY(-50%);
|
||||
animation: hk-brush-strike 700ms var(--ease-expo) 500ms forwards;
|
||||
}
|
||||
@keyframes hk-brush-strike {
|
||||
to { transform: scaleX(1) translateY(-50%); }
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
// ⚠️ 这是 anchor 参考代码,不会被任何项目编译。
|
||||
// 抄到真实项目时(presentation/src/chapters/NN-hook/),
|
||||
// 把下面两个 import 改成:
|
||||
// import { MaskReveal } from "../../components/MaskReveal";
|
||||
// import type { ChapterStepProps } from "../../registry/types";
|
||||
import { MaskReveal } from "../../../templates/src/components/MaskReveal";
|
||||
import type { ChapterStepProps } from "../../../templates/src/registry/types";
|
||||
import "./chapter.css";
|
||||
|
||||
/**
|
||||
* hook-chapter · 完整章节示例
|
||||
* ─────────────────────────────────────────
|
||||
* 默认绑 newsroom 主题(serif + 报头红 + 印刷盖章 motion)。
|
||||
*
|
||||
* 关键手段:
|
||||
* - 真素材:<img src="/hook/{name}.png" /> 而不是 placeholder
|
||||
* - 字号狠对比:hero 用 --t-display-1(≥ 144px)+ 微微负字距
|
||||
* - 主导动作:mask reveal + 印章砸下(贴 newsroom 印刷气质)
|
||||
* - takeover:三张图缩入 + 巨字爆出 + accent 红条贯穿
|
||||
* - 收束:brush 划掉旧概念
|
||||
*
|
||||
* 切其它主题时按那个主题的气质自由换"印章砸下 / brush"等效动作,
|
||||
* 结构和字号节奏保持。
|
||||
*/
|
||||
export default function HookChapter({ step }: ChapterStepProps) {
|
||||
// step 1 — 三张 ghost(精修:加 kicker 引子 + accent 红条)
|
||||
if (step === 0) {
|
||||
return (
|
||||
<div className="hk-scene scene-pad">
|
||||
<div className="hk-kicker">
|
||||
<span className="hk-kicker-line" />
|
||||
<span className="hk-kicker-text">这几天</span>
|
||||
</div>
|
||||
<div className="hk-grid" key={step}>
|
||||
{["01", "02", "03"].map((i, idx) => (
|
||||
<MaskReveal show key={i} delay={idx * 200} duration={900}>
|
||||
<div className="hk-ghost">
|
||||
<span className="hk-ghost-num">{i}</span>
|
||||
<span className="hk-ghost-label">image</span>
|
||||
</div>
|
||||
</MaskReveal>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// step 2-4 — 每张图独占(真素材 + 角章 + 旁白)
|
||||
// ⚠️ 这是结构示例。具体反例 caption / src 应该来自 outline.md 本章
|
||||
// article 补字段(双源原则)—— 别照抄下面这些占位字符串。
|
||||
const reveals: Array<{ src: string; label: string; caption: string }> = [
|
||||
{
|
||||
src: "/hook/<asset-1>.png",
|
||||
label: "01 / 03",
|
||||
caption: "<反例 1 caption,来自 article §X>",
|
||||
},
|
||||
{
|
||||
src: "/hook/<asset-2>.png",
|
||||
label: "02 / 03",
|
||||
caption: "<反例 2 caption>",
|
||||
},
|
||||
{
|
||||
src: "/hook/<asset-3>.png",
|
||||
label: "03 / 03",
|
||||
caption: "<反例 3 caption>",
|
||||
},
|
||||
];
|
||||
if (step >= 1 && step <= 3) {
|
||||
const r = reveals[step - 1];
|
||||
return (
|
||||
<div className="hk-scene scene-pad" key={step}>
|
||||
<div className="hk-solo-frame">
|
||||
<MaskReveal show duration={1100}>
|
||||
<div className="hk-solo-img-wrap">
|
||||
<img className="hk-solo-img" src={r.src} alt={r.caption} />
|
||||
<div className="hk-stamp">FAKE?</div>
|
||||
</div>
|
||||
</MaskReveal>
|
||||
<MaskReveal show delay={400} duration={900}>
|
||||
<div className="hk-solo-meta">
|
||||
<span className="hk-solo-label">{r.label}</span>
|
||||
<span className="hk-solo-caption">{r.caption}</span>
|
||||
</div>
|
||||
</MaskReveal>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// step 5 — takeover:三张缩入 + 巨字爆出 + accent 红条
|
||||
if (step === 4) {
|
||||
return (
|
||||
<div className="hk-scene scene-pad hk-takeover" key={step}>
|
||||
<div className="hk-mini-row">
|
||||
{reveals.map((r, idx) => (
|
||||
<img
|
||||
key={r.src}
|
||||
className="hk-mini"
|
||||
src={r.src}
|
||||
alt={r.caption}
|
||||
style={{ animationDelay: `${idx * 80}ms` }}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<span className="hk-accent-bar" />
|
||||
<h1 className="hk-hero">
|
||||
<MaskReveal show duration={1100}>
|
||||
{/* hero 文案来自 outline 本章 step 5;这里只是占位 */}
|
||||
<主题大字 takeover>
|
||||
</MaskReveal>
|
||||
</h1>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// step 6 — 钩子收束:brush 划掉
|
||||
return (
|
||||
<div className="hk-scene scene-pad hk-close" key={step}>
|
||||
<div className="hk-quote-wrap">
|
||||
<h2 className="hk-quote"><下一句钩子></h2>
|
||||
<span className="hk-brush" aria-hidden />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
# Anchor: list-reveal(列举型逐个揭示)
|
||||
|
||||
> ⚠️ **这是结构示意,不是抄袭模板**。先走 [`../../CHAPTER-CRAFT.md`](../../CHAPTER-CRAFT.md)
|
||||
> Part 0 五问。本 anchor 给的是"列举型章节的结构骨架"(单网格 N 槽位 +
|
||||
> 每 step 只填一个槽位 + 位置不重排)——保留这个**结构**,**按本项目的
|
||||
> 主题 + 内容换动作选型**。倒过来照抄 = [`../../CHAPTER-CRAFT.md`](../../CHAPTER-CRAFT.md)
|
||||
> Part 5 第 8 条「整章只用一种入场动画」同质化反模式。
|
||||
|
||||
## 定位
|
||||
|
||||
口播说"三件事 / 四个原因 / N 个特性"时,**每项 1 step 逐个揭示**。
|
||||
视频中段最常用的章节类型,**最容易翻车成 PPT** —— 这是为什么需要 anchor。
|
||||
|
||||
## 适用场景
|
||||
|
||||
- "<主体> 强在哪 → 三件事"
|
||||
- "选购 <X> → 四个角度"
|
||||
- "为什么我喜欢 <X> → 五个理由"
|
||||
- 任何"主题 + N 个并列子项"的结构
|
||||
|
||||
## 假设的 outline.md 章节段(抽象)
|
||||
|
||||
```markdown
|
||||
## 4. <chapter-id> — <主题 N 件事>(N+1 steps)
|
||||
|
||||
- **step 1** (~3s) — masthead 引子"<N 件事>"
|
||||
- **step 2** (~6s) — 第 1 件:<标题> + <article 抽来的细节>
|
||||
- **step 3** (~6s) — 第 2 件:<标题>
|
||||
- ...
|
||||
- **step N+1** (~6s) — 第 N 件:<标题>
|
||||
```
|
||||
|
||||
## 关键节奏决策
|
||||
|
||||
| step | 视觉布局 |
|
||||
|---|---|
|
||||
| 1 | 中心引子大字 + 序号 01/02/.../N 占位(**不显示内容**,纯占位) |
|
||||
| 2 | "01" 槽位填充:标题 + 简短说明 + accent 编号;其余仍是 ghost |
|
||||
| 3 | "02" 填充;01 已激活变次级;其余仍 ghost |
|
||||
| ... | 当前槽位填充;之前的激活降级;之后的 ghost |
|
||||
|
||||
## CHAPTER-CRAFT.md Part 0 原则 8 的核心实现
|
||||
|
||||
> "布局不重排,只是单元格内容变化"
|
||||
|
||||
整个章节只有**一个网格布局**,N 个槽位的 React 节点位置完全不变。
|
||||
变的只是每个槽位的内容状态(ghost / active / past)。这样:
|
||||
- 单元格不会重排 → 视觉稳
|
||||
- 每点一次只有"一个槽位变化" → 观众视线明确锁定新揭示的项
|
||||
|
||||
**反模式**:每点一次重新渲染整个布局 → 已揭示的项也跟着抖动 / 重新
|
||||
入场 → 观众不知道该看哪。
|
||||
|
||||
## 文件结构
|
||||
|
||||
```
|
||||
list-reveal/
|
||||
├── README.md
|
||||
├── chapter.tsx ← 完整章节示例 —— 默认绑 newsroom 主题
|
||||
└── chapter.css
|
||||
```
|
||||
|
||||
## 关键手段(地板线)
|
||||
|
||||
| 维度 | 这个 anchor 怎么实现 |
|
||||
|---|---|
|
||||
| 字号 | 标题 64px / 巨号 144px serif |
|
||||
| 槽位状态 | dashed → 巨号红色高亮 → 灰化数字(**位置不重排**) |
|
||||
| 序号 | hero-num 字体(衬线大数字) |
|
||||
| 主导动作 | mask reveal(标题)+ 数字砸下(accent 红) |
|
||||
| 伴随动作 | 副标 stagger 200ms + accent 横线 scaleX |
|
||||
| 持续微动 | active 槽位的数字 accent 光晕 `infinite` 呼吸 |
|
||||
| 引子 | masthead 双线规则 + serif 大字 |
|
||||
|
||||
> **新写章节时**:抄结构(单网格 N 槽位、每 step 只填一个槽位、位置
|
||||
> 不重排),按本章内容 + 本主题气质自由设计主导动作的形式。
|
||||
|
||||
## 切到其它主题时
|
||||
|
||||
- `bauhaus-bold` → 序号换 Archivo Black + 大色块;用 hard-cut 砸下
|
||||
- `terminal-green` → 序号 `[01]` `[02]` `[03]` 风格;打字机入场
|
||||
- `chalk-garden` → 粉笔下划线手绘 + wiggle 入场
|
||||
- `midnight-press` → 数字 blur clear 慢锐化 + 暖橙光晕慢呼吸
|
||||
|
||||
**结构不变**:N+1 step、单网格 N 槽位、每 step 只填一个槽位。
|
||||
|
||||
## 想看具象题材应用
|
||||
|
||||
- 科技测评 / 实测对比类视频用这个 anchor 长什么样 →
|
||||
[`../case-tech-review/outline-snippet.md`](../case-tech-review/outline-snippet.md)
|
||||
里 `## 2. why-strong` 章节
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
/* ─────────────────────────────────────────────────────────────────
|
||||
* list-reveal · 完整章节示例样式
|
||||
* 默认绑 newsroom 主题。零硬编码视觉属性。
|
||||
* ───────────────────────────────────────────────────────────────── */
|
||||
|
||||
.lr-scene {
|
||||
color: var(--text);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-6);
|
||||
}
|
||||
|
||||
/* ── masthead ── */
|
||||
.lr-masthead {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
.lr-rule {
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: var(--rule);
|
||||
}
|
||||
.lr-kicker {
|
||||
font-family: var(--font-mono);
|
||||
font-size: var(--t-cue);
|
||||
letter-spacing: 0.18em;
|
||||
color: var(--accent);
|
||||
text-transform: uppercase;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* ── intro step ── */
|
||||
.lr-intro {
|
||||
justify-content: center;
|
||||
align-items: stretch;
|
||||
gap: var(--space-5);
|
||||
}
|
||||
.lr-intro-h {
|
||||
margin: 0;
|
||||
font-family: var(--font-display-cn);
|
||||
font-size: var(--t-display-2);
|
||||
text-align: center;
|
||||
letter-spacing: -0.01em;
|
||||
}
|
||||
.lr-em { color: var(--accent); }
|
||||
.lr-intro-sub {
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--t-h3);
|
||||
color: var(--text-2);
|
||||
text-align: center;
|
||||
margin-bottom: var(--space-5);
|
||||
}
|
||||
|
||||
/* ── grid (3 slots, fixed layout) ── */
|
||||
.lr-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: var(--space-5);
|
||||
}
|
||||
|
||||
/* ── slot base ── */
|
||||
.lr-slot {
|
||||
border-radius: var(--r-card);
|
||||
padding: var(--space-5);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
min-height: 360px;
|
||||
transition: opacity var(--dur-base) var(--ease-quart),
|
||||
filter var(--dur-base) var(--ease-quart),
|
||||
border-color var(--dur-base) var(--ease-quart);
|
||||
}
|
||||
|
||||
/* ghost: 虚线边 + 大灰序号 */
|
||||
.lr-slot-ghost {
|
||||
border: var(--rule-w) dashed var(--rule);
|
||||
background: transparent;
|
||||
opacity: 0.55;
|
||||
}
|
||||
.lr-slot-ghost .lr-slot-num {
|
||||
color: var(--text-faint);
|
||||
}
|
||||
|
||||
/* active: 红框 + 实心面板 + 巨号砸下 */
|
||||
.lr-slot-active {
|
||||
border: var(--rule-w) solid var(--accent);
|
||||
background: var(--surface);
|
||||
box-shadow: var(--shadow-card);
|
||||
}
|
||||
.lr-slot-active .lr-slot-num {
|
||||
color: var(--accent);
|
||||
animation: lr-num-drop var(--dur-base) var(--ease-quart) backwards;
|
||||
}
|
||||
@keyframes lr-num-drop {
|
||||
0% { transform: translateY(-40%) scale(1.6); opacity: 0; }
|
||||
60% { transform: translateY(0) scale(0.94); opacity: 1; }
|
||||
100% { transform: translateY(0) scale(1); }
|
||||
}
|
||||
|
||||
/* past: 灰化 */
|
||||
.lr-slot-past {
|
||||
border: var(--rule-w) solid var(--rule);
|
||||
background: transparent;
|
||||
opacity: 0.7;
|
||||
filter: grayscale(0.4);
|
||||
}
|
||||
.lr-slot-past .lr-slot-num {
|
||||
color: var(--text-mute);
|
||||
}
|
||||
|
||||
/* ── slot internals ── */
|
||||
.lr-slot-num {
|
||||
font-family: var(--hero-num-font, var(--font-display-en));
|
||||
font-style: var(--hero-num-style, normal);
|
||||
font-weight: var(--hero-num-weight, 700);
|
||||
font-size: calc(var(--t-display-2) * 0.85);
|
||||
letter-spacing: var(--hero-num-track, -0.02em);
|
||||
line-height: 1;
|
||||
}
|
||||
.lr-slot-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
.lr-slot-title {
|
||||
font-family: var(--font-display-cn);
|
||||
font-size: var(--t-h2);
|
||||
letter-spacing: -0.005em;
|
||||
}
|
||||
.lr-slot-body {
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--t-body);
|
||||
color: var(--text-2);
|
||||
line-height: 1.6;
|
||||
max-width: 22ch;
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
// ⚠️ 这是 anchor 参考代码,不会被任何项目编译。
|
||||
// 抄到真实项目时(presentation/src/chapters/NN-list/),
|
||||
// 把下面两个 import 改成:
|
||||
// import { MaskReveal } from "../../components/MaskReveal";
|
||||
// import type { ChapterStepProps } from "../../registry/types";
|
||||
import { MaskReveal } from "../../../templates/src/components/MaskReveal";
|
||||
import type { ChapterStepProps } from "../../../templates/src/registry/types";
|
||||
import "./chapter.css";
|
||||
|
||||
/**
|
||||
* list-reveal · 完整章节示例
|
||||
* ─────────────────────────────────────────
|
||||
* 默认绑 newsroom 主题。
|
||||
*
|
||||
* 关键手段:
|
||||
* - 槽位用 hero-num(serif 巨号)替代普通文字编号
|
||||
* - 引子用 masthead 双线规则 + serif 大字
|
||||
* - 槽位状态切换有专属动画:
|
||||
* ghost → active:mask reveal 标题 + 数字砸下(accent 红)
|
||||
* active → past :accent 灰化(filter)
|
||||
* - 关键:所有槽位的 React 节点位置不重排,只切换 className
|
||||
*/
|
||||
const ITEMS = [
|
||||
{ num: "01", title: "文字渲染", body: "图里的文字也能正确写出来" },
|
||||
{ num: "02", title: "指令遵循", body: "可以给到非常具体的要求" },
|
||||
{ num: "03", title: "照片真实感", body: "光影 / 材质 / 人物接近真实" },
|
||||
];
|
||||
|
||||
export default function ListRevealChapter({ step }: ChapterStepProps) {
|
||||
// step 1 — 引子
|
||||
if (step === 0) {
|
||||
return (
|
||||
<div className="lr-scene scene-pad lr-intro">
|
||||
<header className="lr-masthead">
|
||||
<span className="lr-rule" />
|
||||
<span className="lr-kicker">第一部分</span>
|
||||
<span className="lr-rule" />
|
||||
</header>
|
||||
<MaskReveal show duration={1100}>
|
||||
<h1 className="lr-intro-h">
|
||||
强在<span className="lr-em">哪</span>
|
||||
</h1>
|
||||
</MaskReveal>
|
||||
<MaskReveal show delay={400} duration={900}>
|
||||
<div className="lr-intro-sub">三件事 —— 一个个看</div>
|
||||
</MaskReveal>
|
||||
|
||||
<div className="lr-grid">
|
||||
{ITEMS.map((it) => (
|
||||
<Slot key={it.num} state="ghost" item={it} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const activeIdx = step - 1;
|
||||
return (
|
||||
<div className="lr-scene scene-pad">
|
||||
<header className="lr-masthead">
|
||||
<span className="lr-rule" />
|
||||
<span className="lr-kicker">第一部分 · 强在哪</span>
|
||||
<span className="lr-rule" />
|
||||
</header>
|
||||
|
||||
<div className="lr-grid">
|
||||
{ITEMS.map((it, i) => {
|
||||
const state =
|
||||
i < activeIdx ? "past" : i === activeIdx ? "active" : "ghost";
|
||||
return <Slot key={it.num} state={state} item={it} />;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function Slot({
|
||||
state,
|
||||
item,
|
||||
}: {
|
||||
state: "ghost" | "active" | "past";
|
||||
item: { num: string; title: string; body: string };
|
||||
}) {
|
||||
return (
|
||||
<div className={`lr-slot lr-slot-${state}`}>
|
||||
<div className="lr-slot-num">{item.num}</div>
|
||||
<div className="lr-slot-content">
|
||||
{state !== "ghost" && (
|
||||
<>
|
||||
<MaskReveal show duration={900} key={`${item.num}-title`}>
|
||||
<div className="lr-slot-title">{item.title}</div>
|
||||
</MaskReveal>
|
||||
{state === "active" && (
|
||||
<MaskReveal show delay={350} duration={900}>
|
||||
<div className="lr-slot-body">{item.body}</div>
|
||||
</MaskReveal>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user