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,121 @@
|
|
|
1
|
+
# 文案标准(字幕与标题)
|
|
2
|
+
|
|
3
|
+
字幕不是文章。观众只有这条字幕停留的那一两秒,读不完也没有耐心读第二遍。
|
|
4
|
+
|
|
5
|
+
默认要**具体**,不是**抒情**,也不是**口语**——口语只是换一种空话。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 一、三条硬规则(save_storyboard 会直接校验)
|
|
10
|
+
|
|
11
|
+
1. **单条字幕 ≤ 镜头时长 × 5.5 字。** 2 秒镜头约 11 字,5 秒镜头约 27 字。
|
|
12
|
+
超了就删内容,不要缩字号、不要压行、不要用「…」拖过去。
|
|
13
|
+
2. **一条字幕只说一件事。** 两件事就拆两场。
|
|
14
|
+
3. **至少一个可核查的具体信息。** 数字、专名、时间、地点、动作、前后对比。
|
|
15
|
+
一个都没有,说明这条还没想清楚,回去想清楚再写。
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 二、写法
|
|
20
|
+
|
|
21
|
+
### 2.1 动词开头
|
|
22
|
+
|
|
23
|
+
| 弱 | 强 |
|
|
24
|
+
|---|---|
|
|
25
|
+
| 支持多次修改 | 改到第七版,它还记着第一版 |
|
|
26
|
+
| 提供快速导出 | 点一下,8 秒出片 |
|
|
27
|
+
| 拥有丰富模板 | 193 种风格,挑到你满意 |
|
|
28
|
+
|
|
29
|
+
### 2.2 句长要有变化
|
|
30
|
+
|
|
31
|
+
整条都是四字格或六字格,读起来像广告词。长短交错才有呼吸:
|
|
32
|
+
|
|
33
|
+
> 三秒一版分镜。改到第七版,它还记着第一版。
|
|
34
|
+
|
|
35
|
+
前句 6 字,后句 12 字。节奏出来了。
|
|
36
|
+
|
|
37
|
+
### 2.3 保留事实的强度
|
|
38
|
+
|
|
39
|
+
- 「从 3.2 秒压到 0.8 秒」不许改写成「大幅提速」——那是稀释事实
|
|
40
|
+
- 反过来,「大幅提速」也不许擅自补成具体数字——那是编数据
|
|
41
|
+
|
|
42
|
+
**改写时只做减法。** 删掉不承载信息的包装语,不换同义词,不把几个词压成一个概括词。
|
|
43
|
+
|
|
44
|
+
### 2.4 用户没说要「平实」
|
|
45
|
+
|
|
46
|
+
用户没有明确说「写平实点」,就不要降低原有判断的强度。片名可以留白、可以有态度,
|
|
47
|
+
但同样不许堆形容词。
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 三、禁止清单
|
|
52
|
+
|
|
53
|
+
| 类别 | 例子 |
|
|
54
|
+
|---|---|
|
|
55
|
+
| 抽象名词堆叠 | 赋能、闭环、抓手、生态、颗粒度、维度、链路、矩阵 |
|
|
56
|
+
| 四字格排比 | 化繁为简、由浅入深、层层递进 |
|
|
57
|
+
| 空泛评价 | 极致、震撼、完美、惊艳、颠覆、前所未有 |
|
|
58
|
+
| 连接词开场 | 首先、其次、此外、综上所述、值得一提的是 |
|
|
59
|
+
| 万能句式 | 让我们一起…/这不仅…更是…/从…到… |
|
|
60
|
+
| 语气词 / 网络语 | 啊呢吧啦、绝绝子、yyds、狠狠爱了 |
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## 四、对照(左边会被打回)
|
|
65
|
+
|
|
66
|
+
| 打回 | 通过 |
|
|
67
|
+
|---|---|
|
|
68
|
+
| 让我们一起探索这款产品如何赋能创作流程 | 三秒一版分镜。改到第七版,它还记着第一版 |
|
|
69
|
+
| 以极致性能带来前所未有的体验 | 首屏从 3.2 秒压到 0.8 秒 |
|
|
70
|
+
| 化繁为简,让复杂工作变得轻松 | 以前开四个窗口对着抄,现在一屏 |
|
|
71
|
+
| 提供丰富的风格选择,满足不同场景需求 | 193 种风格,水墨到赛博都有 |
|
|
72
|
+
| 通过智能算法大幅提升效率 | 分镜切点自动对齐到毫秒 |
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## 五、标题
|
|
77
|
+
|
|
78
|
+
标题可以比字幕更狠,但同样不许堆形容词。
|
|
79
|
+
|
|
80
|
+
| 类型 | 例子 | 适用 |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| 数字 + 结果 | 193 种风格,一句话出片 | 信息型 |
|
|
83
|
+
| 反差 | 我没剪过一刀 | 故事型 |
|
|
84
|
+
| 具体场景 | 三分钟,从想法到成片 | 演示型 |
|
|
85
|
+
| 提问 | 如果片子能自己长出来? | 悬念型 |
|
|
86
|
+
|
|
87
|
+
标题长度:横屏 ≤ 12 字,竖屏 ≤ 9 字。超过就要换行,换行就要重排字号。
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 六、改写流程
|
|
92
|
+
|
|
93
|
+
拿到一句空话,按顺序做:
|
|
94
|
+
|
|
95
|
+
1. **找出这句话想说的那件事**(往往只有一个)
|
|
96
|
+
2. **问:有没有数字、时间、对比?** 没有就去素材里找
|
|
97
|
+
3. **删掉所有不承载信息的修饰**
|
|
98
|
+
4. **把动词提到前面**
|
|
99
|
+
5. **读一遍,超过 5.5 字/秒就继续删**
|
|
100
|
+
|
|
101
|
+
例:
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
原文:我们致力于通过先进的技术手段,为用户提供高效便捷的创作体验
|
|
105
|
+
↓ 找出那件事:创作很快
|
|
106
|
+
↓ 找数字:一句话,48 秒
|
|
107
|
+
↓ 删修饰:一句话,48 秒出片
|
|
108
|
+
↓ 动词前置:说一句话,48 秒出片
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## 七、自检清单
|
|
114
|
+
|
|
115
|
+
- [ ] 每条字幕 ≤ 镜头时长 × 5.5 字?
|
|
116
|
+
- [ ] 一条只说一件事?
|
|
117
|
+
- [ ] 至少有一个可核查的具体信息?
|
|
118
|
+
- [ ] 没有命中禁止清单里的词?
|
|
119
|
+
- [ ] 句长有变化?
|
|
120
|
+
- [ ] 事实强度没有被稀释,也没有被编造?
|
|
121
|
+
- [ ] 标题 ≤ 12 字(竖屏 ≤ 9 字)?
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# 数据可视化
|
|
2
|
+
|
|
3
|
+
数据镜头是「信息型视频」的主力。做得好是说服力,做不好是 PPT 截图。
|
|
4
|
+
|
|
5
|
+
这一份讲:柱状、折线、环形、数字滚动、对比,以及怎么让它们动得有道理。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 一、先定比例,再画
|
|
10
|
+
|
|
11
|
+
所有图表都要先算「数据 → 像素」的映射,不要边画边算。
|
|
12
|
+
|
|
13
|
+
```js
|
|
14
|
+
const max = Math.max(...values);
|
|
15
|
+
const yOf = (v) => baseY - (v / max) * plotHeight;
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
**纵轴从 0 开始**。截断纵轴会夸大差异,是数据可视化的头号错误。
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 二、柱状
|
|
23
|
+
|
|
24
|
+
### 2.1 生长
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
for (let i = 0; i < values.length; i++) {
|
|
28
|
+
const u = easeOutCubic(tween(t, start + i * 0.06, 0.5));
|
|
29
|
+
const h = (values[i] / max) * plotHeight * u;
|
|
30
|
+
ctx.fillRect(xOf(i), baseY - h, barW, h);
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
错开 60ms 一根,比同时长起来有节奏。
|
|
35
|
+
|
|
36
|
+
### 2.2 强调一根
|
|
37
|
+
|
|
38
|
+
要突出某一根,其余降透明度到 0.35,被强调的保持 1.0 并加高亮色。
|
|
39
|
+
|
|
40
|
+
### 2.3 圆角与间距
|
|
41
|
+
|
|
42
|
+
柱宽 : 间距 = 3 : 1。圆角只在顶部(`roundRect` 或手动路径),底部保持直角贴住基线。
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 三、折线
|
|
47
|
+
|
|
48
|
+
### 3.1 描线
|
|
49
|
+
|
|
50
|
+
用 `setLineDash` + `lineDashOffset` 做「画出来」的效果:
|
|
51
|
+
|
|
52
|
+
```js
|
|
53
|
+
const len = pathLength;
|
|
54
|
+
ctx.setLineDash([len, len]);
|
|
55
|
+
ctx.lineDashOffset = len * (1 - u);
|
|
56
|
+
ctx.stroke();
|
|
57
|
+
ctx.setLineDash([]);
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
或者逐段绘制(更可控,能配合点的高亮)。
|
|
61
|
+
|
|
62
|
+
### 3.2 面积填充
|
|
63
|
+
|
|
64
|
+
折线下方填充半透明色,alpha 0.15–0.25:
|
|
65
|
+
|
|
66
|
+
```js
|
|
67
|
+
ctx.beginPath();
|
|
68
|
+
ctx.moveTo(pts[0].x, baseY);
|
|
69
|
+
for (const p of pts) ctx.lineTo(p.x, p.y);
|
|
70
|
+
ctx.lineTo(pts[pts.length - 1].x, baseY);
|
|
71
|
+
ctx.closePath();
|
|
72
|
+
ctx.fillStyle = 'rgba(255,138,61,0.18)';
|
|
73
|
+
ctx.fill();
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 3.3 数据点
|
|
77
|
+
|
|
78
|
+
点在高亮时放大到 1.5 倍并加光晕。**不要每个点都加光晕**,那样像星空。
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## 四、环形 / 饼图
|
|
83
|
+
|
|
84
|
+
### 4.1 扇区
|
|
85
|
+
|
|
86
|
+
```js
|
|
87
|
+
let a0 = -Math.PI / 2;
|
|
88
|
+
for (const seg of segments) {
|
|
89
|
+
const a1 = a0 + (seg.value / total) * Math.PI * 2 * u;
|
|
90
|
+
ctx.beginPath();
|
|
91
|
+
ctx.moveTo(cx, cy);
|
|
92
|
+
ctx.arc(cx, cy, r, a0, a1);
|
|
93
|
+
ctx.closePath();
|
|
94
|
+
ctx.fill();
|
|
95
|
+
a0 = a1;
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 4.2 环形进度
|
|
100
|
+
|
|
101
|
+
中间挖空(`globalCompositeOperation = 'destination-out'` 画一个内圆),
|
|
102
|
+
再在中间放百分比数字。
|
|
103
|
+
|
|
104
|
+
### 4.3 别用饼图
|
|
105
|
+
|
|
106
|
+
超过 4 个扇区,人眼分不清角度。改用横向条形图,长度比角度好判断。
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## 五、数字
|
|
111
|
+
|
|
112
|
+
### 5.1 滚动到目标
|
|
113
|
+
|
|
114
|
+
```js
|
|
115
|
+
const u = easeOutQuint(tween(t, start, 0.9));
|
|
116
|
+
const shown = target * u;
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
末尾必须减速,否则读者看不清最终值。
|
|
120
|
+
|
|
121
|
+
### 5.2 千分位
|
|
122
|
+
|
|
123
|
+
```js
|
|
124
|
+
const fmt = (n) => n.toLocaleString('zh-CN', { maximumFractionDigits: 1 });
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 5.3 单位位置
|
|
128
|
+
|
|
129
|
+
单位(万 / 亿 / % / ms)放在数字右侧,字号是数字的 0.5 倍,基线对齐。
|
|
130
|
+
|
|
131
|
+
### 5.4 对比
|
|
132
|
+
|
|
133
|
+
两个数字对比时,**同时**滚动到各自目标值,而不是先后。同时到达才有「比较」的意味。
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## 六、对比与变化
|
|
138
|
+
|
|
139
|
+
### 6.1 前后对比
|
|
140
|
+
|
|
141
|
+
左右并排,中间一条分隔线。两边的动画同步启动、同步结束。
|
|
142
|
+
|
|
143
|
+
### 6.2 差值高亮
|
|
144
|
+
|
|
145
|
+
用一段色块标出「变化的部分」,配一个数字标注差值。
|
|
146
|
+
|
|
147
|
+
### 6.3 上升 / 下降
|
|
148
|
+
|
|
149
|
+
箭头方向 + 颜色(涨用暖色、跌用冷色),但**颜色不是唯一信号**——色盲用户看不出来,
|
|
150
|
+
所以箭头形状也要区分。
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## 七、动效纪律
|
|
155
|
+
|
|
156
|
+
| 该动 | 不该动 |
|
|
157
|
+
|---|---|
|
|
158
|
+
| 柱子的生长 | 坐标轴数字乱跳 |
|
|
159
|
+
| 折线的描绘 | 网格线闪烁 |
|
|
160
|
+
| 数字滚动 | 图例颜色渐变 |
|
|
161
|
+
| 高亮切换 | 整个图表旋转 |
|
|
162
|
+
|
|
163
|
+
**图表动效的目的是引导阅读顺序**,不是炫技。观众应该先看标题,再看高亮的那一根,
|
|
164
|
+
最后看总数。
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## 八、常见错误
|
|
169
|
+
|
|
170
|
+
| 错误 | 后果 |
|
|
171
|
+
|---|---|
|
|
172
|
+
| 纵轴不从 0 开始 | 夸大差异,误导 |
|
|
173
|
+
| 饼图扇区 > 4 | 分不清 |
|
|
174
|
+
| 每个数据点都发光 | 像星空,看不出趋势 |
|
|
175
|
+
| 数字滚动不减速 | 读不出最终值 |
|
|
176
|
+
| 图例用 6 种颜色 | 记不住 |
|
|
177
|
+
| 网格线比数据线还粗 | 喧宾夺主 |
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## 九、自检清单
|
|
182
|
+
|
|
183
|
+
- [ ] 纵轴从 0 开始?
|
|
184
|
+
- [ ] 柱子错开生长(60ms)?
|
|
185
|
+
- [ ] 数字末尾减速,单位提前固定?
|
|
186
|
+
- [ ] 对比的两个数字同时到达?
|
|
187
|
+
- [ ] 图表动效 ≤ 2 种?
|
|
188
|
+
- [ ] 高亮只有一处?
|
|
189
|
+
- [ ] 颜色之外还有形状/文字区分?
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# 视觉特效
|
|
2
|
+
|
|
3
|
+
特效是调味,不是主菜。一条片子里特效超过三处,观众就只看特效不看内容了。
|
|
4
|
+
|
|
5
|
+
这一份讲:模糊、发光、故障、拖影、扫描线、噪点,以及它们的性能代价。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 一、模糊
|
|
10
|
+
|
|
11
|
+
### 1.1 运动模糊(方向模糊)
|
|
12
|
+
|
|
13
|
+
快速移动的元素,沿运动方向拉出拖影。Canvas 没有内置方向模糊,用多次错位绘制近似:
|
|
14
|
+
|
|
15
|
+
```js
|
|
16
|
+
function drawMotionBlur(fn, dx, dy, samples = 6, alpha = 0.16) {
|
|
17
|
+
for (let i = 0; i < samples; i++) {
|
|
18
|
+
const k = i / (samples - 1 || 1);
|
|
19
|
+
ctx.save();
|
|
20
|
+
ctx.globalAlpha = alpha;
|
|
21
|
+
ctx.translate(-dx * k, -dy * k);
|
|
22
|
+
fn();
|
|
23
|
+
ctx.restore();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// 用:一个向右移动 60px/帧的方块
|
|
27
|
+
drawMotionBlur(() => ctx.fillRect(x, y, 80, 80), 60, 0);
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
`samples` 越多越顺滑,但绘制成本线性上升。6 个样本 + 0.16 alpha 是最省的做法。
|
|
31
|
+
|
|
32
|
+
### 1.2 景深模糊
|
|
33
|
+
|
|
34
|
+
前后景分层,后景模糊。Canvas 用 `ctx.filter = 'blur(Npx)'`:
|
|
35
|
+
|
|
36
|
+
```js
|
|
37
|
+
ctx.save();
|
|
38
|
+
ctx.filter = 'blur(8px)';
|
|
39
|
+
drawBackground(t);
|
|
40
|
+
ctx.restore();
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`ctx.filter` 在部分浏览器上很慢(每帧重新计算卷积)。静态背景可以先渲染到离屏
|
|
44
|
+
canvas,只在需要时重画。
|
|
45
|
+
|
|
46
|
+
### 1.3 什么时候不要模糊
|
|
47
|
+
|
|
48
|
+
- 文字:小字模糊后完全读不出来
|
|
49
|
+
- 细线:1px 的线模糊后消失
|
|
50
|
+
- 大面积纯色:模糊了看不出来,白费性能
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 二、发光
|
|
55
|
+
|
|
56
|
+
### 2.1 多层叠加
|
|
57
|
+
|
|
58
|
+
比 `shadowBlur` 便宜且可控:
|
|
59
|
+
|
|
60
|
+
```js
|
|
61
|
+
function glowDot(x, y, r, color) {
|
|
62
|
+
ctx.globalCompositeOperation = 'lighter';
|
|
63
|
+
for (let i = 3; i >= 1; i--) {
|
|
64
|
+
const rr = r * (1 + i * 0.9);
|
|
65
|
+
const g = ctx.createRadialGradient(x, y, 0, x, y, rr);
|
|
66
|
+
g.addColorStop(0, color.replace('ALPHA', 0.28 / i));
|
|
67
|
+
g.addColorStop(1, color.replace('ALPHA', 0));
|
|
68
|
+
ctx.fillStyle = g;
|
|
69
|
+
ctx.fillRect(x - rr, y - rr, rr * 2, rr * 2);
|
|
70
|
+
}
|
|
71
|
+
ctx.globalCompositeOperation = 'source-over';
|
|
72
|
+
}
|
|
73
|
+
// 用:glowDot(400, 300, 20, 'rgba(255,138,61,ALPHA)')
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 2.2 阈值发光(bloom)
|
|
77
|
+
|
|
78
|
+
真实 bloom 是「亮的地方溢光」。近似做法:把亮部提出来,模糊,再叠加。
|
|
79
|
+
|
|
80
|
+
```js
|
|
81
|
+
// 1) 画场景
|
|
82
|
+
drawScene(t);
|
|
83
|
+
// 2) 提亮部(简化:直接整屏叠一层模糊后的自身)
|
|
84
|
+
ctx.save();
|
|
85
|
+
ctx.globalCompositeOperation = 'lighter';
|
|
86
|
+
ctx.globalAlpha = 0.25;
|
|
87
|
+
ctx.filter = 'blur(12px)';
|
|
88
|
+
ctx.drawImage(canvas, 0, 0);
|
|
89
|
+
ctx.restore();
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
整屏 bloom 会让暗部发灰。只在关键帧用。
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 三、故障(glitch)
|
|
97
|
+
|
|
98
|
+
### 3.1 切片错位
|
|
99
|
+
|
|
100
|
+
```js
|
|
101
|
+
function glitchSlices(t, amount) {
|
|
102
|
+
const slices = 12;
|
|
103
|
+
// 按帧号取种子:同一帧永远是同一个错位量,导出才不会抖
|
|
104
|
+
const rand = mulberry32(Math.floor(t * 30) + 1);
|
|
105
|
+
for (let i = 0; i < slices; i++) {
|
|
106
|
+
const y = (H / slices) * i;
|
|
107
|
+
const off = (rand() - 0.5) * 2 * amount;
|
|
108
|
+
ctx.drawImage(canvas, 0, y, W, H / slices, off, y, W, H / slices);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
`Math.random()` 在这里是错的:预览时每帧随机,看着像故障;导出时按帧取样,
|
|
114
|
+
同一个 `t` 每次渲染结果不同,视频会出现无法复现的抖动。
|
|
115
|
+
|
|
116
|
+
### 3.2 RGB 分离
|
|
117
|
+
|
|
118
|
+
```js
|
|
119
|
+
ctx.globalCompositeOperation = 'lighter';
|
|
120
|
+
ctx.fillStyle = 'rgba(255,0,80,0.6)'; ctx.fillRect(-4, 0, W, H);
|
|
121
|
+
ctx.fillStyle = 'rgba(0,220,255,0.6)'; ctx.fillRect(4, 0, W, H);
|
|
122
|
+
ctx.globalCompositeOperation = 'source-over';
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### 3.3 什么时候用
|
|
126
|
+
|
|
127
|
+
- 科技 / 赛博 / 故障艺术题材
|
|
128
|
+
- 转场(0.1–0.2s,不要持续)
|
|
129
|
+
- **不要**用在文字为主的信息型画面上,读不清
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## 四、拖影
|
|
134
|
+
|
|
135
|
+
### 4.1 残影(trail)
|
|
136
|
+
|
|
137
|
+
不擦除画布,只叠一层半透明底色,上一次的绘制就会留下:
|
|
138
|
+
|
|
139
|
+
```js
|
|
140
|
+
// 每帧开头
|
|
141
|
+
ctx.fillStyle = 'rgba(11,13,18,0.18)'; // 不是 clearRect
|
|
142
|
+
ctx.fillRect(0, 0, W, H);
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
这是**有状态**的做法,违反纯函数约束。导出时会因为跳帧而拖影不一致。
|
|
146
|
+
|
|
147
|
+
替代:按 `t` 计算最近 N 帧的位置,逐个画出来:
|
|
148
|
+
|
|
149
|
+
```js
|
|
150
|
+
for (let k = 0; k < 6; k++) {
|
|
151
|
+
const tk = Math.max(0, t - k * (1 / 30));
|
|
152
|
+
ctx.globalAlpha = 0.5 * Math.pow(0.6, k);
|
|
153
|
+
drawDotAt(tk);
|
|
154
|
+
}
|
|
155
|
+
ctx.globalAlpha = 1;
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### 4.2 速度线
|
|
159
|
+
|
|
160
|
+
漫画式速度线:从中心向外放射的短线段,透明度随速度变化。
|
|
161
|
+
|
|
162
|
+
```js
|
|
163
|
+
const lines = 40;
|
|
164
|
+
for (let i = 0; i < lines; i++) {
|
|
165
|
+
const a = (i / lines) * Math.PI * 2;
|
|
166
|
+
const r0 = 120 + rand() * 60;
|
|
167
|
+
const r1 = r0 + 40 + speed * 80;
|
|
168
|
+
ctx.strokeStyle = `rgba(255,255,255,${0.05 + speed * 0.15})`;
|
|
169
|
+
ctx.beginPath();
|
|
170
|
+
ctx.moveTo(cx + Math.cos(a) * r0, cy + Math.sin(a) * r0);
|
|
171
|
+
ctx.lineTo(cx + Math.cos(a) * r1, cy + Math.sin(a) * r1);
|
|
172
|
+
ctx.stroke();
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## 五、扫描线 / CRT
|
|
179
|
+
|
|
180
|
+
```js
|
|
181
|
+
function scanlines(gap = 3, alpha = 0.08) {
|
|
182
|
+
ctx.fillStyle = `rgba(0,0,0,${alpha})`;
|
|
183
|
+
for (let y = 0; y < H; y += gap) ctx.fillRect(0, y, W, 1);
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
配合轻微的垂直滚动,就是 CRT 质感:
|
|
188
|
+
|
|
189
|
+
```js
|
|
190
|
+
const offset = (t * 30) % gap;
|
|
191
|
+
for (let y = -gap; y < H; y += gap) ctx.fillRect(0, y + offset, W, 1);
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
`gap` 小于 2 在 1080p 上会摩尔纹,建议 3–4。
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## 六、粒子光效
|
|
199
|
+
|
|
200
|
+
见 `get_reference("particles")`。这里只讲一个原则:**粒子用贴图,不要每个都画路径**。
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## 七、性能对照
|
|
205
|
+
|
|
206
|
+
| 特效 | 1080p 单帧成本 | 建议 |
|
|
207
|
+
|---|---|---|
|
|
208
|
+
| `fillRect` 叠色 | <0.1ms | 随便用 |
|
|
209
|
+
| 径向渐变光斑 | 0.5–2ms | 预渲染成贴图 |
|
|
210
|
+
| `shadowBlur` 20px | 2–5ms | 少用 |
|
|
211
|
+
| `ctx.filter = blur(8px)` | 4–10ms | 只在关键帧 |
|
|
212
|
+
| 逐像素 `getImageData` | 15–50ms | 禁止 |
|
|
213
|
+
| 切片错位 12 片 | 1–3ms | 可接受 |
|
|
214
|
+
| 6 样本运动模糊 | ×6 绘制成本 | 只给快速元素 |
|
|
215
|
+
|
|
216
|
+
**一帧 33ms 的预算(30fps),特效超过 10ms 就会掉帧。**
|
|
217
|
+
|
|
218
|
+
---
|
|
219
|
+
|
|
220
|
+
## 八、纪律
|
|
221
|
+
|
|
222
|
+
1. **一条片子最多三种特效**。多了像滤镜堆叠。
|
|
223
|
+
2. **特效服务于叙事**:故障是为了表达「系统崩了」,不是为了好看。
|
|
224
|
+
3. **不要用特效掩盖内容空洞**。画面没内容时加特效,只会显得更空。
|
|
225
|
+
4. **导出前逐帧看**,特效在静态截图里往往比动态播放时好看。
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## 九、自检清单
|
|
230
|
+
|
|
231
|
+
- [ ] 全片特效种类 ≤ 3?
|
|
232
|
+
- [ ] 每个特效都有叙事理由?
|
|
233
|
+
- [ ] 所有随机都用了固定种子?
|
|
234
|
+
- [ ] 模糊没有用在文字和细线上?
|
|
235
|
+
- [ ] 一帧的特效总成本 < 10ms?
|
|
236
|
+
- [ ] 拖影是按 `t` 重算的,不是靠画布残留?
|