dsh-creation-mode 3.0.0 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client.js +213 -1282
- package/data.js +34 -2
- package/flow.js +82 -157
- package/index.js +115 -5
- package/package.json +5 -2
- package/references/audio.md +167 -0
- package/references/camera.md +168 -0
- package/references/color.md +272 -0
- package/references/copy.md +121 -0
- package/references/data-viz.md +189 -0
- package/references/effects.md +236 -0
- package/references/motion.md +322 -0
- package/references/particles.md +166 -0
- package/references/sequencing.md +143 -0
- package/references/text-animation.md +213 -0
- package/references/timbre.md +182 -0
- package/references/transitions.md +231 -0
- package/references/typography.md +276 -0
- package/tools.js +227 -33
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# 文字动画
|
|
2
|
+
|
|
3
|
+
字是最贵的画面元素:它承载信息,也最容易做砸。
|
|
4
|
+
|
|
5
|
+
这一份讲:进场、出场、强调、跟随,以及什么时候**不要**动。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 一、先问一句:这个字需要动吗
|
|
10
|
+
|
|
11
|
+
| 情况 | 动不动 |
|
|
12
|
+
|---|---|
|
|
13
|
+
| 主标题首次出现 | 动(这是全片的「定调」) |
|
|
14
|
+
| 副标题补充说明 | 轻动(淡入即可) |
|
|
15
|
+
| 正文 / 字幕 | **不动**(动了读者跟不上) |
|
|
16
|
+
| 数据 / 数字 | 动(滚动到目标值) |
|
|
17
|
+
| 重复出现的界面文字 | 不动 |
|
|
18
|
+
|
|
19
|
+
一条片子里,需要「炫技」的文字动画最多两处。其余全部用淡入 + 位移。
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## 二、进场
|
|
24
|
+
|
|
25
|
+
### 2.1 淡入 + 上浮(最稳)
|
|
26
|
+
|
|
27
|
+
```js
|
|
28
|
+
const u = easeOutCubic(tween(t, start, 0.45));
|
|
29
|
+
ctx.globalAlpha = u;
|
|
30
|
+
ctx.fillText(text, x, y + 24 * (1 - u)); // 从下方 24px 浮上来
|
|
31
|
+
ctx.globalAlpha = 1;
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
位移量 20–30px。超过 60px 会像「掉下来」,少于 10px 看不出在动。
|
|
35
|
+
|
|
36
|
+
### 2.2 遮罩揭示
|
|
37
|
+
|
|
38
|
+
文字被「擦」出来,比淡入有力,适合主标题:
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
ctx.save();
|
|
42
|
+
ctx.beginPath();
|
|
43
|
+
ctx.rect(0, y - fontSize, W, fontSize * 1.35 * u);
|
|
44
|
+
ctx.clip();
|
|
45
|
+
ctx.fillText(text, x, y);
|
|
46
|
+
ctx.restore();
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
方向:从下往上(最常见)、从左往右(像被写出来)。
|
|
50
|
+
|
|
51
|
+
### 2.3 逐字进场
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
function drawChars(t, text, x, y, start, perChar = 0.03, dur = 0.35) {
|
|
55
|
+
let cursor = x;
|
|
56
|
+
for (let i = 0; i < text.length; i++) {
|
|
57
|
+
const u = tween(t, start + i * perChar, dur);
|
|
58
|
+
const w = ctx.measureText(text[i]).width;
|
|
59
|
+
if (u > 0) {
|
|
60
|
+
ctx.globalAlpha = easeOutCubic(u);
|
|
61
|
+
ctx.fillText(text[i], cursor, y + 14 * (1 - easeOutBack(u)));
|
|
62
|
+
ctx.globalAlpha = 1;
|
|
63
|
+
}
|
|
64
|
+
cursor += w;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
参数纪律:
|
|
70
|
+
|
|
71
|
+
| 参数 | 范围 | 超了会怎样 |
|
|
72
|
+
|---|---|---|
|
|
73
|
+
| 每字延迟 | 20–40ms | 60ms 以上变成「一个一个蹦」 |
|
|
74
|
+
| 单字时长 | 0.3–0.4s | 太长整句拖沓 |
|
|
75
|
+
| 位移 | ≤ 字高 30% | 太大像掉字 |
|
|
76
|
+
|
|
77
|
+
### 2.4 缩放落定
|
|
78
|
+
|
|
79
|
+
从 1.15 倍缩到 1.0,配合淡入,像「砸」在屏幕上:
|
|
80
|
+
|
|
81
|
+
```js
|
|
82
|
+
const u = easeOutQuint(tween(t, start, 0.4));
|
|
83
|
+
ctx.translate(cx, cy);
|
|
84
|
+
ctx.scale(1 + 0.15 * (1 - u), 1 + 0.15 * (1 - u));
|
|
85
|
+
ctx.translate(-cx, -cy);
|
|
86
|
+
ctx.globalAlpha = u;
|
|
87
|
+
ctx.fillText(text, x, y);
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 三、出场
|
|
93
|
+
|
|
94
|
+
出场比进场更需要克制。观众已经读完了,快走就行。
|
|
95
|
+
|
|
96
|
+
| 方式 | 时长 | 说明 |
|
|
97
|
+
|---|---|---|
|
|
98
|
+
| 淡出 | 0.2–0.3s | 最稳 |
|
|
99
|
+
| 上移淡出 | 0.3s | 有「翻页」感 |
|
|
100
|
+
| 缩小淡出 | 0.25s | 有「退后」感 |
|
|
101
|
+
| 硬切 | 0 帧 | 卡点上用,最干净 |
|
|
102
|
+
|
|
103
|
+
**不要**做出场动画比进场还花哨——那是本末倒置。
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 四、强调
|
|
108
|
+
|
|
109
|
+
### 4.1 高亮扫过
|
|
110
|
+
|
|
111
|
+
一条色块从左扫到右,字变亮:
|
|
112
|
+
|
|
113
|
+
```js
|
|
114
|
+
const u = easeInOutCubic(tween(t, start, 0.5));
|
|
115
|
+
ctx.save();
|
|
116
|
+
ctx.beginPath();
|
|
117
|
+
ctx.rect(x - 8, y - fontSize * 1.1, (w + 16) * u, fontSize * 1.35);
|
|
118
|
+
ctx.clip();
|
|
119
|
+
ctx.fillStyle = accent;
|
|
120
|
+
ctx.fillRect(x - 8, y - fontSize * 1.1, w + 16, fontSize * 1.35);
|
|
121
|
+
ctx.restore();
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### 4.2 下划线生长
|
|
125
|
+
|
|
126
|
+
```js
|
|
127
|
+
const u = easeOutCubic(tween(t, start, 0.4));
|
|
128
|
+
ctx.fillRect(x, y + 10, w * u, 4);
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### 4.3 圈注 / 手绘感
|
|
132
|
+
|
|
133
|
+
```js
|
|
134
|
+
// 椭圆逐段画出
|
|
135
|
+
const u = easeOutCubic(tween(t, start, 0.6));
|
|
136
|
+
ctx.strokeStyle = accent;
|
|
137
|
+
ctx.lineWidth = 3;
|
|
138
|
+
ctx.beginPath();
|
|
139
|
+
ctx.ellipse(cx, cy, rx, ry, -0.1, -Math.PI / 2, -Math.PI / 2 + Math.PI * 2 * u);
|
|
140
|
+
ctx.stroke();
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
手绘感靠两点:轻微的不规则(用固定种子扰动半径)+ 端点圆角。
|
|
144
|
+
|
|
145
|
+
### 4.4 抖动强调
|
|
146
|
+
|
|
147
|
+
字被「震」一下,适合冲击性内容:
|
|
148
|
+
|
|
149
|
+
```js
|
|
150
|
+
const shake = 6 * Math.sin(t * 60) * Math.exp(-(t - start) * 8);
|
|
151
|
+
ctx.fillText(text, x + shake, y);
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
幅度 4–8px,衰减要快(0.3s 内归零)。
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## 五、数字
|
|
159
|
+
|
|
160
|
+
### 5.1 滚动
|
|
161
|
+
|
|
162
|
+
```js
|
|
163
|
+
const u = easeOutQuint(tween(t, start, 0.9));
|
|
164
|
+
const shown = target * u;
|
|
165
|
+
ctx.fillText(format(shown), x, y);
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### 5.2 单位与符号
|
|
169
|
+
|
|
170
|
+
单位、百分号、正负号**在滚动开始前就固定**,不要最后才出现——那会让数字宽度
|
|
171
|
+
跳动,眼睛跟不上。
|
|
172
|
+
|
|
173
|
+
### 5.3 等宽
|
|
174
|
+
|
|
175
|
+
数字用等宽字体,否则 1 和 8 的宽度差会让数字左右抖:
|
|
176
|
+
|
|
177
|
+
```js
|
|
178
|
+
ctx.font = '700 96px ui-monospace, "SF Mono", Menlo, monospace';
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## 六、多语言与混排
|
|
184
|
+
|
|
185
|
+
- 中英混排时,英文用西文字体,中文用中文字体,`font-family` 里中文放后面兜底
|
|
186
|
+
- 日文假名和中文汉字宽度接近,可以共用一套
|
|
187
|
+
- 阿拉伯语 / 希伯来语是 RTL,Canvas 需要 `ctx.direction = 'rtl'`
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## 七、常见错误
|
|
192
|
+
|
|
193
|
+
| 错误 | 后果 |
|
|
194
|
+
|---|---|
|
|
195
|
+
| 每个字都弹跳 | 廉价、拖沓 |
|
|
196
|
+
| 字幕做动画 | 读不完 |
|
|
197
|
+
| 进场 1.2s | 观众等得不耐烦 |
|
|
198
|
+
| 出场比进场花哨 | 本末倒置 |
|
|
199
|
+
| 数字宽度跳动 | 眼睛跟不上 |
|
|
200
|
+
| 逐字延迟 80ms | 变成「一个一个蹦」 |
|
|
201
|
+
| 强调色用三种 | 没有重点 |
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## 八、自检清单
|
|
206
|
+
|
|
207
|
+
- [ ] 需要炫技的文字动画 ≤ 2 处?
|
|
208
|
+
- [ ] 字幕没有做动画?
|
|
209
|
+
- [ ] 逐字延迟 20–40ms?
|
|
210
|
+
- [ ] 进场位移 ≤ 30px(或字高 30%)?
|
|
211
|
+
- [ ] 最后一句留了 6 帧静止?
|
|
212
|
+
- [ ] 数字用等宽字体,单位提前固定?
|
|
213
|
+
- [ ] 出场时长 ≤ 进场时长?
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# 音色配方(28 条,已逐个试听校准)
|
|
2
|
+
|
|
3
|
+
「听感」那栏是它应该听起来像什么。如果你写出来的东西和这栏不符,说明配方用错了,重写。
|
|
4
|
+
|
|
5
|
+
**禁令:任何音色都不许做低频扫频(把正弦从一两百赫兹一路滑到三四十赫兹)。**
|
|
6
|
+
这种声音听感像放屁,不是鼓。底鼓要「咚」就给一个固定频率的低正弦加一层鼓皮噪声,
|
|
7
|
+
让包络去做冲击,不要让频率去滑。自写配乐同样适用,没有例外。
|
|
8
|
+
|
|
9
|
+
| 音色 | 听感 | 做法 |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| 底鼓 kick | 低频「咚」,一击即止,音高不滑动 | 噪声过 220Hz 带通衰 0.05s(鼓皮),叠 62Hz 正弦**固定频率**衰 0.13s |
|
|
12
|
+
| 军鼓 snare | 中频「沙」加一点鼓皮音 | 2200Hz 带通噪声衰 0.07s,叠 190Hz 与 330Hz 正弦短衰减 |
|
|
13
|
+
| 闭合踩镲 | 极短极亮,只有「呲」一声 | 7000Hz 高通噪声,衰减 0.012s |
|
|
14
|
+
| 开镲 open hat | 同样高频,但尾巴拖长 | 同上,衰减 0.18s |
|
|
15
|
+
| 通鼓 tom | 有明确音高的鼓,比军鼓低沉 | 正弦 170→105Hz,衰减 0.22s |
|
|
16
|
+
| 吊镲 crash | 宽频金属噪声,尾巴很长 | 4200Hz 高通噪声乘非谐波调制,衰减 0.42s |
|
|
17
|
+
| 古筝拨弦 | 明亮拨弦,「啪」的起音加金属余韵 | Karplus-Strong,循环反馈 0.998,见示例一 |
|
|
18
|
+
| 清音吉他 | 同一类拨弦但更闷、衰更快 | Karplus-Strong 反馈 0.982,输出再过 1800Hz 低通 |
|
|
19
|
+
| 失真主音 | 沙哑持续,有攻击性 | 两路失谐锯齿叠加,tanh 硬削波,再过 900Hz 带通 |
|
|
20
|
+
| 竹笛 | 柔和持续音,带明显气声和轻微颤动 | 正弦加 0.4% 颤音,混 2400Hz 带通噪声 0.35,60ms 渐入 |
|
|
21
|
+
| 弦乐铺底 | 慢起音、宽而厚、无节奏 | 三路失谐锯齿加一个八度,1400Hz 低通,350ms 渐入 |
|
|
22
|
+
| 低频贝斯 sub | 极低频,音高明确但更像震动 | 55Hz 正弦加 110Hz 二次谐波 0.25 |
|
|
23
|
+
| 钟铃 bell | 金属非谐波,不是乐器音 | FM:载波 660Hz,调制比 3.51,调制指数从 6 指数衰到 0 |
|
|
24
|
+
| 马林巴 | 木头味,短促,有余音共鸣 | 523Hz 正弦衰 0.22,叠 4 倍频衰 0.06 与 9.2 倍频衰 0.02 |
|
|
25
|
+
| 上行扫频 riser | 亮度和音高一起往上冲 | 噪声过 400→7400Hz 扫频低通,叠 200→1100Hz 正弦 |
|
|
26
|
+
| 落重音 impact | 比底鼓更重更长,用在切点 | 52Hz 正弦**固定频率**衰 0.42,叠 2600Hz 高通噪声衰 0.3 与 240Hz 短瞬态 |
|
|
27
|
+
| 808 底鼓 | 长拖尾,有音高,但音高不滑动 | 62Hz 正弦**固定频率**,衰减 0.55s,轻微饱和,叠 4ms 噪声瞬态 |
|
|
28
|
+
| 酸性贝斯 303 | 锯齿过强共振,每个音「呜」一下 | 锯齿过 Q=11 状态变量滤波,截止 240→2840Hz 指数衰 0.075s,见示例二 |
|
|
29
|
+
| 超级锯齿 supersaw | 多层失谐,宽、亮、有合唱感 | 七路锯齿失谐 ±0.09 半音叠加,6000Hz 低通,见示例三 |
|
|
30
|
+
| 合成拨弦 pluck | 起音很亮然后瞬间变闷 | 锯齿过 Q=3 状态变量滤波,截止 400→5600Hz 衰 0.05s |
|
|
31
|
+
| 琶音 arp | 十六分音符快速跑动,每个音很短 | 方波每个十六分一个音,滤波截止瞬开瞬关 |
|
|
32
|
+
| FM 贝斯 | 低频但起音带金属「叮」 | 载波 55Hz,调制比 2,调制指数 420 指数衰 0.035s |
|
|
33
|
+
| 门限铺底 trance gate | 和弦被节奏切成一格一格 | 锯齿和弦过 2200Hz 低通,再乘八分音符硬门限 |
|
|
34
|
+
| 芯片方波 chip | 单薄、刺耳,红白机味 | 25% 占空比方波,短音符 |
|
|
35
|
+
| 抖动贝斯 wobble | 周期性「哇—哇—」 | 锯齿过 Q=9 状态变量滤波,截止被 4Hz LFO 在 200–2400Hz 扫动 |
|
|
36
|
+
| 下坠噪声 downlifter | 亮度往下掉,只掉亮度不掉音高 | 噪声过 7000→500Hz 反向扫频低通;**不要再叠低频正弦** |
|
|
37
|
+
| 侧链泵动 sidechain | 铺底每拍被压下去再弹回 | 铺底增益按 0.18 + 0.82 × (拍内相位 / 0.55) 起伏 |
|
|
38
|
+
| 四踩律动 four-on-floor | 每拍一记底鼓,二四拍军鼓 | 底鼓每拍,二四拍噪声军鼓,八分音符踩镲 |
|
|
39
|
+
|
|
40
|
+
#### 完整示例一:拨弦(古筝 / 吉他)
|
|
41
|
+
|
|
42
|
+
~~~js
|
|
43
|
+
// Karplus-Strong:噪声填一个周期长度的循环缓冲,每绕一圈丢一点高频。
|
|
44
|
+
// 这个衰减过程就是「被拨动的弦」和「一个 beep」的区别。
|
|
45
|
+
function pluckBuffer(freq, seconds, feedback) {
|
|
46
|
+
const n = Math.floor(seconds * ctx.sampleRate);
|
|
47
|
+
const buf = ctx.createBuffer(1, n, ctx.sampleRate);
|
|
48
|
+
const d = buf.getChannelData(0);
|
|
49
|
+
const period = Math.max(2, Math.round(ctx.sampleRate / freq));
|
|
50
|
+
const line = new Float32Array(period);
|
|
51
|
+
for (let i = 0; i < period; i++) line[i] = rand() * 2 - 1;
|
|
52
|
+
let prev = 0;
|
|
53
|
+
let idx = 0;
|
|
54
|
+
for (let i = 0; i < n; i++) {
|
|
55
|
+
const cur = line[idx];
|
|
56
|
+
line[idx] = 0.5 * (cur + prev) * feedback;
|
|
57
|
+
prev = cur;
|
|
58
|
+
idx = (idx + 1) % period;
|
|
59
|
+
d[i] = cur;
|
|
60
|
+
}
|
|
61
|
+
return buf;
|
|
62
|
+
}
|
|
63
|
+
// 古筝:feedback 0.998,余音长且带金属感
|
|
64
|
+
// 清音吉他:feedback 0.982,再接一个 1800Hz 低通,更闷更短
|
|
65
|
+
~~~
|
|
66
|
+
|
|
67
|
+
#### 完整示例二:酸性贝斯(滤波器扫动)
|
|
68
|
+
|
|
69
|
+
~~~js
|
|
70
|
+
// 状态变量滤波器,截止频率可以逐样本变化。
|
|
71
|
+
// biquad 做不到平滑扫频(系数按 buffer 固定),而扫频正是 acid 和 wobble 的灵魂。
|
|
72
|
+
function svf(input, cutoffAt, q) {
|
|
73
|
+
const out = new Float32Array(input.length);
|
|
74
|
+
const maxFc = ctx.sampleRate / 8; // 再高会不稳定
|
|
75
|
+
let low = 0;
|
|
76
|
+
let band = 0;
|
|
77
|
+
for (let i = 0; i < input.length; i++) {
|
|
78
|
+
const fc = Math.max(20, Math.min(maxFc, cutoffAt(i / ctx.sampleRate)));
|
|
79
|
+
const f = 2 * Math.sin((Math.PI * fc) / ctx.sampleRate);
|
|
80
|
+
const high = input[i] - low - (1 / q) * band;
|
|
81
|
+
band += f * high;
|
|
82
|
+
low += f * band;
|
|
83
|
+
out[i] = low;
|
|
84
|
+
}
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function sawBuffer(freq, seconds) {
|
|
89
|
+
const n = Math.floor(seconds * ctx.sampleRate);
|
|
90
|
+
const buf = ctx.createBuffer(1, n, ctx.sampleRate);
|
|
91
|
+
const d = buf.getChannelData(0);
|
|
92
|
+
for (let i = 0; i < n; i++) d[i] = 2 * (((freq * i) / ctx.sampleRate) % 1) - 1;
|
|
93
|
+
return buf;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 303:锯齿过 Q=11 的滤波器,截止从 2640Hz 在 75ms 内指数关掉
|
|
97
|
+
const raw = sawBuffer(55, 0.25).getChannelData(0);
|
|
98
|
+
const acid = svf(raw, (t) => 240 + 2400 * Math.exp(-t / 0.075), 11);
|
|
99
|
+
~~~
|
|
100
|
+
|
|
101
|
+
#### 完整示例三:超级锯齿(宽亮主音)
|
|
102
|
+
|
|
103
|
+
~~~js
|
|
104
|
+
// 七路失谐锯齿叠加,这是 trance / EDM 那种「宽」的来源。
|
|
105
|
+
const detunes = [-0.09, -0.06, -0.03, 0, 0.03, 0.06, 0.09];
|
|
106
|
+
const mix = new Float32Array(Math.floor(duration * ctx.sampleRate));
|
|
107
|
+
for (const d of detunes) {
|
|
108
|
+
const v = sawBuffer(261.6 * Math.pow(2, d / 12), duration).getChannelData(0);
|
|
109
|
+
for (let i = 0; i < mix.length; i++) mix[i] += v[i] / detunes.length;
|
|
110
|
+
}
|
|
111
|
+
// 再接 6000Hz 低通削掉刺耳高频
|
|
112
|
+
~~~
|
|
113
|
+
|
|
114
|
+
上面的示例只演示关键部分:master、sawBuffer 这些辅助函数要在你自己的代码里完整定义。
|
|
115
|
+
|
|
116
|
+
写完用 renderAt 自检首帧、中段、尾帧:不是黑屏、文字不溢出、不同时间画面确实在动。
|
|
117
|
+
把"未走分镜确认"之类的提示当回事——那是流程被跳过的信号。
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## 编曲:从音色到一条完整的配乐
|
|
122
|
+
|
|
123
|
+
有了音色还不够,还得排成一条有起伏的配乐。这一段讲层次、密度和收尾。
|
|
124
|
+
|
|
125
|
+
### 层次(同一时刻最多 4 层)
|
|
126
|
+
|
|
127
|
+
| 层 | 作用 | 音量 | 例子 |
|
|
128
|
+
|---|---|---|---|
|
|
129
|
+
| 低频 | 地基 | −12dB | 贝斯根音、底鼓 |
|
|
130
|
+
| 中频 | 主体 | −9dB | 和弦、主旋律 |
|
|
131
|
+
| 高频 | 空气感 | −18dB | 踩镲、铃声、噪声铺底 |
|
|
132
|
+
| 点状 | 强调 | −6dB | 重击、转场音效 |
|
|
133
|
+
|
|
134
|
+
层次超过 4 层会糊成一团。**同一时刻不要有两个乐器抢同一个频段**:
|
|
135
|
+
电钢琴和吉他都在 200–800Hz,同时弹就打架,错开八度或错开时间。
|
|
136
|
+
|
|
137
|
+
### 密度曲线
|
|
138
|
+
|
|
139
|
+
配乐的密度必须跟着画面走:
|
|
140
|
+
|
|
141
|
+
| 段落 | 密度 | 做法 |
|
|
142
|
+
|---|---|---|
|
|
143
|
+
| 开场 | 低 | 只有铺底 + 一两个点缀 |
|
|
144
|
+
| 铺垫 | 中 | 加节奏,鼓组进来 |
|
|
145
|
+
| 高潮 | 高 | 全编制,重音密集 |
|
|
146
|
+
| 收尾 | 低 | 撤掉鼓,只留和弦散开 |
|
|
147
|
+
|
|
148
|
+
全程一个密度,观众听 20 秒就走神了。
|
|
149
|
+
|
|
150
|
+
### 卡点处的三种处理
|
|
151
|
+
|
|
152
|
+
| 处理 | 听感 | 什么时候用 |
|
|
153
|
+
|---|---|---|
|
|
154
|
+
| 加一层重击 | 有力 | 普通卡点 |
|
|
155
|
+
| 撤掉所有声音 0.15s | 屏息 | 高潮前 |
|
|
156
|
+
| 只留高频 + 一记低频 | 空间感 | 转场 |
|
|
157
|
+
|
|
158
|
+
**不要每个卡点都用同一种处理。** 连续三次同样的重击,第四次就没感觉了。
|
|
159
|
+
|
|
160
|
+
### 收尾
|
|
161
|
+
|
|
162
|
+
最后 0.5s 做三件事:
|
|
163
|
+
|
|
164
|
+
1. 鼓组停
|
|
165
|
+
2. 和弦延长(不要断)
|
|
166
|
+
3. 整体音量线性衰减到 0
|
|
167
|
+
|
|
168
|
+
```js
|
|
169
|
+
master.gain.setValueAtTime(0.9, t0 + duration - 0.5);
|
|
170
|
+
master.gain.linearRampToValueAtTime(0, t0 + duration);
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
硬切会「啪」一声。
|
|
174
|
+
|
|
175
|
+
### 自检
|
|
176
|
+
|
|
177
|
+
- [ ] 同一时刻不超过 4 层?
|
|
178
|
+
- [ ] 没有两个乐器抢同一频段?
|
|
179
|
+
- [ ] 密度有起伏(开场低、高潮高、收尾低)?
|
|
180
|
+
- [ ] 卡点处理有变化(不是每次都同一招)?
|
|
181
|
+
- [ ] 结尾有 0.5s 衰减?
|
|
182
|
+
- [ ] 全曲峰值 ≤ −1dBFS?
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# 转场
|
|
2
|
+
|
|
3
|
+
镜头之间怎么接,决定了片子是「一段一段」还是「一条」。这一份讲:接法有哪些、
|
|
4
|
+
时长怎么定、时间轴怎么算、转场时画面该守什么纪律。
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 一、三种时间关系
|
|
9
|
+
|
|
10
|
+
先把概念分清。两个镜头 A(0–3s)和 B(3–6s),接法不同,总时长不同:
|
|
11
|
+
|
|
12
|
+
| 接法 | 时间关系 | 总时长 |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| 硬切 | B 从 A 结束的那一帧开始 | 6s(不变) |
|
|
15
|
+
| 叠化 | A 与 B 重叠 d 秒 | 6 − d |
|
|
16
|
+
| 推/擦除 | B 在同一时刻覆盖 A | 6s(不变) |
|
|
17
|
+
|
|
18
|
+
**叠化会吃掉时长**,这是最容易出错的地方。分镜写的是 0–3、3–6,实际叠化 0.4s 后
|
|
19
|
+
总长变成 5.6s,后面所有卡点、配乐小节线全部要跟着挪。
|
|
20
|
+
|
|
21
|
+
要么在分镜阶段就预留重叠(A: 0–3.4,B: 3.0–6),要么干脆用硬切。
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 二、硬切
|
|
26
|
+
|
|
27
|
+
0 帧转场,最干净、最有力。快节奏片子应该以硬切为主。
|
|
28
|
+
|
|
29
|
+
### 2.1 对齐
|
|
30
|
+
|
|
31
|
+
切点两侧的构图重心尽量落在同一位置,切过去才不跳。
|
|
32
|
+
|
|
33
|
+
```js
|
|
34
|
+
// A 场结尾:主体在画面中央偏左
|
|
35
|
+
// B 场开头:主体也在中央偏左
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 2.2 节奏
|
|
39
|
+
|
|
40
|
+
硬切必须落在节拍上。BPM 120 时一拍 0.5s,切点放在 0.5 的整数倍上,
|
|
41
|
+
观众会觉得「踩上了」;放在 0.37s 就会觉得「怎么这时候切」。
|
|
42
|
+
|
|
43
|
+
### 2.3 切点前后的微调
|
|
44
|
+
|
|
45
|
+
- 切点前 2 帧:画面开始「收」(轻微压暗或缩小 2%)
|
|
46
|
+
- 切点后 2 帧:画面「放」(从 98% 弹到 100%)
|
|
47
|
+
|
|
48
|
+
这两帧的呼吸感让硬切不像「断了」,而像「换了口气」。
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 三、叠化
|
|
53
|
+
|
|
54
|
+
### 3.1 实现
|
|
55
|
+
|
|
56
|
+
```js
|
|
57
|
+
function drawCrossfade(t, a, b, start, dur) {
|
|
58
|
+
const u = clamp01((t - start) / dur);
|
|
59
|
+
// A 淡出用 ease-in,B 淡入用 ease-out,交叉点更自然
|
|
60
|
+
const aAlpha = 1 - easeInCubic(u);
|
|
61
|
+
const bAlpha = easeOutCubic(u);
|
|
62
|
+
if (aAlpha > 0) { ctx.globalAlpha = aAlpha; a(t); }
|
|
63
|
+
if (bAlpha > 0) { ctx.globalAlpha = bAlpha; b(t); }
|
|
64
|
+
ctx.globalAlpha = 1;
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**不要两条都是线性**,中间会有一段时间两边都是 50%,画面发灰。
|
|
69
|
+
用 ease-in / ease-out 交叉,总亮度更稳。
|
|
70
|
+
|
|
71
|
+
### 3.2 时长
|
|
72
|
+
|
|
73
|
+
| 节奏 | 叠化时长 |
|
|
74
|
+
|---|---|
|
|
75
|
+
| 快(BPM > 110) | 0.15–0.25s |
|
|
76
|
+
| 中速 | 0.3s |
|
|
77
|
+
| 慢 / 抒情 | 0.4–0.6s |
|
|
78
|
+
| 超过 0.8s | 拖沓,除非刻意做「时间流逝」 |
|
|
79
|
+
|
|
80
|
+
### 3.3 白闪 / 黑场叠化
|
|
81
|
+
|
|
82
|
+
叠化到一个纯色再叠出来,比直接叠化更有仪式感,而且不改变时间轴:
|
|
83
|
+
|
|
84
|
+
```js
|
|
85
|
+
// 0–0.15s 叠到白,0.15–0.3s 从白叠出
|
|
86
|
+
if (u < 0.5) {
|
|
87
|
+
ctx.fillStyle = `rgba(255,255,255,${easeInCubic(u * 2)})`;
|
|
88
|
+
} else {
|
|
89
|
+
ctx.fillStyle = `rgba(255,255,255,${1 - easeOutCubic((u - 0.5) * 2)})`;
|
|
90
|
+
}
|
|
91
|
+
ctx.fillRect(0, 0, W, H);
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 四、推 / 擦除 / 划像
|
|
97
|
+
|
|
98
|
+
同一时刻 B 覆盖 A,不改变时间轴。
|
|
99
|
+
|
|
100
|
+
### 4.1 横向擦除
|
|
101
|
+
|
|
102
|
+
```js
|
|
103
|
+
function drawWipe(t, a, b, start, dur, dir = 1) {
|
|
104
|
+
const u = easeInOutCubic(clamp01((t - start) / dur));
|
|
105
|
+
a(t);
|
|
106
|
+
ctx.save();
|
|
107
|
+
ctx.beginPath();
|
|
108
|
+
if (dir > 0) ctx.rect(0, 0, W * u, H); // 从左往右露出 B
|
|
109
|
+
else ctx.rect(W * (1 - u), 0, W * u, H);
|
|
110
|
+
ctx.clip();
|
|
111
|
+
b(t);
|
|
112
|
+
ctx.restore();
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### 4.2 推镜
|
|
117
|
+
|
|
118
|
+
B 从右侧推入,A 同时向左退出 20%,有纵深感:
|
|
119
|
+
|
|
120
|
+
```js
|
|
121
|
+
const u = easeOutQuint(clamp01((t - start) / dur));
|
|
122
|
+
ctx.save(); ctx.translate(-W * 0.2 * u, 0); a(t); ctx.restore();
|
|
123
|
+
ctx.save(); ctx.translate(W * (1 - u), 0); b(t); ctx.restore();
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### 4.3 圆形扩散
|
|
127
|
+
|
|
128
|
+
从一点向外扩,适合「聚焦到某个细节」:
|
|
129
|
+
|
|
130
|
+
```js
|
|
131
|
+
ctx.save();
|
|
132
|
+
ctx.beginPath();
|
|
133
|
+
ctx.arc(cx, cy, Math.max(W, H) * u, 0, Math.PI * 2);
|
|
134
|
+
ctx.clip();
|
|
135
|
+
b(t);
|
|
136
|
+
ctx.restore();
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## 五、不占时长的光效
|
|
142
|
+
|
|
143
|
+
闪白、漏光、暗场推进、色带扫过——这些是**叠在切点上**的效果,几帧成本就能
|
|
144
|
+
让硬切看起来像「被打断」。
|
|
145
|
+
|
|
146
|
+
### 5.1 闪白
|
|
147
|
+
|
|
148
|
+
```js
|
|
149
|
+
// 切点前 2 帧到后 3 帧,全屏白,alpha 从 0 冲到 0.8 再落回 0
|
|
150
|
+
const cut = 3.0;
|
|
151
|
+
const u = (t - (cut - 0.066)) / 0.166;
|
|
152
|
+
if (u >= 0 && u <= 1) {
|
|
153
|
+
ctx.fillStyle = `rgba(255,255,255,${0.8 * Math.sin(u * Math.PI)})`;
|
|
154
|
+
ctx.fillRect(0, 0, W, H);
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### 5.2 漏光
|
|
159
|
+
|
|
160
|
+
一道暖色斜向光带扫过,0.3s:
|
|
161
|
+
|
|
162
|
+
```js
|
|
163
|
+
const u = clamp01((t - start) / 0.3);
|
|
164
|
+
const x = -W * 0.5 + (W * 2) * easeInOutCubic(u);
|
|
165
|
+
const g = ctx.createLinearGradient(x, 0, x + W * 0.4, H);
|
|
166
|
+
g.addColorStop(0, 'rgba(255,180,90,0)');
|
|
167
|
+
g.addColorStop(0.5, 'rgba(255,180,90,0.35)');
|
|
168
|
+
g.addColorStop(1, 'rgba(255,180,90,0)');
|
|
169
|
+
ctx.globalCompositeOperation = 'screen';
|
|
170
|
+
ctx.fillStyle = g; ctx.fillRect(0, 0, W, H);
|
|
171
|
+
ctx.globalCompositeOperation = 'source-over';
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### 5.3 故障
|
|
175
|
+
|
|
176
|
+
三帧内随机偏移色块 + RGB 分离,适合科技/赛博题材:
|
|
177
|
+
|
|
178
|
+
```js
|
|
179
|
+
const u = tween(t, cut - 0.1, 0.1);
|
|
180
|
+
if (u > 0 && u < 1) {
|
|
181
|
+
const off = 8 * Math.sin(u * Math.PI * 6);
|
|
182
|
+
ctx.globalCompositeOperation = 'lighter';
|
|
183
|
+
ctx.fillStyle = 'rgba(255,0,80,0.5)'; ctx.fillRect(off, 0, W, H);
|
|
184
|
+
ctx.fillStyle = 'rgba(0,220,255,0.5)'; ctx.fillRect(-off, 0, W, H);
|
|
185
|
+
ctx.globalCompositeOperation = 'source-over';
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## 六、纪律
|
|
192
|
+
|
|
193
|
+
### 6.1 转场期间只做一件事
|
|
194
|
+
|
|
195
|
+
前一秒在放大标题、后一秒又推镜头又换配色,观众的注意力会被撕开。
|
|
196
|
+
|
|
197
|
+
### 6.2 一个片子最多两种转场手法
|
|
198
|
+
|
|
199
|
+
三种以上显得没有章法。硬切 + 叠化已经能覆盖 90% 的场景。
|
|
200
|
+
|
|
201
|
+
### 6.3 转场不解决问题
|
|
202
|
+
|
|
203
|
+
如果两场之间接得生硬,八成是内容没接上(上一场没交代完,下一场突然开始),
|
|
204
|
+
换更花哨的转场只会让问题更明显。
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## 七、和配乐的关系
|
|
209
|
+
|
|
210
|
+
每个切点都应该是配乐的一个重音(`beats` 里标出来)。切点落下去、鼓也落下去,
|
|
211
|
+
观众才觉得「这一下是故意的」。
|
|
212
|
+
|
|
213
|
+
反过来:如果配乐在切点上是空的,那就不要在那个切点做花哨转场,硬切反而更干净。
|
|
214
|
+
|
|
215
|
+
| 转场 | 配乐 |
|
|
216
|
+
|---|---|
|
|
217
|
+
| 硬切 | 短促打击(kick / hit),同帧 |
|
|
218
|
+
| 叠化 | 不需要重音,让音乐自然过渡 |
|
|
219
|
+
| 擦除 / 推 | 上行扫频(whoosh),提前 0.1s 起 |
|
|
220
|
+
| 闪白 | 白噪声瞬态 + 低频撞击 |
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## 八、自检清单
|
|
225
|
+
|
|
226
|
+
- [ ] 叠化有没有偷偷改变总时长?卡点要不要跟着挪?
|
|
227
|
+
- [ ] 切点是否落在节拍上?
|
|
228
|
+
- [ ] 叠化交叉的两条曲线是不是一条 ease-in、一条 ease-out?
|
|
229
|
+
- [ ] 一个片子里转场手法是否 ≤ 2 种?
|
|
230
|
+
- [ ] 转场期间是否只有一件事在变化?
|
|
231
|
+
- [ ] 每个切点在 `beats` 里都有对应重音吗?
|