morfeo 0.1.2 → 0.2.0
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/bin/morfeo.mjs +35 -1
- package/index.d.ts +59 -7
- package/lib/config.mjs +11 -8
- package/lib/diccionario.mjs +5 -2
- package/lib/estructura.mjs +104 -0
- package/lib/guion.mjs +25 -8
- package/lib/hoja.mjs +98 -0
- package/lib/imagenes.mjs +2 -1
- package/lib/personajes.mjs +1 -1
- package/lib/recursos.mjs +24 -12
- package/lib/render.mjs +9 -2
- package/package.json +2 -1
- package/python/preparar.py +1 -1
- package/remotion/estilo.ts +13 -0
- package/remotion/kit/ilustrado.tsx +220 -0
- package/remotion/kit/index.ts +6 -0
- package/remotion/kit/tres.tsx +328 -0
- package/remotion/motor/Video.tsx +171 -20
- package/remotion/motor/anotaciones.tsx +296 -0
- package/remotion/motor/capas.tsx +50 -27
- package/remotion/motor/fondo.tsx +56 -2
- package/remotion/motor/modelo3d.tsx +47 -5
- package/remotion/motor/plantillas.tsx +26 -1
- package/remotion/motor/subtitulos.tsx +159 -36
- package/remotion/motor/tema.ts +46 -17
- package/remotion/motor/util.ts +3 -0
- package/remotion/tipos.ts +2 -0
|
@@ -1,15 +1,26 @@
|
|
|
1
1
|
// Subtítulos del motor de capas.
|
|
2
|
-
// karaoke: frase corta arriba; lo ya dicho en
|
|
3
|
-
//
|
|
2
|
+
// karaoke: frase corta arriba; lo ya dicho en un color, lo que falta tenue y la palabra actual resaltada.
|
|
3
|
+
// seleccion: las palabras aparecen al decirse; lo dicho queda tenue, la actual en claro y la palabra clave de la
|
|
4
|
+
// frase se queda dentro de una caja de selección con asas (documentales ilustrados).
|
|
5
|
+
// pop: 1–3 palabras grandes en mayúsculas; la palabra clave en color de acento y la actual da un golpe de escala.
|
|
4
6
|
// bloque: 2 renglones centrados en mayúsculas; la palabra actual dentro de una caja de color (series animadas).
|
|
5
|
-
// palabra: una sola palabra grande a la vez;
|
|
7
|
+
// palabra: una sola palabra grande a la vez; la palabra clave en color de acento (videos 3D).
|
|
8
|
+
// caja: la frase completa dentro de una etiqueta clara (historias, anime).
|
|
6
9
|
// karaoke con `fondo`: la frase dentro de una caja oscura (infografías).
|
|
10
|
+
// Palabra clave de una frase: la que la escena marque en `claves: ['cerebro']`; si no, una cifra; si no, la más larga.
|
|
7
11
|
import React from 'react';
|
|
8
|
-
import {W, backOut, clamp} from '../estilo';
|
|
12
|
+
import {W, backOut, clamp, easeOut, resorte} from '../estilo';
|
|
9
13
|
import type {Tema} from './tema';
|
|
10
14
|
|
|
11
15
|
type Palabra = [string, number, number];
|
|
12
16
|
|
|
17
|
+
const limpia = (w: string) =>
|
|
18
|
+
w
|
|
19
|
+
.normalize('NFD')
|
|
20
|
+
.replace(/[̀-ͯ]/g, '')
|
|
21
|
+
.toLowerCase()
|
|
22
|
+
.replace(/[^a-z0-9]/g, '');
|
|
23
|
+
|
|
13
24
|
/** Parte las palabras de la escena en frases de hasta `max` palabras, cortando en la puntuación. */
|
|
14
25
|
function frases(palabras: Palabra[], max: number): Palabra[][] {
|
|
15
26
|
const out: Palabra[][] = [];
|
|
@@ -25,23 +36,54 @@ function frases(palabras: Palabra[], max: number): Palabra[][] {
|
|
|
25
36
|
return out;
|
|
26
37
|
}
|
|
27
38
|
|
|
28
|
-
|
|
39
|
+
/** Índice de la palabra clave de la frase (-1 = ninguna). */
|
|
40
|
+
function claveDe(g: Palabra[], claves?: string[]): number {
|
|
41
|
+
if (claves?.length) {
|
|
42
|
+
const cs = claves.map(limpia).filter(Boolean);
|
|
43
|
+
const i = g.findIndex(([w]) => cs.some((c) => limpia(w).startsWith(c)));
|
|
44
|
+
if (i >= 0) return i;
|
|
45
|
+
}
|
|
46
|
+
const cifra = g.findIndex(([w]) => /\d/.test(w));
|
|
47
|
+
if (cifra >= 0) return cifra;
|
|
48
|
+
let mejor = -1;
|
|
49
|
+
let largo = 7; // solo palabras de 8 letras o más
|
|
50
|
+
g.forEach(([w], i) => {
|
|
51
|
+
const n = limpia(w).length;
|
|
52
|
+
if (n > largo) {
|
|
53
|
+
largo = n;
|
|
54
|
+
mejor = i;
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
return mejor;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Golpe de escala al decir una palabra: sube de golpe y se asienta. */
|
|
61
|
+
const golpe = (t: number, a: number, fuerza: number) => (t < a ? 1 : 1 + fuerza * (1 - easeOut((t - a) / 0.22)));
|
|
62
|
+
|
|
63
|
+
const Asa: React.FC<{x: string; y: string; color: string}> = ({x, y, color}) => (
|
|
64
|
+
<span style={{position: 'absolute', left: x, top: y, width: 11, height: 11, marginLeft: -6, marginTop: -6, background: '#fff', border: `2.5px solid ${color}`, boxSizing: 'border-box'}} />
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
export const Subtitulos: React.FC<{tema: Tema; palabras: Palabra[]; t: number; dur: number; claves?: string[]}> = ({tema, palabras, t, dur, claves}) => {
|
|
29
68
|
const S = tema.subtitulos;
|
|
30
69
|
if (S.tipo === 'ninguno' || !palabras.length) return null;
|
|
31
|
-
const max = S.tipo === 'pop' ?
|
|
70
|
+
const max = S.tipo === 'pop' ? Math.min(S.maxPalabras, 4) : S.tipo === 'bloque' ? Math.min(S.maxPalabras, 5) : S.tipo === 'palabra' ? 1 : S.maxPalabras;
|
|
32
71
|
const grupos = frases(palabras, max);
|
|
33
72
|
const gi = grupos.findIndex((g, i) => t < (grupos[i + 1]?.[0][1] ?? dur) - 0.02);
|
|
34
73
|
const g = grupos[gi];
|
|
35
74
|
if (!g || t < g[0][1] - 0.12) return null;
|
|
36
75
|
const entrada = clamp((t - (g[0][1] - 0.12)) / 0.18);
|
|
76
|
+
const clave = claveDe(g, claves);
|
|
77
|
+
const texto = (w: string) => (S.mayusculas ? w.toUpperCase() : w);
|
|
37
78
|
|
|
38
79
|
if (S.tipo === 'palabra') {
|
|
39
80
|
const [w] = g[0];
|
|
40
|
-
|
|
81
|
+
// con una palabra por frase, la clave se decide sobre toda la escena: las marcadas, las cifras y las largas
|
|
82
|
+
const acento = claveDe([g[0]], claves) === 0;
|
|
41
83
|
return (
|
|
42
|
-
<div style={{position: 'absolute', left: 40, width: W - 80, top: S.y, transform: `translateY(-50%) scale(${
|
|
84
|
+
<div style={{position: 'absolute', left: 40, width: W - 80, top: S.y, transform: `translateY(-50%) scale(${resorte(entrada, 0.25)})`, textAlign: 'center'}}>
|
|
43
85
|
<span style={{fontFamily: tema.fuentes.texto, fontWeight: 900, fontSize: S.tam * 1.25, color: acento ? tema.colores.acento : S.color, WebkitTextStroke: `${S.contorno ?? 9}px #000`, paintOrder: 'stroke fill', textShadow: '0 6px 0 rgba(0,0,0,0.35)'}}>
|
|
44
|
-
{
|
|
86
|
+
{texto(w)}
|
|
45
87
|
</span>
|
|
46
88
|
</div>
|
|
47
89
|
);
|
|
@@ -67,7 +109,8 @@ export const Subtitulos: React.FC<{tema: Tema; palabras: Palabra[]; t: number; d
|
|
|
67
109
|
borderRadius: 10,
|
|
68
110
|
WebkitTextStroke: actual && S.resalte ? undefined : `${S.contorno ?? 8}px #1a1a1a`,
|
|
69
111
|
paintOrder: 'stroke fill',
|
|
70
|
-
|
|
112
|
+
display: 'inline-block',
|
|
113
|
+
transform: `scale(${golpe(t, a, 0.1)})`,
|
|
71
114
|
}}
|
|
72
115
|
>
|
|
73
116
|
{w}
|
|
@@ -79,14 +122,94 @@ export const Subtitulos: React.FC<{tema: Tema; palabras: Palabra[]; t: number; d
|
|
|
79
122
|
}
|
|
80
123
|
|
|
81
124
|
if (S.tipo === 'pop') {
|
|
125
|
+
const tam = S.tam * 1.5;
|
|
82
126
|
return (
|
|
83
|
-
<div style={{position: 'absolute', left: 60, width: W - 120, top: S.y, transform: `translateY(-50%) scale(${
|
|
84
|
-
{g.map(([w, a
|
|
85
|
-
|
|
127
|
+
<div style={{position: 'absolute', left: 60, width: W - 120, top: S.y, transform: `translateY(-50%) scale(${resorte(entrada, 0.2)})`, display: 'flex', justifyContent: 'center', flexWrap: 'wrap', gap: `0 ${Math.round(tam * 0.3)}px`}}>
|
|
128
|
+
{g.map(([w, a], i) => (
|
|
129
|
+
<span
|
|
130
|
+
key={i}
|
|
131
|
+
style={{
|
|
132
|
+
fontFamily: tema.fuentes.titulo,
|
|
133
|
+
fontWeight: 900,
|
|
134
|
+
fontSize: tam,
|
|
135
|
+
lineHeight: 1.12,
|
|
136
|
+
color: i === clave ? tema.colores.acento : S.color,
|
|
137
|
+
WebkitTextStroke: `${S.contorno ?? 10}px #000`,
|
|
138
|
+
paintOrder: 'stroke fill',
|
|
139
|
+
textShadow: '0 6px 0 rgba(0,0,0,0.4)',
|
|
140
|
+
textTransform: 'uppercase',
|
|
141
|
+
display: 'inline-block',
|
|
142
|
+
transformOrigin: 'center bottom',
|
|
143
|
+
opacity: t >= a - 0.04 ? 1 : 0.55,
|
|
144
|
+
transform: `scale(${golpe(t, a, 0.14)})`,
|
|
145
|
+
}}
|
|
146
|
+
>
|
|
147
|
+
{w}
|
|
148
|
+
</span>
|
|
149
|
+
))}
|
|
150
|
+
</div>
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (S.tipo === 'caja') {
|
|
155
|
+
return (
|
|
156
|
+
<div style={{position: 'absolute', left: 70, width: W - 140, top: S.y, transform: `translateY(-50%) scale(${0.94 + 0.06 * resorte(entrada, 0.2)})`, opacity: clamp(entrada * 2), textAlign: 'center'}}>
|
|
157
|
+
<span
|
|
158
|
+
style={{
|
|
159
|
+
display: 'inline-block',
|
|
160
|
+
background: S.fondo ?? '#EAF3FF',
|
|
161
|
+
color: S.color,
|
|
162
|
+
fontFamily: tema.fuentes.texto,
|
|
163
|
+
fontWeight: 800,
|
|
164
|
+
fontSize: S.tam,
|
|
165
|
+
lineHeight: 1.25,
|
|
166
|
+
padding: '14px 30px',
|
|
167
|
+
borderRadius: 18,
|
|
168
|
+
boxShadow: '0 8px 22px rgba(0,0,0,0.28)',
|
|
169
|
+
}}
|
|
170
|
+
>
|
|
171
|
+
{g.map(([w, a], i) => (
|
|
172
|
+
<React.Fragment key={i}>
|
|
173
|
+
<span style={{opacity: t >= a - 0.04 ? 1 : 0.38, color: i === clave && S.resalte ? S.resalte : undefined}}>{texto(w)}</span>{' '}
|
|
174
|
+
</React.Fragment>
|
|
175
|
+
))}
|
|
176
|
+
</span>
|
|
177
|
+
</div>
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (S.tipo === 'seleccion') {
|
|
182
|
+
const marco = S.resalte || '#4C8DFF';
|
|
183
|
+
const dichas = g.filter(([, a]) => t >= a - 0.03);
|
|
184
|
+
if (!dichas.length) return null;
|
|
185
|
+
return (
|
|
186
|
+
<div style={{position: 'absolute', left: 90, width: W - 180, top: S.y, transform: 'translateY(-50%)', textAlign: 'center', fontFamily: tema.fuentes.texto, fontSize: S.tam, lineHeight: 1.42, textShadow: '0 2px 10px rgba(0,0,0,0.45)'}}>
|
|
187
|
+
{dichas.map(([w, a, b], i) => {
|
|
188
|
+
const actual = t < b + 0.08 || i === dichas.length - 1;
|
|
189
|
+
const esClave = i === clave;
|
|
190
|
+
const k = resorte(clamp((t - a) / 0.3), 0.3);
|
|
86
191
|
return (
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
|
|
192
|
+
<React.Fragment key={i}>
|
|
193
|
+
<span
|
|
194
|
+
style={{
|
|
195
|
+
position: 'relative',
|
|
196
|
+
display: 'inline-block',
|
|
197
|
+
color: actual || esClave ? S.color : S.pendiente,
|
|
198
|
+
fontWeight: actual || esClave ? 800 : 600,
|
|
199
|
+
opacity: clamp((t - a + 0.03) / 0.1),
|
|
200
|
+
padding: esClave ? '0 10px' : undefined,
|
|
201
|
+
margin: esClave ? '0 2px' : undefined,
|
|
202
|
+
}}
|
|
203
|
+
>
|
|
204
|
+
{esClave ? (
|
|
205
|
+
<span style={{position: 'absolute', inset: '4px 0', border: `3px solid ${marco}`, background: `${marco}33`, borderRadius: 3, transform: `scale(${0.7 + 0.3 * k})`, opacity: clamp(k * 2)}}>
|
|
206
|
+
<Asa x="0%" y="0%" color={marco} />
|
|
207
|
+
<Asa x="100%" y="100%" color={marco} />
|
|
208
|
+
</span>
|
|
209
|
+
) : null}
|
|
210
|
+
<span style={{position: 'relative'}}>{texto(w)}</span>
|
|
211
|
+
</span>{' '}
|
|
212
|
+
</React.Fragment>
|
|
90
213
|
);
|
|
91
214
|
})}
|
|
92
215
|
</div>
|
|
@@ -111,26 +234,26 @@ export const Subtitulos: React.FC<{tema: Tema; palabras: Palabra[]; t: number; d
|
|
|
111
234
|
}}
|
|
112
235
|
>
|
|
113
236
|
<span style={S.fondo ? {background: S.fondo, padding: '10px 22px', borderRadius: 14, boxDecorationBreak: 'clone', WebkitBoxDecorationBreak: 'clone', lineHeight: 1.6} : undefined}>
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
237
|
+
{g.map(([w, a, b], i) => {
|
|
238
|
+
const dicha = t >= a;
|
|
239
|
+
const actual = t >= a && t < b + 0.06;
|
|
240
|
+
return (
|
|
241
|
+
<React.Fragment key={i}>
|
|
242
|
+
<span
|
|
243
|
+
style={{
|
|
244
|
+
color: dicha ? S.color : S.pendiente,
|
|
245
|
+
fontWeight: actual ? 800 : 700,
|
|
246
|
+
background: actual && S.resalte ? S.resalte : undefined,
|
|
247
|
+
borderRadius: 10,
|
|
248
|
+
padding: actual && S.resalte ? '2px 10px' : undefined,
|
|
249
|
+
margin: actual && S.resalte ? '0 -10px' : undefined,
|
|
250
|
+
}}
|
|
251
|
+
>
|
|
252
|
+
{texto(w)}
|
|
253
|
+
</span>{' '}
|
|
254
|
+
</React.Fragment>
|
|
255
|
+
);
|
|
256
|
+
})}
|
|
134
257
|
</span>
|
|
135
258
|
</div>
|
|
136
259
|
);
|
package/remotion/motor/tema.ts
CHANGED
|
@@ -32,7 +32,12 @@ export type Tema = {
|
|
|
32
32
|
fuentes: {titulo: string; texto: string; mono: string};
|
|
33
33
|
tituloMayusculas: boolean;
|
|
34
34
|
subtitulos: {
|
|
35
|
-
|
|
35
|
+
/**
|
|
36
|
+
* karaoke: frase arriba, lo dicho en un color y lo pendiente tenue · seleccion: igual, pero solo la palabra clave
|
|
37
|
+
* de cada frase lleva caja de selección con asas · pop: 1–3 palabras grandes · bloque: 2 renglones con caja ·
|
|
38
|
+
* palabra: una palabra a la vez · caja: la frase completa en una etiqueta clara.
|
|
39
|
+
*/
|
|
40
|
+
tipo: 'karaoke' | 'seleccion' | 'pop' | 'bloque' | 'palabra' | 'caja' | 'ninguno';
|
|
36
41
|
mayusculas?: boolean;
|
|
37
42
|
contorno?: number;
|
|
38
43
|
fondo?: string; // caja detrás de la frase (karaoke)
|
|
@@ -43,7 +48,16 @@ export type Tema = {
|
|
|
43
48
|
resalte: string; // caja de la palabra actual ('' = sin caja)
|
|
44
49
|
maxPalabras: number;
|
|
45
50
|
};
|
|
46
|
-
transicion: 'corte' | 'fundido' | 'deslizar' | 'zoom';
|
|
51
|
+
transicion: 'corte' | 'fundido' | 'deslizar' | 'zoom' | 'barrido' | 'empuje' | 'mascara' | 'destello';
|
|
52
|
+
/** Ritmo de las animaciones: duraciones (s) y curva de las entradas. Sin esto: 0.5 / 0.35 / 0.35 y 'suave'. */
|
|
53
|
+
movimiento?: {entrada?: number; salida?: number; transicion?: number; curva?: 'suave' | 'resorte' | 'seca'};
|
|
54
|
+
/** Acabado sobre todo el cuadro: tinte de color, cono de luz y desenfoque de bordes (profundidad). */
|
|
55
|
+
post?: {
|
|
56
|
+
tinte?: {color: string; fuerza?: number; modo?: 'multiply' | 'screen' | 'overlay' | 'soft-light'};
|
|
57
|
+
luz?: {x?: number; y?: number; ancho?: number; largo?: number; color?: string; fuerza?: number; angulo?: number};
|
|
58
|
+
desenfoqueBordes?: number; // px de blur arriba y abajo (efecto maqueta / profundidad)
|
|
59
|
+
vinetaColor?: string;
|
|
60
|
+
};
|
|
47
61
|
marca: 'esquina' | 'arriba' | 'ninguna';
|
|
48
62
|
/** Movimiento suave de cámara por defecto en cada escena (zoom de 1 a este valor). */
|
|
49
63
|
zoomLento: number;
|
|
@@ -62,8 +76,9 @@ export const ESTILOS: Record<string, Tema> = {
|
|
|
62
76
|
tituloMayusculas: true,
|
|
63
77
|
subtitulos: {...SUB_BASE, y: 300, color: '#141414', pendiente: 'rgba(20,20,20,0.28)', resalte: ''},
|
|
64
78
|
transicion: 'deslizar',
|
|
79
|
+
movimiento: {entrada: 0.45, salida: 0.25, transicion: 0.42, curva: 'seca'},
|
|
65
80
|
marca: 'esquina',
|
|
66
|
-
zoomLento: 1.
|
|
81
|
+
zoomLento: 1.02,
|
|
67
82
|
},
|
|
68
83
|
// Fondo azul profundo con rayos de luz y burbujas; datos en chips (naturaleza, ciencia, océano)
|
|
69
84
|
documental: {
|
|
@@ -74,10 +89,12 @@ export const ESTILOS: Record<string, Tema> = {
|
|
|
74
89
|
colores: {texto: '#FFFFFF', acento: '#7FE3FF', acento2: '#FF5A5A', suave: 'rgba(255,255,255,0.55)', tarjeta: 'rgba(8,20,40,0.78)', tarjetaTexto: '#FFFFFF', borde: '#7FE3FF'},
|
|
75
90
|
fuentes: {titulo: MONT, texto: MONT, mono: MONO},
|
|
76
91
|
tituloMayusculas: false,
|
|
77
|
-
subtitulos: {...SUB_BASE, color: '#FFFFFF', pendiente: 'rgba(255,255,255,0.
|
|
92
|
+
subtitulos: {...SUB_BASE, tipo: 'seleccion', y: 310, tam: 50, color: '#FFFFFF', pendiente: 'rgba(255,255,255,0.5)', resalte: '#4C8DFF'},
|
|
78
93
|
transicion: 'fundido',
|
|
94
|
+
movimiento: {entrada: 0.6, salida: 0.4, transicion: 0.5, curva: 'suave'},
|
|
95
|
+
post: {tinte: {color: '#1E6BFF', fuerza: 0.14, modo: 'soft-light'}},
|
|
79
96
|
marca: 'esquina',
|
|
80
|
-
zoomLento: 1.
|
|
97
|
+
zoomLento: 1.07,
|
|
81
98
|
},
|
|
82
99
|
// Escenarios ilustrados (bosque, ciudad): el fondo suele ser una imagen tuya; cielo de atardecer por defecto
|
|
83
100
|
ilustrado: {
|
|
@@ -87,8 +104,10 @@ export const ESTILOS: Record<string, Tema> = {
|
|
|
87
104
|
colores: {texto: '#FFFFFF', acento: '#FFE27A', acento2: '#FF6B6B', suave: 'rgba(255,255,255,0.55)', tarjeta: 'rgba(20,14,30,0.75)', tarjetaTexto: '#FFFFFF', borde: '#FFFFFF'},
|
|
88
105
|
fuentes: {titulo: MONT, texto: MONT, mono: MONO},
|
|
89
106
|
tituloMayusculas: false,
|
|
90
|
-
subtitulos: {...SUB_BASE, color: '#FFFFFF', pendiente: 'rgba(255,255,255,0.
|
|
107
|
+
subtitulos: {...SUB_BASE, tipo: 'seleccion', y: 310, tam: 50, color: '#FFFFFF', pendiente: 'rgba(255,255,255,0.5)', resalte: '#4C8DFF'},
|
|
91
108
|
transicion: 'corte',
|
|
109
|
+
movimiento: {entrada: 0.55, salida: 0.3, transicion: 0.4, curva: 'resorte'},
|
|
110
|
+
post: {tinte: {color: '#FF9A5A', fuerza: 0.1, modo: 'soft-light'}},
|
|
92
111
|
marca: 'esquina',
|
|
93
112
|
zoomLento: 1.08,
|
|
94
113
|
},
|
|
@@ -101,10 +120,12 @@ export const ESTILOS: Record<string, Tema> = {
|
|
|
101
120
|
colores: {texto: '#FFFFFF', acento: '#FFD66B', acento2: '#FF4A4A', suave: 'rgba(255,255,255,0.5)', tarjeta: 'rgba(0,0,0,0.6)', tarjetaTexto: '#FFD66B', borde: '#FFD66B'},
|
|
102
121
|
fuentes: {titulo: ANTON, texto: MONT, mono: MONO},
|
|
103
122
|
tituloMayusculas: true,
|
|
104
|
-
subtitulos: {...SUB_BASE, color: '#FFFFFF', pendiente: 'rgba(255,255,255,0.
|
|
123
|
+
subtitulos: {...SUB_BASE, tipo: 'seleccion', y: 310, tam: 50, color: '#FFFFFF', pendiente: 'rgba(255,255,255,0.45)', resalte: '#FFD66B'},
|
|
105
124
|
transicion: 'corte',
|
|
125
|
+
movimiento: {entrada: 0.6, salida: 0.35, transicion: 0.45, curva: 'suave'},
|
|
126
|
+
post: {luz: {x: 700, ancho: 900, largo: 1700, color: '#FFE2A8', fuerza: 0.22, angulo: 12}, vinetaColor: '#05060F'},
|
|
106
127
|
marca: 'esquina',
|
|
107
|
-
zoomLento: 1.
|
|
128
|
+
zoomLento: 1.06,
|
|
108
129
|
},
|
|
109
130
|
// Objetos sobre una mesa con foco de luz, contadores tipo LCD (dinero, crecimiento, comparaciones)
|
|
110
131
|
mesa: {
|
|
@@ -115,10 +136,12 @@ export const ESTILOS: Record<string, Tema> = {
|
|
|
115
136
|
colores: {texto: '#FFFFFF', acento: '#FFD24A', acento2: '#5CFF7A', suave: 'rgba(255,255,255,0.5)', tarjeta: 'rgba(10,8,6,0.85)', tarjetaTexto: '#FFD24A', borde: '#3A2A1A'},
|
|
116
137
|
fuentes: {titulo: ANTON, texto: MONT, mono: MONO},
|
|
117
138
|
tituloMayusculas: true,
|
|
118
|
-
subtitulos: {...SUB_BASE, color: '#FFFFFF', pendiente: 'rgba(255,255,255,0.
|
|
139
|
+
subtitulos: {...SUB_BASE, tipo: 'seleccion', y: 310, tam: 50, color: '#FFFFFF', pendiente: 'rgba(255,255,255,0.45)', resalte: '#4C8DFF'},
|
|
119
140
|
transicion: 'fundido',
|
|
141
|
+
movimiento: {entrada: 0.55, salida: 0.35, transicion: 0.5, curva: 'resorte'},
|
|
142
|
+
post: {luz: {x: 540, ancho: 820, largo: 1500, color: '#FFD9A0', fuerza: 0.3}, desenfoqueBordes: 5, vinetaColor: '#0A0503'},
|
|
120
143
|
marca: 'esquina',
|
|
121
|
-
zoomLento: 1.
|
|
144
|
+
zoomLento: 1.05,
|
|
122
145
|
},
|
|
123
146
|
// El de los shorts clásicos (morado oscuro, Anton, amarillo): para capas dentro de 'datos-curiosos'
|
|
124
147
|
'datos-curiosos': {
|
|
@@ -129,6 +152,7 @@ export const ESTILOS: Record<string, Tema> = {
|
|
|
129
152
|
tituloMayusculas: true,
|
|
130
153
|
subtitulos: {...SUB_BASE, tipo: 'pop', y: 1420, color: '#FFFFFF', pendiente: '#FFFFFF', resalte: ''},
|
|
131
154
|
transicion: 'corte',
|
|
155
|
+
movimiento: {entrada: 0.45, salida: 0.25, transicion: 0.3, curva: 'resorte'},
|
|
132
156
|
marca: 'arriba',
|
|
133
157
|
zoomLento: 1,
|
|
134
158
|
},
|
|
@@ -141,8 +165,9 @@ export const ESTILOS: Record<string, Tema> = {
|
|
|
141
165
|
tituloMayusculas: true,
|
|
142
166
|
subtitulos: {tipo: 'bloque', y: 1080, tam: 62, maxPalabras: 4, color: '#FFFFFF', pendiente: '#FFFFFF', resalte: '#FF3B5C', contorno: 8},
|
|
143
167
|
transicion: 'corte',
|
|
168
|
+
movimiento: {entrada: 0.45, salida: 0.25, transicion: 0.3, curva: 'resorte'},
|
|
144
169
|
marca: 'ninguna',
|
|
145
|
-
zoomLento: 1.
|
|
170
|
+
zoomLento: 1.03,
|
|
146
171
|
},
|
|
147
172
|
// Infografía plana (cuerpo, ciencia, comparaciones): gris claro, encabezado y subtítulos abajo en caja
|
|
148
173
|
infografia: {
|
|
@@ -152,22 +177,25 @@ export const ESTILOS: Record<string, Tema> = {
|
|
|
152
177
|
fuentes: {titulo: MONT, texto: MONT, mono: MONO},
|
|
153
178
|
tituloMayusculas: true,
|
|
154
179
|
subtitulos: {tipo: 'karaoke', y: 1500, tam: 48, maxPalabras: 4, color: '#FFFFFF', pendiente: 'rgba(255,255,255,0.45)', resalte: '', fondo: '#1B1B1F'},
|
|
155
|
-
transicion: '
|
|
180
|
+
transicion: 'empuje',
|
|
181
|
+
movimiento: {entrada: 0.42, salida: 0.25, transicion: 0.45, curva: 'seca'},
|
|
156
182
|
marca: 'esquina',
|
|
157
|
-
zoomLento: 1.
|
|
183
|
+
zoomLento: 1.02,
|
|
158
184
|
},
|
|
159
185
|
// Escenas 3D (construcción, cómo funciona por dentro): una palabra grande a la vez
|
|
160
186
|
'3d': {
|
|
161
187
|
fondo: {tipo: 'degradado', colores: ['#BFE3FF', '#EAF6FF']},
|
|
162
188
|
vineta: 0.1,
|
|
163
189
|
// cielo claro: el texto propio va oscuro; los subtítulos siguen blancos con contorno negro
|
|
164
|
-
colores: {texto: '#14233A', acento: '#
|
|
190
|
+
colores: {texto: '#14233A', acento: '#FFC400', acento2: '#FF5A36', suave: '#4F6278', tarjeta: 'rgba(20,20,20,0.78)', tarjetaTexto: '#FFFFFF', borde: '#141414'},
|
|
165
191
|
fuentes: {titulo: MONT, texto: MONT, mono: MONO},
|
|
166
192
|
tituloMayusculas: true,
|
|
167
|
-
subtitulos: {tipo: '
|
|
193
|
+
subtitulos: {tipo: 'pop', y: 1380, tam: 50, maxPalabras: 3, color: '#FFFFFF', pendiente: '#FFFFFF', resalte: '', contorno: 10},
|
|
168
194
|
transicion: 'corte',
|
|
195
|
+
movimiento: {entrada: 0.5, salida: 0.3, transicion: 0.35, curva: 'resorte'},
|
|
196
|
+
post: {desenfoqueBordes: 4},
|
|
169
197
|
marca: 'esquina',
|
|
170
|
-
zoomLento: 1.
|
|
198
|
+
zoomLento: 1.04,
|
|
171
199
|
},
|
|
172
200
|
// Oscuro y limpio, para lo que sea
|
|
173
201
|
minimal: {
|
|
@@ -178,8 +206,9 @@ export const ESTILOS: Record<string, Tema> = {
|
|
|
178
206
|
tituloMayusculas: false,
|
|
179
207
|
subtitulos: {...SUB_BASE, color: '#FFFFFF', pendiente: 'rgba(255,255,255,0.35)', resalte: '#8B7BFF'},
|
|
180
208
|
transicion: 'fundido',
|
|
209
|
+
movimiento: {entrada: 0.5, salida: 0.3, transicion: 0.4, curva: 'suave'},
|
|
181
210
|
marca: 'esquina',
|
|
182
|
-
zoomLento: 1.
|
|
211
|
+
zoomLento: 1.04,
|
|
183
212
|
},
|
|
184
213
|
};
|
|
185
214
|
|
package/remotion/motor/util.ts
CHANGED
|
@@ -10,6 +10,9 @@ export type CtxEscena = {
|
|
|
10
10
|
tema: Tema;
|
|
11
11
|
semilla: string;
|
|
12
12
|
inicios?: number[]; // inicio de cada escena (para el HUD: cambios en {escena: n})
|
|
13
|
+
escena?: number; // en el `mundo`: número de la escena actual (1 = la primera)
|
|
14
|
+
camara?: {x: number; y: number; zoom: number; giro: number}; // en el `mundo`: la cámara de este cuadro (para paralaje)
|
|
15
|
+
tEscena?: number; // en el `mundo`: segundos desde que empezó la escena actual
|
|
13
16
|
};
|
|
14
17
|
|
|
15
18
|
const norm = (s: string) =>
|
package/remotion/tipos.ts
CHANGED
|
@@ -21,6 +21,8 @@ export type Datos = {
|
|
|
21
21
|
estilo?: string; // 'datos-curiosos' (clásico) o un estilo del motor de capas
|
|
22
22
|
tema?: Record<string, any>; // ajustes del estilo para este video
|
|
23
23
|
hud?: Record<string, any>[]; // capas fijas sobre todo el video (día, barra de progreso, hora...)
|
|
24
|
+
mundo?: Record<string, any>[]; // capas del mundo: persisten todo el video y la cámara las recorre con la `toma` de cada escena
|
|
25
|
+
serie?: string; // pastilla fija arriba: 'GYM CON CIENCIA · PARTE 5'
|
|
24
26
|
audio: string;
|
|
25
27
|
whisper: number;
|
|
26
28
|
escenas: Escena[];
|