primocode 10.13.0 → 10.14.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/README.md +66 -12
- package/bin/primocode.js +148 -8
- package/lib/act.js +56 -3
- package/lib/catalogo.js +12 -1
- package/lib/config.js +1 -0
- package/lib/estudio/cota.js +157 -0
- package/lib/estudio/dados/projetos/clube-ad5.json +105 -0
- package/lib/estudio/dados/projetos/clube-ae-sem-memoria.json +107 -0
- package/lib/estudio/dados/projetos/clube-agent.json +120 -0
- package/lib/estudio/dados/projetos/clube-antes-e-agora.json +86 -0
- package/lib/estudio/dados/projetos/clube-apresentando-clube.json +110 -0
- package/lib/estudio/dados/projetos/clube-bluevisa.json +106 -0
- package/lib/estudio/dados/projetos/clube-caught-in-the-fire.json +127 -0
- package/lib/estudio/dados/projetos/clube-chat-cliente-satisfeito.json +98 -0
- package/lib/estudio/dados/projetos/clube-chatgpt-anuncios.json +120 -0
- package/lib/estudio/dados/projetos/clube-claude-dj.json +140 -0
- package/lib/estudio/dados/projetos/clube-claude-mcp.json +142 -0
- package/lib/estudio/dados/projetos/clube-cleanui.json +106 -0
- package/lib/estudio/dados/projetos/clube-clinica-take-unico.json +128 -0
- package/lib/estudio/dados/projetos/clube-clinica.json +104 -0
- package/lib/estudio/dados/projetos/clube-clube-opener.json +125 -0
- package/lib/estudio/dados/projetos/clube-clube.json +108 -0
- package/lib/estudio/dados/projetos/clube-codigo.json +101 -0
- package/lib/estudio/dados/projetos/clube-distilbook.json +104 -0
- package/lib/estudio/dados/projetos/clube-drive-upload.json +115 -0
- package/lib/estudio/dados/projetos/clube-dub.json +119 -0
- package/lib/estudio/dados/projetos/clube-enxovaly.json +105 -0
- package/lib/estudio/dados/projetos/clube-fecha7.json +118 -0
- package/lib/estudio/dados/projetos/clube-filatov.json +128 -0
- package/lib/estudio/dados/projetos/clube-frame.json +108 -0
- package/lib/estudio/dados/projetos/clube-glass.json +118 -0
- package/lib/estudio/dados/projetos/clube-letra-em-apps.json +84 -0
- package/lib/estudio/dados/projetos/clube-letra-globo-3d.json +111 -0
- package/lib/estudio/dados/projetos/clube-letra-nos-apps.json +146 -0
- package/lib/estudio/dados/projetos/clube-letra-seis-mundos.json +100 -0
- package/lib/estudio/dados/projetos/clube-lula-3.json +146 -0
- package/lib/estudio/dados/projetos/clube-morph.json +119 -0
- package/lib/estudio/dados/projetos/clube-motion-designer.json +115 -0
- package/lib/estudio/dados/projetos/clube-motion-pacote.json +112 -0
- package/lib/estudio/dados/projetos/clube-movimento.json +103 -0
- package/lib/estudio/dados/projetos/clube-not-like-us.json +117 -0
- package/lib/estudio/dados/projetos/clube-pinterest-inspiracao.json +86 -0
- package/lib/estudio/dados/projetos/clube-pixel-e-tinta.json +108 -0
- package/lib/estudio/dados/projetos/clube-ponte.json +118 -0
- package/lib/estudio/dados/projetos/clube-processo-edit-tonal.json +126 -0
- package/lib/estudio/dados/projetos/clube-processo-motion-100-ia.json +109 -0
- package/lib/estudio/dados/projetos/clube-promo.json +114 -0
- package/lib/estudio/dados/projetos/clube-proposito.json +115 -0
- package/lib/estudio/dados/projetos/clube-rankhog.json +128 -0
- package/lib/estudio/dados/projetos/clube-recursao.json +116 -0
- package/lib/estudio/dados/projetos/clube-revelar.json +118 -0
- package/lib/estudio/dados/projetos/clube-robo.json +125 -0
- package/lib/estudio/dados/projetos/clube-shelby-ep2.json +144 -0
- package/lib/estudio/dados/projetos/clube-shelby.json +97 -0
- package/lib/estudio/dados/projetos/clube-solar-eclipse.json +96 -0
- package/lib/estudio/dados/projetos/clube-soldier.json +128 -0
- package/lib/estudio/dados/projetos/clube-som-de-cena.json +108 -0
- package/lib/estudio/dados/projetos/clube-spotify-candidatura.json +112 -0
- package/lib/estudio/dados/projetos/clube-tda-app.json +118 -0
- package/lib/estudio/dados/projetos/clube-tda-ia.json +75 -0
- package/lib/estudio/dados/projetos/clube-telegram-chat.json +118 -0
- package/lib/estudio/dados/projetos/clube-telegram-ilha.json +106 -0
- package/lib/estudio/dados/projetos/clube-tennessee-nike.json +132 -0
- package/lib/estudio/dados/projetos/clube-tudo-se-move.json +112 -0
- package/lib/estudio/dados/projetos/clube-uiad.json +107 -0
- package/lib/estudio/dados/projetos/clube-viralgrow.json +108 -0
- package/lib/estudio/dados/projetos/clube-whose-side.json +140 -0
- package/lib/estudio/dados/projetos/clube-youtube-inscrever.json +108 -0
- package/lib/estudio/dados/projetos/elementos-do-site-em-movimento.json +49 -0
- package/lib/estudio/dados/projetos/explainer-narrado.json +51 -0
- package/lib/estudio/dados/projetos/lancamento-de-produto.json +51 -0
- package/lib/estudio/dados/projetos/motion-so-com-sons.json +50 -0
- package/lib/estudio/dados/projetos/titulo-em-after-effects.json +51 -0
- package/lib/estudio/dados/projetos/tour-de-saas.json +50 -0
- package/lib/estudio/dados/projetos-ae/antes-e-agora/ae/cenas.jsx +733 -0
- package/lib/estudio/dados/projetos-ae/antes-e-agora/assets/fazer_assets.py +88 -0
- package/lib/estudio/dados/projetos-ae/letra-em-apps/ae/cenas.jsx +402 -0
- package/lib/estudio/dados/projetos-ae/letra-em-apps/assets/preparar_assets.py +17 -0
- package/lib/estudio/dados/projetos-ae/letra-em-apps/assets/svg_para_ae.py +25 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/BRIEF-AGENTES.md +31 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/README.md +53 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ROTEIRO-CENAS.md +69 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/A-agenda.jsx +148 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/B-docs.jsx +174 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/C-alttab.jsx +113 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/D-spotify.jsx +118 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/E-notas.jsx +146 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/F-jornal1.jsx +144 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/G-google.jsx +153 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/H-mapa.jsx +176 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/I-imagens.jsx +132 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/J-jornal2.jsx +191 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/K-maps.jsx +184 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/camera-movimentos.jsx +48 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/camera.jsx +57 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/card-spotify.jsx +98 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/digitar.jsx +38 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/jornal-comum.jsx +196 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/letra.jsx +23 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/letra_automatica.py +83 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/montagem.jsx +74 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/w-agenda/render_html.py +13 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/w-agenda/util.jsx +162 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/w-midia-comum.jsx +114 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/ae/wg-util.jsx +360 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/assets/F-jornal1/render_html.py +13 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/assets/render_w-midia.py +26 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/assets/w-midia-ferramentas/inclin.py +30 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/assets/w-midia-ferramentas/medir_d.py +36 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/assets/w-midia-ferramentas/medir_e.py +27 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/ajustar_movimentos.py +61 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/ancorar.py +74 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/assar.py +47 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/camera3d_jornal.py +172 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/comparar.py +32 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/dados_ae_jornal.py +46 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/folha_contato.py +28 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/homografia_jornal.py +74 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/integrar.py +44 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/medir_quadro_a_quadro.py +32 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/mesa_2d.py +64 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/plot_cenas.py +11 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/retificar_quadros.py +38 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/w-agenda/assar_ab.py +52 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/w-agenda/medir_ab.py +59 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/wg_ajustar_h.py +111 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/wg_corrige_cam.py +17 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/wg_ecc_cadeia.py +80 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/wg_finalizar_cam.py +65 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/camera/wg_mosaico.py +61 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces/01-gramatica-e-roteiro.md +55 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces/02-camera.md +66 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces/03-montagem-no-ae.md +50 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces/04-render-e-conferencia.md +43 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces/05-abertura-celular-mergulho.md +43 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/guia-do-metodo/tutorial-lyric-edit-interfaces.md +42 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/atraso_imagem.py +31 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/fundo.py +56 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/juntar.py +45 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/montar.py +177 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/rastreio.py +74 -0
- package/lib/estudio/dados/projetos-ae/letra-nos-apps/transicao/sincronia.py +18 -0
- package/lib/estudio/dados/projetos-ae/pinterest-inspiracao/ae/cenas.jsx +530 -0
- package/lib/estudio/dados/projetos-ae/pinterest-inspiracao/assets/fazer_assets.py +107 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/assets/pessoas/figurinha.py +46 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/BRIEF-A.md +83 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/audio.py +179 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/confere.py +20 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/dados/tempo-parte1.js +1 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/dados/tempo-parte2.js +1 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/gerar_emojis.py +11 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/img/filmes/_indice.js +122 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/base.js +380 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/main.js +86 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/p1a.js +169 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/p1b.js +455 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/p1c.js +389 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/js/p2.js +379 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/comp-a/video.py +62 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/take-unico/README.md +65 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/take-unico/oraculo_take.py +137 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/take-unico/render_take.py +388 -0
- package/lib/estudio/dados/projetos-ae/processo-motion-100-ia/take-unico/take.js +235 -0
- package/lib/estudio/editor.js +290 -0
- package/lib/estudio/effectcraft.js +152 -0
- package/lib/estudio/exportar.js +181 -0
- package/lib/estudio/index.js +97 -2
- package/lib/estudio/kit-ae/base.jsx +1146 -0
- package/lib/estudio/kit-ae/cenas.jsx +395 -0
- package/lib/estudio/kit-ae/primitivas.jsx +911 -0
- package/lib/estudio/maquina-diretor.js +393 -0
- package/lib/estudio/maquina-elementos.js +134 -0
- package/lib/estudio/maquina-layout.js +198 -0
- package/lib/estudio/maquina-som.js +478 -0
- package/lib/estudio/maquina-trilha.js +160 -0
- package/lib/estudio/maquina-voz.js +429 -0
- package/lib/estudio/maquina.js +516 -0
- package/lib/estudio/projetos.js +72 -0
- package/lib/headless.js +71 -0
- package/lib/hooks.js +72 -0
- package/lib/limites.js +100 -0
- package/lib/local/cerebro.json +398 -12
- package/lib/local/entrada.js +46 -2
- package/lib/marca-kit.js +312 -0
- package/lib/mcp.js +286 -6
- package/lib/perigo.js +31 -3
- package/lib/planos.js +49 -19
- package/lib/preparar.js +111 -0
- package/lib/retomar.js +55 -0
- package/lib/sandbox.js +197 -0
- package/lib/skills.js +3 -1
- package/lib/tools.js +13 -4
- package/lib/ui.js +1 -1
- package/package.json +4 -2
- package/skills/after-effects/SKILL.md +52 -0
- package/skills/maquina-de-motion/SKILL.md +84 -0
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
/* p2.js — Parte 2 inteira (0–96,3 s) no MODO PALCO (STORYBOARD-B.md): legenda curta embaixo e, no palco, o elemento que
|
|
2
|
+
* desenha a palavra-chave de cada frase (leve, intuitivo, bem-humorado). Coluna escondida a parte inteira.
|
|
3
|
+
* 23 herói ✨ "4" · 24 quebra-cabeça com a peça errada 🤦 → 🧭 perdida → grade de clones 🥱 · 25 notificações reais + Elon →
|
|
4
|
+
* linha do tempo "no tempo certo" → 🤔 com "?" · 26 ✏️ desenha a onda e os ícones nascem nos picos · 27 só texto ·
|
|
5
|
+
* tesla (logos, Elon, "ontem", voo real com som) · 28 🧠 assimila → curva de atenção 🍿/😴 · 29 os 4 passos → pacote →
|
|
6
|
+
* enviado ao Claude → 99% e o 1% que pula 🔥 · 30–31 dock: cada ferramenta pula no instante falado (cadeado, Remotion cinza,
|
|
7
|
+
* prints reais) · 32 Claude com haltere 💪 → roteiro.md escrito a mão · 33 DM do Instagram. */
|
|
8
|
+
(function () {
|
|
9
|
+
const ELON = '../assets/pessoas/elon-musk-04-2025-09-3x4.jpg';
|
|
10
|
+
const SANS = 'GS, sans-serif';
|
|
11
|
+
const caixa = (pai, css, html) => $('div', null, pai, Object.assign({ position: 'absolute' }, css), html);
|
|
12
|
+
function emo(pai, id, cx, cy, tam) { const d = caixa(pai, { left: (cx - tam / 2) + 'px', top: (cy - tam / 2) + 'px', width: tam + 'px', height: tam + 'px' }); d.lot = emojiLot(d, id, tam); return d; }
|
|
13
|
+
function ima(pai, src, cx, cy, tam, css = {}) { const i = $('img', null, pai, Object.assign({ position: 'absolute', left: (cx - tam / 2) + 'px', top: (cy - tam / 2) + 'px', width: tam + 'px', height: tam + 'px', objectFit: 'contain' }, css)); i.src = src; return i; }
|
|
14
|
+
const fotoRedonda = (pai, cx, cy, tam) => ima(pai, ELON, cx, cy, tam, { objectFit: 'cover', objectPosition: '50% 22%', borderRadius: '50%', boxShadow: '0 18px 40px rgba(20,20,30,.18)', border: '6px solid #fff' });
|
|
15
|
+
// amplia: tudo da cena cresce em volta do centro do palco (s) e desce (dy) para ficar perto da legenda
|
|
16
|
+
const amplia = (el, s, dy = 0, oy = PALCO_Y) => caixa(el, { left: '0', top: '0', width: '1080px', height: '1920px', transformOrigin: `540px ${oy}px`, transform: `translate(0px,${dy}px) scale(${s})` });
|
|
17
|
+
const vivo = (t, k = 0, a = 6) => ({ x: a * Math.sin(t * 1.1 + k * 1.7), y: a * 0.8 * Math.cos(t * 0.9 + k * 2.3), r: 1.5 * Math.sin(t * 0.8 + k) });
|
|
18
|
+
function cartaoBranco(pai, css, html) { return caixa(pai, Object.assign({ background: '#fff', borderRadius: '40px', boxShadow: '0 30px 70px -20px rgba(40,30,20,.22), 0 0 0 1px rgba(0,0,0,.04)', overflow: 'hidden', fontFamily: SANS }, css), html); }
|
|
19
|
+
|
|
20
|
+
ocultaColuna(0, 96.3, 0.01);
|
|
21
|
+
|
|
22
|
+
// ── abre: "Parte 2" → herói do passo 4 ──
|
|
23
|
+
palco(0.15, 1.35, (el, P) => { P.tx = caixa(el, { left: '0', right: '0', top: (PALCO_Y + 60) + 'px', textAlign: 'center', font: `600 110px/1 ${SANS}`, letterSpacing: '-0.04em', color: 'var(--cinza)' }, 'Parte 2'); }, (t, P) => { tf(P.tx, { y: -6 * (t - P.t0) }); });
|
|
24
|
+
passoHeroi(4, 'u2728', F0('23') - 0.08, PF('23', 'elementos', true) + 0.3, { ate: F0('29') - 0.3, rotulo: 'Elementos' });
|
|
25
|
+
legenda('23', [pula(3)]);
|
|
26
|
+
|
|
27
|
+
// ── 24 · "Aqui é uma das partes mais importantes, que muita gente erra." ──
|
|
28
|
+
legenda('24', [
|
|
29
|
+
'Aqui é uma das partes', 'mais f|importantes,', ['que muita gente', 'f|erra.', emoji('u1f926-200d-2642')],
|
|
30
|
+
'Em muitos a|vídeos que a', 'o|inteligência o|artificial faz,', 'quando você não dá o', 'f|direcionamento', 'correto para ela,',
|
|
31
|
+
'o resultado sai', 'muito bem feito,', ['porém muito', 'c|genérico.', emoji('u1f971')],
|
|
32
|
+
]);
|
|
33
|
+
// quebra-cabeça: 3 peças encaixam (partes importantes); a peça errada é forçada no último lugar, treme e não entra
|
|
34
|
+
palco(F0('24') - 0.05, PF('24', 'Em') - 0.1, (el, P) => {
|
|
35
|
+
el = amplia(el, 1.55, 60);
|
|
36
|
+
const L = 250, x0 = 540 - L, y0 = PALCO_Y - L - 20;
|
|
37
|
+
const cores = ['#2F55F4', '#D97757', '#2BB07F'];
|
|
38
|
+
const peca = (cor, kn) => `<svg width="${L + 80}" height="${L + 80}" viewBox="-40 -40 ${L + 80} ${L + 80}" style="position:absolute;left:-40px;top:-40px;overflow:visible"><rect x="0" y="0" width="${L}" height="${L}" rx="26" fill="${cor}"/>${kn.map(([cx, cy]) => `<circle cx="${cx}" cy="${cy}" r="38" fill="${cor}"/>`).join('')}</svg>`;
|
|
39
|
+
P.slot = caixa(el, { left: (x0 + L) + 'px', top: (y0 + L) + 'px', width: L + 'px', height: L + 'px', borderRadius: '26px', border: '6px dashed #C9C6BE', boxSizing: 'border-box' });
|
|
40
|
+
P.ps = [[x0, y0, [[L, L / 2], [L / 2, L]]], [x0 + L, y0, [[L / 2, L]]], [x0, y0 + L, [[L, L / 2]]]].map(([x, y, kn], i) => { const d = caixa(el, { left: x + 'px', top: y + 'px', width: L + 'px', height: L + 'px' }, peca(cores[i], kn)); d.t = F0('24') + 0.15 + i * 0.45; ev(d.t + 0.3, 'pouso', (i - 1) * 0.3, 0.25); return d; });
|
|
41
|
+
P.errada = caixa(el, { left: (x0 + L) + 'px', top: (y0 + L) + 'px', width: L + 'px', height: L + 'px' }, `<svg width="${L + 80}" height="${L + 80}" viewBox="-40 -40 ${L + 80} ${L + 80}" style="position:absolute;left:-40px;top:-40px;overflow:visible"><path d="M0 26 Q0 0 26 0 L${L - 26} 0 Q${L} 0 ${L} 26 L${L} ${L} L0 ${L} Z" fill="#8F8B83"/><path d="M-70 ${L / 2 - 46} L-4 ${L / 2} L-70 ${L / 2 + 46} Z" fill="#8F8B83"/><path d="M${L / 2 - 50} -4 L${L / 2} -66 L${L / 2 + 50} -4 Z" fill="#8F8B83"/></svg>`);
|
|
42
|
+
P.tE = PF('24', 'que') - 0.05; P.tErra = PF('24', 'erra');
|
|
43
|
+
P.fp = emo(el, 'u1f926-200d-2642', 540 + 190, y0 - 10, 190);
|
|
44
|
+
P.lb = caixa(el, { left: (x0 + L + 20) + 'px', top: (y0 + 2 * L + 34) + 'px', font: `600 34px/1 ${SANS}`, color: '#E5484D' }, 'não encaixa');
|
|
45
|
+
ev(P.tE + 0.45, 'tranco', 0.2, 0.35); ev(P.tErra, 'erro', 0, 0.3);
|
|
46
|
+
}, (t, P) => {
|
|
47
|
+
P.ps.forEach((d, i) => { const n = nasce(t, d.t, 0.5); const v = vivo(t, i, 2); tf(d, { op: n.op, x: (1 - n.k) * [-160, 160, -160][i] + v.x, y: (1 - n.k) * [-120, -120, 140][i] + v.y, r: (1 - n.k) * [-20, 18, 14][i], blur: n.blur }); });
|
|
48
|
+
tf(P.slot, { op: seg(t, P.ps[2].t, P.ps[2].t + 0.3) });
|
|
49
|
+
const e = ILL(seg(t, P.tE, P.tE + 0.45)), forca = seg(t, P.tE + 0.45, P.tE + 2.2), tr = forca > 0 && forca < 1 ? 1 : 0;
|
|
50
|
+
const shake = tr * (7 * Math.sin(t * 61) + 4 * Math.sin(t * 37));
|
|
51
|
+
tf(P.errada, { op: clamp(e * 3), x: lerp(340, 46, e) + shake, y: lerp(260, 30, e) + shake * 0.4, r: lerp(40, 9, e) + tr * 2.5 * Math.sin(t * 45), blur: (1 - e) * 12 });
|
|
52
|
+
{ const r = palavraVida(t, P.tErra + 0.15, false); tf(P.lb, { op: r.op, y: r.y + shake * 0.3, blur: r.blur }); }
|
|
53
|
+
const k = nasce(t, P.tErra + 0.05, 0.5); tf(P.fp, { op: k.op, s: k.s, y: k.y, blur: k.blur }); P.fp.lot.toca(t, P.tErra);
|
|
54
|
+
});
|
|
55
|
+
// 🧭 perdida: gira para um lado e para o outro, deixando um rastro que se embola
|
|
56
|
+
palco(PF('24', 'Em') - 0.1, PF('24', 'o', 1) - 0.12, (el, P) => {
|
|
57
|
+
el = amplia(el, 1.9, 60);
|
|
58
|
+
P.sv = caixa(el, { left: '0', top: '0', width: '1080px', height: '1400px' }, `<svg width="1080" height="1400" viewBox="0 0 1080 1400"><path id="rastro" d="M540 ${PALCO_Y} C640 640 720 700 690 820 S470 910 440 790 S540 610 660 690 S700 910 560 915 S405 820 470 690" fill="none" stroke="#C9C6BE" stroke-width="5" stroke-linecap="round" stroke-dasharray="2 22"/></svg>`);
|
|
59
|
+
P.r = P.sv.querySelector('#rastro'); P.c = emo(el, 'u1f9ed', 540, PALCO_Y, 360);
|
|
60
|
+
P.lb = caixa(el, { left: '0', right: '0', top: (PALCO_Y + 200) + 'px', textAlign: 'center', font: `600 26px/1 ${SANS}`, color: 'var(--cinza)' }, 'sem direção');
|
|
61
|
+
P.tD = PF('24', 'direcionamento'); ev(P.tD, 'tempo', 0, 0.3); ev(P.t0 + 0.6, 'tempo', -0.2, 0.2);
|
|
62
|
+
}, (t, P) => {
|
|
63
|
+
const L = P.r.getTotalLength(), u = seg(t, P.t0 + 0.3, P.t1 - 0.2); P.r.style.strokeDashoffset = '0'; P.r.setAttribute('stroke-dasharray', `2 22`);
|
|
64
|
+
P.r.style.clipPath = ''; P.r.style.opacity = clamp(u * 3);
|
|
65
|
+
P.r.setAttribute('stroke-dashoffset', '0'); P.r.style.strokeDasharray = `${(L * u).toFixed(1)} ${L}`;
|
|
66
|
+
const giro = 40 * Math.sin((t - P.t0) * 2.1) + 25 * Math.sin((t - P.t0) * 5.3) + 900 * ILL(seg(t, P.tD - 0.05, P.tD + 0.9));
|
|
67
|
+
{ const r = palavraVida(t, P.tD + 0.2, false); tf(P.lb, { op: r.op, y: r.y, blur: r.blur }); }
|
|
68
|
+
tf(P.c, { r: giro, x: 22 * Math.sin((t - P.t0) * 1.3), y: 14 * Math.sin((t - P.t0) * 1.9) }); P.c.lot.toca(t, P.t0);
|
|
69
|
+
});
|
|
70
|
+
// genérico: grade de clones do mesmo vídeo; em "genérico" perde a cor e 🥱
|
|
71
|
+
palco(PF('24', 'o', 1) - 0.12, F0('25') - 0.1, bloco({ h: 531, kind: 'abre',
|
|
72
|
+
monta(el, B) { B.cs = []; for (let i = 0; i < 9; i++) { const c = $('div', 'janela', el, { inset: 'auto', left: (i % 3) * 311 + 'px', top: Math.floor(i / 3) * 184 + 'px', width: '290px', height: '163px', borderRadius: '18px', background: '#fff' }); c.f = filme(c, 'generico-dub'); B.cs.push(c); }
|
|
73
|
+
B.bo = emo(el, 'u1f971', 912 - 60, -50, 260); B.tg = PF('24', 'generico'); },
|
|
74
|
+
seek(t, B) { const kg = ILL(seg(t, B.tg, B.tg + 0.5));
|
|
75
|
+
B.cs.forEach((c, i) => { const ti = B.t + [0, 1, 2, 1, 2, 3, 2, 3, 4][i] * 0.07, n = nasce(t, ti, 0.4); tf(c, { op: n.op * (1 - 0.3 * kg), s: lerp(0.7, 1, n.k) * (1 - 0.03 * kg), y: n.y, blur: n.blur });
|
|
76
|
+
c.f.toca(t, B.t); c.f.e.style.filter = `grayscale(${kg.toFixed(3)})`; });
|
|
77
|
+
const n = nasce(t, B.tg + 0.1, 0.5); tf(B.bo, { op: n.op, s: n.s, y: n.y, blur: n.blur }); B.bo.lot.toca(t, B.tg); } }), { y: 840 });
|
|
78
|
+
[0, 1, 2, 3, 4].forEach(k => ev(PF('24', 'o', 1) + k * 0.07, 'pop', (k - 2) * 0.3, 0.16));
|
|
79
|
+
|
|
80
|
+
// ── 25 · WhatsApp, Facebook, Instagram e o Elon (a parte que ele mais gostou) ──
|
|
81
|
+
legenda('25', [
|
|
82
|
+
'Por exemplo, se eu', 'estiver falando de', [logo('whatsapp-icon.svg', { gira: false }), 'WhatsApp', 'conectando com'],
|
|
83
|
+
[logo('facebook-icon.svg', { gira: false }), 'Facebook,'], [logo('instagram-icon.svg', { gira: false }), 'Instagram'], 'e o f|Elon Musk,',
|
|
84
|
+
'no seu vídeo, cada', 'elemento, ícone e imagem', 'tem que aparecer', 'f|corretamente', 'no tempo em que', 'está sendo falado.',
|
|
85
|
+
'Senão, as pessoas', ['não vão', 'c|entender.'],
|
|
86
|
+
]);
|
|
87
|
+
// tela de bloqueio: as notificações reais chegam no instante de cada app, com o som de cada um; o Elon aparece no nome dele
|
|
88
|
+
const N = [
|
|
89
|
+
{ t: PF('25', 'WhatsApp') - 0.06, app: 'WHATSAPP', ic: 'whatsapp-icon.svg', tit: 'Cliente novo', msg: 'Oi! Vi seu vídeo e quero um igual 😍', som: 'notif-whatsapp' },
|
|
90
|
+
{ t: PF('25', 'Facebook') - 0.06, app: 'FACEBOOK', ic: 'facebook-icon.svg', tit: 'Ana Souza', msg: 'comentou: "que edição!"', som: 'notif-facebook' },
|
|
91
|
+
{ t: PF('25', 'Instagram') - 0.06, app: 'INSTAGRAM', ic: 'instagram-icon.svg', tit: 'elonmusk', msg: 'começou a seguir você.', som: 'notif-instagram', foto: ELON },
|
|
92
|
+
];
|
|
93
|
+
N.forEach(n => ev(n.t, n.som, 0, 0.7));
|
|
94
|
+
palco(PF('25', 'Por') - 0.1, PF('25', 'cada') - 0.05, (el, P) => {
|
|
95
|
+
// um iPhone de verdade (moldura, ilha, papel de parede), grande; as notificações chegam dentro dele
|
|
96
|
+
const FX = 540 - 310, FY = 110, FW = 620, FH = 1110;
|
|
97
|
+
P.tel = caixa(el, { left: FX + 'px', top: FY + 'px', width: FW + 'px', height: FH + 'px', borderRadius: '112px', background: '#111', boxShadow: '0 50px 100px -30px rgba(20,20,40,.45), 0 0 0 3px #2a2a2e inset' });
|
|
98
|
+
P.tela = caixa(P.tel, { left: '16px', top: '16px', right: '16px', bottom: '16px', borderRadius: '98px', overflow: 'hidden', background: 'radial-gradient(120% 80% at 20% 10%, #9FB7FF 0%, #C8B5FF 38%, #F6C9D9 70%, #FFE3C8 100%)' });
|
|
99
|
+
caixa(P.tela, { left: '50%', top: '22px', width: '190px', height: '56px', marginLeft: '-95px', borderRadius: '30px', background: '#000' });
|
|
100
|
+
caixa(P.tela, { left: '0', right: '0', top: '120px', textAlign: 'center', font: `600 34px/1 ${SANS}`, color: 'rgba(255,255,255,.92)' }, 'sexta-feira, 17 de outubro');
|
|
101
|
+
P.hora = caixa(P.tela, { left: '0', right: '0', top: '160px', textAlign: 'center', font: `700 190px/1 ${SANS}`, letterSpacing: '-0.05em', color: 'rgba(255,255,255,.96)' }, '9:41');
|
|
102
|
+
N.forEach(n => { n.el = caixa(P.tela, { left: '20px', top: '0', width: '548px', height: '146px', borderRadius: '40px', background: 'rgba(255,255,255,.78)', backdropFilter: 'blur(24px) saturate(1.6)', boxShadow: '0 14px 34px rgba(30,30,60,.16)', display: 'flex', alignItems: 'center', gap: '20px', padding: '0 24px', boxSizing: 'border-box', fontFamily: SANS },
|
|
103
|
+
`<div style="position:relative;width:78px;height:78px;flex:none"><img src="${LOGO(n.ic)}" style="width:78px;height:78px;border-radius:18px;object-fit:cover">${n.foto ? `<img src="${n.foto}" style="position:absolute;right:-8px;bottom:-8px;width:42px;height:42px;border-radius:50%;object-fit:cover;object-position:50% 22%;border:3px solid #fff">` : ''}</div>
|
|
104
|
+
<div style="flex:1;min-width:0"><div style="display:flex;justify-content:space-between;font:600 22px ${SANS};color:#6E6E78;letter-spacing:.6px"><span>${n.app}</span><span style="letter-spacing:0">agora</span></div>
|
|
105
|
+
<div style="font:700 32px ${SANS};color:#111;margin-top:2px">${n.tit}</div><div style="font:500 28px ${SANS};color:#333;white-space:nowrap;overflow:hidden;text-overflow:ellipsis">${n.msg}</div></div>`); });
|
|
106
|
+
// o Elon, no nome dele: a foto sobe de dentro do telefone e passa para fora da moldura, grande
|
|
107
|
+
P.elon = fotoRedonda(el, 830, 1010, 270); P.tElon = PF('25', 'Elon') - 0.06; ev(P.tElon, 'pop', 0, 0.3);
|
|
108
|
+
P.nome = caixa(el, { left: '630px', width: '400px', top: '1170px', textAlign: 'center', font: `700 46px/1 ${SANS}`, color: 'var(--tinta)', textShadow: '0 2px 18px rgba(255,255,255,.9)' }, 'Elon Musk');
|
|
109
|
+
}, (t, P) => {
|
|
110
|
+
tf(P.hora, { op: 1 - 0.35 * ILL(seg(t, N[0].t, N[0].t + 0.4)), y: -18 * ILL(seg(t, N[0].t, N[0].t + 0.4)) });
|
|
111
|
+
N.forEach((n, i) => { const u = seg(t, n.t - 0.04, n.t + 0.5), k = ILL(u), dt = Math.max(0, t - n.t), mola = 1 - Math.exp(-7 * dt) * Math.cos(dt * 13);
|
|
112
|
+
let desce = 0; N.slice(i + 1).forEach(m => { desce += ILL(seg(t, m.t - 0.04, m.t + 0.42)) * 166; });
|
|
113
|
+
tf(n.el, { y: lerp(260, 420, clamp(mola, 0, 1.06)) + desce, s: lerp(0.9, 1, k), op: clamp(u * 4), blur: Math.pow(1 - k, 2) * 14 }); });
|
|
114
|
+
const tw = Math.exp(-Math.pow((t - N[N.length - 1].t - 0.05) / 0.06, 2)) + Math.exp(-Math.pow((t - N[0].t - 0.05) / 0.06, 2)) + Math.exp(-Math.pow((t - N[1].t - 0.05) / 0.06, 2));
|
|
115
|
+
tf(P.tel, { r: 1.2 * Math.sin(t * 0.7) + 1.4 * tw * Math.sin(t * 70), y: 6 * Math.sin(t * 0.9) }); // o telefone vibra a cada notificação
|
|
116
|
+
const e = nasce(t, P.tElon, 0.5), v = vivo(t, 3, 4); tf(P.elon, { op: e.op, s: e.s, y: e.y + v.y, x: v.x, blur: e.blur });
|
|
117
|
+
const nm = palavraVida(t, P.tElon + 0.2, false); tf(P.nome, { op: nm.op, y: nm.y, blur: nm.blur });
|
|
118
|
+
});
|
|
119
|
+
// "no tempo em que está sendo falado": a onda da fala com o cursor de reprodução; cada ícone acende quando o cursor passa
|
|
120
|
+
palco(PF('25', 'cada') - 0.05, PF('25', 'Senão') - 0.12, (el, P) => {
|
|
121
|
+
el = amplia(el, 1.12, 70);
|
|
122
|
+
P.c = cartaoBranco(el, { left: '70px', top: (PALCO_Y - 150) + 'px', width: '940px', height: '300px' });
|
|
123
|
+
let bars = ''; for (let i = 0; i < 60; i++) { const h = 20 + 90 * Math.abs(Math.sin(i * 0.9) * Math.cos(i * 0.37)) + 20 * ((i * 7) % 3); bars += `<rect x="${40 + i * 14.4}" y="${190 - h / 2}" width="8" height="${h}" rx="4" fill="#D9D6CE"/>`; }
|
|
124
|
+
P.c.innerHTML = `<svg width="940" height="300" style="position:absolute;inset:0">${bars}</svg>`;
|
|
125
|
+
P.ph = caixa(P.c, { left: '0', top: '50px', width: '6px', height: '260px', borderRadius: '3px', background: 'var(--az)' });
|
|
126
|
+
P.marks = [{ x: 190, src: LOGO('whatsapp-icon.svg') }, { x: 470, src: LOGO('instagram-icon.svg') }, { x: 750, src: ELON, foto: true }].map((m, i) => {
|
|
127
|
+
m.el = ima(el, m.src, 70 + m.x, PALCO_Y - 250, 160, m.foto ? { objectFit: 'cover', objectPosition: '50% 22%', borderRadius: '50%', border: '5px solid #fff', boxShadow: '0 12px 30px rgba(20,20,30,.16)' } : {}); return m; });
|
|
128
|
+
P.ta = P.t0 + 0.35; P.tb = P.t1 - 0.4; P.marks.forEach(m => { m.t = P.ta + (P.tb - P.ta) * (m.x - 40) / 860; ev(m.t, 'pop', (m.x - 470) / 500, 0.25); });
|
|
129
|
+
ev(P.ta, 'playhead', 0, 0.2);
|
|
130
|
+
}, (t, P) => {
|
|
131
|
+
const u = seg(t, P.ta, P.tb), x = 40 + 860 * u; P.ph.style.transform = `translateX(${x.toFixed(1)}px)`;
|
|
132
|
+
P.marks.forEach((m, i) => { const n = nasce(t, m.t, 0.45), v = vivo(t, i, 3); tf(m.el, { op: n.op, s: n.s, y: n.y + v.y, blur: n.blur }); });
|
|
133
|
+
});
|
|
134
|
+
// "não vão entender": 🤔 com pontos de interrogação girando em volta
|
|
135
|
+
palco(PF('25', 'Senão') - 0.12, F0('26') - 0.1, (el, P) => {
|
|
136
|
+
el = amplia(el, 1.9, 70);
|
|
137
|
+
P.p = emo(el, 'u1f914', 540, PALCO_Y, 340);
|
|
138
|
+
P.q = [0, 1, 2].map(i => caixa(el, { left: '0', top: '0', font: `700 ${[120, 90, 100][i]}px/1 ${SANS}`, color: i === 1 ? 'var(--az)' : 'var(--cinza)' }, '?'));
|
|
139
|
+
P.tq = PF('25', 'entender') - 0.1; ev(P.tq, 'pop', 0, 0.25);
|
|
140
|
+
}, (t, P) => {
|
|
141
|
+
P.p.lot.toca(t, P.t0); tf(P.p, { r: 5 * Math.sin((t - P.t0) * 2) });
|
|
142
|
+
P.q.forEach((q, i) => { const a = (t - P.t0) * 2.2 + i * 2.09, n = nasce(t, P.tq + i * 0.1, 0.4);
|
|
143
|
+
tf(q, { x: 540 - 30 + 215 * Math.cos(a), y: PALCO_Y - 60 + 150 * Math.sin(a), r: 25 * Math.sin(a * 2), op: n.op, s: n.s, blur: n.blur }); });
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
// ── 26 · "O seu vídeo tem que ser desenhado com base no que está sendo falado no seu áudio. Entendeu?" ──
|
|
147
|
+
legenda('26', ['O seu a|vídeo tem que ser', ['f|desenhado', emoji('u270f')], 'com base no que está', 'sendo falado no seu áudio.', ['Entendeu?', emoji('u1f609')]]);
|
|
148
|
+
palco(F0('26') - 0.1, F1('26') + 0.1, (el, P) => {
|
|
149
|
+
const pts = []; for (let i = 0; i <= 120; i++) { const x = 80 + i * 7.67, a = 210 * Math.sin(i * 0.33) * (0.35 + 0.65 * Math.abs(Math.sin(i * 0.052))); pts.push([x, PALCO_Y + 140 + a]); }
|
|
150
|
+
P.d = 'M' + pts.map(p => p.map(v => v.toFixed(1)).join(' ')).join(' L');
|
|
151
|
+
P.sv = caixa(el, { left: '0', top: '0', width: '1080px', height: '1400px' }, `<svg width="1080" height="1400"><path id="onda" d="${P.d}" fill="none" stroke="var(--tinta)" stroke-width="12" stroke-linecap="round" stroke-linejoin="round"/></svg>`);
|
|
152
|
+
P.on = P.sv.querySelector('#onda');
|
|
153
|
+
P.w = el; P.lap = emo(el, 'u270f', 0, 0, 230);
|
|
154
|
+
// picos (mínimos de y = pontos mais altos) recebem um ícone
|
|
155
|
+
P.picos = [[LOGO('whatsapp-icon.svg')], [ELON, 1], [LOGO('youtube-icon.svg')], [LOGO('spotify-icon.svg')]].map(([src, f], i) => ({ src, f, i }));
|
|
156
|
+
P.ta = PF('26', 'desenhado') - 0.1; P.tb = PF('26', 'audio', true);
|
|
157
|
+
ev(P.ta, 'desenha', -0.3, 0.3); ev(P.ta + 1.0, 'desenha', 0, 0.25); ev(P.ta + 2.0, 'desenha', 0.3, 0.25);
|
|
158
|
+
}, (t, P) => {
|
|
159
|
+
const L = P.on.getTotalLength(); if (!P.pk) { P.pk = []; let ult = -1; for (let s = 0; s <= L; s += 6) { const a = P.on.getPointAtLength(s - 6), b = P.on.getPointAtLength(s), c = P.on.getPointAtLength(s + 6); if (b.y < a.y && b.y <= c.y && b.y < PALCO_Y + 40 && s - ult > 120) { P.pk.push({ s, x: b.x, y: b.y }); ult = s; } }
|
|
160
|
+
P.pk = P.pk.slice(0, 4); P.pk.forEach((p, i) => { const m = P.picos[i]; p.el = ima(P.w, m.src, p.x, p.y - 110, 160, m.f ? { objectFit: 'cover', objectPosition: '50% 22%', borderRadius: '50%', border: '5px solid #fff' } : {}); p.t = P.ta + (P.tb - P.ta) * (p.s / L); ev(p.t, 'pop', (p.x - 540) / 500, 0.22); }); }
|
|
161
|
+
const u = IO(seg(t, P.ta, P.tb)), s = L * u, q = P.on.getPointAtLength(s);
|
|
162
|
+
P.on.style.strokeDasharray = `${s.toFixed(1)} ${L}`;
|
|
163
|
+
tf(P.lap, { x: q.x - 40, y: q.y - 200, r: 6 * Math.sin(t * 9), op: seg(t, P.ta - 0.2, P.ta) * (1 - seg(t, P.tb + 0.1, P.tb + 0.4)) }); P.lap.lot.toca(t, P.ta);
|
|
164
|
+
P.pk.forEach((p, i) => { const n = nasce(t, p.t, 0.45), v = vivo(t, i, 3); tf(p.el, { op: n.op, s: n.s, y: n.y + v.y, blur: n.blur }); });
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// ── 27 · o exemplo genérico: só o texto, no meio da tela, sem nada (de propósito) ──
|
|
168
|
+
legenda('27', ['Exemplo: "A Tesla,', 'do Elon Musk, acabou', 'de divulgar ontem', 'no YouTube a', 'demonstração do seu', 'primeiro carro voador."'], { y: PALCO_Y + 120, ate: CN('tesla_desenhado').t0 - 0.05 });
|
|
169
|
+
|
|
170
|
+
// ── tesla_desenhado: a mesma frase volta desenhada (logos, Elon, "ontem") e o voo real com o som do vídeo ──
|
|
171
|
+
(function () {
|
|
172
|
+
const T = CN('tesla_desenhado').t0, TV = T + 1.15;
|
|
173
|
+
palco(T - 0.02, F0('28') - 0.05, (el, P) => {
|
|
174
|
+
P.it = [{ src: LOGO('tesla-t.svg'), t: T + 0.02, x: 300 }, { src: ELON, t: T + 0.3, x: 540, f: 1 }, { src: LOGO('youtube-icon.svg'), t: T + 0.58, x: 780 }].map(o => {
|
|
175
|
+
o.el = ima(el, o.src, o.x, 300, 200, o.f ? { objectFit: 'cover', objectPosition: '50% 22%', borderRadius: '50%', border: '6px solid #fff', boxShadow: '0 14px 30px rgba(20,20,30,.16)' } : {}); ev(o.t, 'pop', (o.x - 540) / 400, 0.3); return o; });
|
|
176
|
+
P.ontem = caixa(el, { left: (780 - 150) + 'px', width: '300px', top: '430px', textAlign: 'center', font: `600 56px/1 ${SANS}`, color: 'var(--cinza)' }, 'ontem');
|
|
177
|
+
}, (t, P) => {
|
|
178
|
+
P.it.forEach((o, i) => { const n = nasce(t, o.t, 0.45), v = vivo(t, i, 3); tf(o.el, { op: n.op, s: lerp(0.3, 1, n.k), r: (1 - n.k) * -90, y: n.y + v.y, blur: n.blur }); });
|
|
179
|
+
const o = palavraVida(t, T + 0.7, false); tf(P.ontem, { op: o.op, y: o.y, blur: o.blur });
|
|
180
|
+
}, { entra: false });
|
|
181
|
+
palco(TV - 0.02, F0('28') - 0.05, janela('roadster-voo', { t: TV, ate: F0('28') + 0.05, som: true, alvo: -12.5 }), { y: 830 }); // o voo real (a trilha para)
|
|
182
|
+
legenda('27', [[palavra('carro', T + 0.85), palavra('voador', T + 0.97, 'af'), emoji('u1f680', { t: T + 1.05 })]], { ate: F0('28') - 0.05 });
|
|
183
|
+
})();
|
|
184
|
+
|
|
185
|
+
// ── 28 · "Percebe que, quando você desenha na tela o que está falando, é muito mais fácil para a pessoa assimilar..." ──
|
|
186
|
+
legenda('28', ['Percebe que, quando você', 'f|desenha na tela', 'o que está falando,', 'é muito mais fácil', 'para a pessoa f|assimilar', 'o que está acontecendo?',
|
|
187
|
+
'Ela fica muito mais', ['f|entretida', emoji('u1f37f')], 'no seu vídeo.', 'Senão, o seu vídeo', ['acaba sendo', 'c|chato!', emoji('u1f634')]]);
|
|
188
|
+
// 🧠: o que é desenhado entra na cabeça (assimila) e a ideia acende 💡
|
|
189
|
+
palco(F0('28') - 0.08, PF('28', 'Ela') - 0.12, (el, P) => {
|
|
190
|
+
el = amplia(el, 1.8, 50);
|
|
191
|
+
P.b = emo(el, 'u1f9e0', 540, PALCO_Y + 40, 380);
|
|
192
|
+
P.ic = [LOGO('whatsapp-icon.svg'), ELON, LOGO('youtube-icon.svg'), LOGO('tesla-t.svg')].map((src, i) => { const f = src === ELON; const e = ima(el, src, 0, 0, 120, f ? { objectFit: 'cover', objectPosition: '50% 22%', borderRadius: '50%', border: '5px solid #fff' } : {});
|
|
193
|
+
return { e, t: PF('28', 'desenha') + i * 0.55, a: [-2.4, -0.8, 0.8, 2.4][i] }; });
|
|
194
|
+
P.ic.forEach(o => ev(o.t + 0.5, 'pop', o.a / 3, 0.2));
|
|
195
|
+
P.lamp = emo(el, 'u1f4a1', 540, PALCO_Y - 260, 180); P.tl = PF('28', 'acontecendo'); ev(P.tl, 'brilho', 0, 0.3);
|
|
196
|
+
}, (t, P) => {
|
|
197
|
+
P.b.lot.toca(t, P.t0);
|
|
198
|
+
let pulso = 0; P.ic.forEach(o => { const u = seg(t, o.t, o.t + 0.6), k = IO(u), x = 540 + 420 * Math.sin(o.a) * (1 - k), y = PALCO_Y + 40 - 420 * Math.cos(o.a * 0.6) * (1 - k);
|
|
199
|
+
tf(o.e, { x: x - 60, y: y - 60, s: lerp(1, 0.3, k), op: clamp(u * 5) * (1 - seg(t, o.t + 0.5, o.t + 0.62)), blur: k * 6 }); pulso = Math.max(pulso, Math.exp(-Math.pow((t - o.t - 0.6) / 0.08, 2))); });
|
|
200
|
+
tf(P.b, { s: 1 + 0.06 * pulso + 0.015 * Math.sin(t * 2.5) });
|
|
201
|
+
const n = nasce(t, P.tl, 0.5); tf(P.lamp, { op: n.op, s: n.s, y: n.y + 6 * Math.sin(t * 2), blur: n.blur }); P.lamp.lot.toca(t, P.tl);
|
|
202
|
+
});
|
|
203
|
+
// curva de atenção (desenhado segura × genérico cai) + 🍿 e 😴 nas pontas
|
|
204
|
+
palco(PF('28', 'Ela') - 0.12, F1('28') + 0.2, bloco({ h: 640, kind: 'abre',
|
|
205
|
+
monta(el, B) {
|
|
206
|
+
B.el = cartaoBranco(el, { inset: '0' });
|
|
207
|
+
const A = 'M60,150 C200,138 320,160 460,148 S700,158 852,154', G = 'M60,150 C190,180 280,280 420,330 S690,420 852,440';
|
|
208
|
+
B.el.innerHTML = `<svg viewBox="0 0 912 520" width="912" height="520" style="position:absolute;left:0;top:70px">
|
|
209
|
+
${[150, 250, 350, 450].map(y => `<line x1="60" x2="852" y1="${y}" y2="${y}" stroke="#ECEAE4" stroke-width="2"/>`).join('')}
|
|
210
|
+
<path id="ca" d="${A}" fill="none" stroke="#2F55F4" stroke-width="11" stroke-linecap="round"/><path id="cg" d="${G}" fill="none" stroke="#B4B0A8" stroke-width="11" stroke-linecap="round"/>
|
|
211
|
+
<circle id="pa" r="14" fill="#2F55F4"/><circle id="pg" r="14" fill="#B4B0A8"/></svg>
|
|
212
|
+
<div style="position:absolute;left:60px;top:40px;font:600 32px ${SANS};color:#8A877F">atenção ao longo do vídeo</div>
|
|
213
|
+
<div id="la" style="position:absolute;left:470px;top:150px;font:700 40px ${SANS};color:#2F55F4">desenhado</div>
|
|
214
|
+
<div id="lg" style="position:absolute;left:470px;top:440px;font:700 40px ${SANS};color:#A39F96">genérico</div>`;
|
|
215
|
+
[['ca', 'ca'], ['cg', 'cg'], ['pa', 'pa'], ['pg', 'pg'], ['la', 'la'], ['lg', 'lg']].forEach(([k, id]) => { B[k] = B.el.querySelector('#' + id); });
|
|
216
|
+
B.pip = emo(el, 'u1f37f', 912 - 40, 50, 240); B.zz = emo(el, 'u1f634', 912 - 40, 560, 220);
|
|
217
|
+
B.ta = PF('28', 'Ela') + 0.1; B.tE = PF('28', 'entretida'); B.tg = PF('28', 'Senao') + 0.05; B.tC = PF('28', 'chato');
|
|
218
|
+
ev(B.ta + 0.2, 'desenha', -0.2, 0.25); ev(B.tg, 'desenha', 0.2, 0.25);
|
|
219
|
+
},
|
|
220
|
+
seek(t, B) {
|
|
221
|
+
abreJanela(t, B.el, B.t, { lado: 0 });
|
|
222
|
+
const ua = IO(seg(t, B.ta, B.tE + 0.2)), ug = IO(seg(t, B.tg, B.tC + 0.1)), L1 = B.ca.getTotalLength(), L2 = B.cg.getTotalLength();
|
|
223
|
+
B.ca.style.strokeDasharray = `${(L1 * ua).toFixed(1)} ${L1}`; B.cg.style.strokeDasharray = `${(L2 * ug).toFixed(1)} ${L2}`;
|
|
224
|
+
const pa = B.ca.getPointAtLength(L1 * ua), pg = B.cg.getPointAtLength(L2 * ug);
|
|
225
|
+
B.pa.setAttribute('cx', pa.x); B.pa.setAttribute('cy', pa.y); B.pa.style.opacity = ua > 0.01 ? 1 : 0; B.pg.setAttribute('cx', pg.x); B.pg.setAttribute('cy', pg.y); B.pg.style.opacity = ug > 0.01 ? 1 : 0;
|
|
226
|
+
const la = palavraVida(t, B.ta + 0.3, false), lg = palavraVida(t, B.tg + 0.3, false); tf(B.la, la); tf(B.lg, lg);
|
|
227
|
+
const p = nasce(t, B.tE, 0.5), z = nasce(t, B.tC, 0.5); tf(B.pip, { op: p.op, s: p.s, y: p.y, blur: p.blur }); B.pip.lot.toca(t, B.tE); tf(B.zz, { op: z.op, s: z.s, y: z.y, blur: z.blur }); B.zz.lot.toca(t, B.tC);
|
|
228
|
+
} }), { y: 820 });
|
|
229
|
+
|
|
230
|
+
// ── 29 · juntar → mandar pra IA → 99% (o 1% é o seu vídeo 🔥) ──
|
|
231
|
+
legenda('29', ['Entendendo e aplicando', 'esses passos, agora', 'é só f|juntar tudo isso', ['e f|mandar pra sua', logo('claude-symbol.svg'), 'o|IA.'], 'Com as ferramentas',
|
|
232
|
+
'necessárias, você vai ter,', 'com certeza, um a|vídeo', 'f|diferente de 99%#5', 'dos vídeos de a|motion', 'que tão rodando por aí.']);
|
|
233
|
+
(function () {
|
|
234
|
+
const TJ = PF('29', 'juntar'), TM = PF('29', 'mandar'), T99 = PF('29', 'um') - 0.1;
|
|
235
|
+
const PAS = [['u1f50d', 'Referência'], ['u1f3a8', 'Identidade'], ['u1f3b5', 'Som'], ['u2728', 'Elementos']];
|
|
236
|
+
palco(F0('29') - 0.08, T99, (el, P) => {
|
|
237
|
+
el = amplia(el, 1.2, 40);
|
|
238
|
+
P.ps = PAS.map(([id, nome], i) => { const cx = 210 + i * 220; const g = caixa(el, { left: '0', top: '0', width: '1080px', height: '1400px' }); g.e = emo(g, id, cx, PALCO_Y - 120, 170);
|
|
239
|
+
g.n = caixa(g, { left: (cx - 120) + 'px', width: '240px', top: (PALCO_Y - 10) + 'px', textAlign: 'center', font: `600 34px/1 ${SANS}`, color: 'var(--tinta)' }, `<span style="color:var(--az)">${i + 1}</span> ${nome}`);
|
|
240
|
+
g.cx = cx; g.t = F0('29') + 0.1 + i * 0.25; g.tj = TJ + 0.05 + i * 0.12; ev(g.t, 'pop', (cx - 540) / 500, 0.2); ev(g.tj + 0.4, 'pouso', 0, 0.2); return g; });
|
|
241
|
+
P.pac = emo(el, 'u1f4c1', 540, PALCO_Y + 260, 260);
|
|
242
|
+
P.claude = cartaoBranco(el, { left: '220px', top: (PALCO_Y + 120) + 'px', width: '640px', height: '170px', display: 'flex', alignItems: 'center', gap: '24px', padding: '0 34px', boxSizing: 'border-box' },
|
|
243
|
+
`<img src="${LOGO('claude-symbol.svg')}" style="width:70px;height:70px"><div style="flex:1;font:500 34px ${SANS};color:#9A968C;white-space:nowrap" class="ph">Responder ao Claude…</div><div class="chip" style="font:600 30px ${SANS};color:#141413;background:#F1EFE8;border-radius:18px;padding:12px 20px;opacity:0">📁 processo</div><div class="env" style="width:74px;height:74px;border-radius:20px;background:#D97757;display:grid;place-items:center"><svg width="30" height="30" viewBox="0 0 20 20"><path d="M10 16V4M4.5 9.5 10 4l5.5 5.5" stroke="#fff" stroke-width="2.4" fill="none" stroke-linecap="round" stroke-linejoin="round"/></svg></div>`);
|
|
244
|
+
P.res = cartaoBranco(el, { left: (540 - 290) + 'px', top: (PALCO_Y - 340) + 'px', width: '580px', height: '326px', borderRadius: '30px', background: '#111' }); P.resF = filme(P.res, 'glass-hub'); P.resF.e.style.cssText += ';position:absolute;inset:0;width:100%;height:100%;object-fit:cover';
|
|
245
|
+
P.chip = P.claude.querySelector('.chip'); P.ph = P.claude.querySelector('.ph'); P.env = P.claude.querySelector('.env');
|
|
246
|
+
P.cur = caixa(el, { left: '0', top: '0', width: '64px', height: '64px', zIndex: 9 }, '<svg viewBox="0 0 32 32" width="64" height="64"><path d="M7 3.5 L7 25.5 L12.4 20.4 L16 28.6 L19.6 27 L16.1 19 L23.6 19 Z" fill="#111" stroke="#fff" stroke-width="2" stroke-linejoin="round"/></svg>');
|
|
247
|
+
P.tc = TM - 0.15; P.tClique = PF('29', 'IA') + 0.15; ev(P.tc + 0.1, 'carry', 0, 0.25); ev(P.tClique, 'clique', 0.2, 0.4); ev(P.tClique + 0.08, 'imessage', 0.2, 0.5);
|
|
248
|
+
}, (t, P) => {
|
|
249
|
+
// os 4 passos voam para o pacote
|
|
250
|
+
P.ps.forEach((g, i) => { const n = nasce(t, g.t, 0.45), u = seg(t, g.tj, g.tj + 0.5), k = IO(u), v = vivo(t, i, 3);
|
|
251
|
+
tf(g.e, { op: n.op * (1 - seg(t, g.tj + 0.4, g.tj + 0.5)), s: n.s * lerp(1, 0.35, k), x: (540 - g.cx) * k + v.x * (1 - k), y: n.y + 380 * k - 120 * Math.sin(Math.PI * k) + v.y * (1 - k), blur: n.blur });
|
|
252
|
+
g.e.lot.toca(t, g.t); const nn = palavraVida(t, g.t + 0.1, false); tf(g.n, { op: nn.op * (1 - ILL(seg(t, g.tj - 0.1, g.tj + 0.2))), y: nn.y, blur: nn.blur + 10 * seg(t, g.tj - 0.1, g.tj + 0.2) }); });
|
|
253
|
+
// pacote: nasce no "juntar", engorda a cada passo, depois o cursor leva para o card do Claude
|
|
254
|
+
let sq = 0; P.ps.forEach(g => { sq = Math.max(sq, Math.exp(-Math.pow((t - g.tj - 0.45) / 0.07, 2))); });
|
|
255
|
+
const np = nasce(t, TJ - 0.05, 0.4), arr = IO(seg(t, P.tc + 0.15, P.tc + 0.75)), some = seg(t, P.tc + 0.7, P.tc + 0.85);
|
|
256
|
+
const px0 = 540, py0 = PALCO_Y + 260, px1 = 640, py1 = PALCO_Y + 205;
|
|
257
|
+
tf(P.pac, { op: np.op * (1 - some), s: np.s * (1 + 0.1 * sq) * lerp(1, 0.4, arr), x: (px1 - px0) * arr, y: np.y + (py1 - py0) * arr - 260 * Math.sin(Math.PI * arr) + 340 * (1 - ILL(seg(t, TJ - 0.05, TJ + 0.5))) * 0, blur: np.blur }); P.pac.lot.toca(t, TJ);
|
|
258
|
+
const nc = nasce(t, P.tc - 0.2, 0.45); tf(P.claude, { op: nc.op * (1 - seg(t, T99 - 0.3, T99)), s: lerp(0.9, 1, nc.k), y: nc.y + 280 * (1 - nc.k) * 0 });
|
|
259
|
+
P.chip.style.opacity = seg(t, P.tc + 0.75, P.tc + 0.9) * (1 - seg(t, P.tClique + 0.05, P.tClique + 0.25)); P.ph.textContent = t > P.tClique + 0.1 ? 'Enviado ✓' : 'Responder ao Claude…'; P.ph.style.color = t > P.tClique + 0.1 ? '#2BB07F' : '#9A968C';
|
|
260
|
+
{ const n = nasce(t, P.tClique + 0.55, 0.5), so = 1 - seg(t, T99 - 0.3, T99); tf(P.res, { op: n.op * so, s: lerp(0.7, 1, n.k), y: n.y, blur: n.blur }); P.resF.toca(t, P.tClique + 0.55); }
|
|
261
|
+
P.env.style.transform = `scale(${(1 - 0.14 * Math.exp(-Math.pow((t - P.tClique) / 0.06, 2))).toFixed(3)})`;
|
|
262
|
+
const cx = t < P.tc + 0.75 ? lerp(760, px0 + 40 + (px1 - px0) * arr, ILL(seg(t, P.tc - 0.3, P.tc + 0.15))) : lerp(px1 + 40, 795, IO(seg(t, P.tClique - 0.4, P.tClique)));
|
|
263
|
+
const cy = t < P.tc + 0.75 ? lerp(1300, py0 + 20 + (py1 - py0) * arr - 260 * Math.sin(Math.PI * arr), ILL(seg(t, P.tc - 0.3, P.tc + 0.15))) : lerp(py1 + 20, PALCO_Y + 205, IO(seg(t, P.tClique - 0.4, P.tClique)));
|
|
264
|
+
tf(P.cur, { x: cx, y: cy, op: seg(t, P.tc - 0.3, P.tc - 0.1) * (1 - seg(t, P.tClique + 0.35, P.tClique + 0.55)), s: 1 - 0.15 * Math.exp(-Math.pow((t - P.tClique) / 0.05, 2)) });
|
|
265
|
+
});
|
|
266
|
+
// 99%: o contador corre; num quadro de 100 pontos, 99 ficam cinza e o 1% (o seu vídeo) pula para fora brilhando
|
|
267
|
+
palco(T99, F1('29') + 0.25, (el, P) => {
|
|
268
|
+
const GL = 150, GT = 430, ST = 78, D = 58;
|
|
269
|
+
P.n = caixa(el, { left: '0', right: '0', top: '150px', textAlign: 'center', font: `700 230px/1 ${SANS}`, letterSpacing: '-0.06em', color: 'var(--tinta)' }, '0%');
|
|
270
|
+
P.ds = []; for (let i = 0; i < 100; i++) P.ds.push(caixa(el, { left: (GL + (i % 10) * ST) + 'px', top: (GT + Math.floor(i / 10) * ST) + 'px', width: D + 'px', height: D + 'px', borderRadius: '50%', background: i === 99 ? 'var(--az)' : '#D3D0C8' }));
|
|
271
|
+
P.rot = caixa(el, { left: (GL + 9 * ST - 420) + 'px', width: '400px', top: (GT + 9 * ST - 225) + 'px', textAlign: 'right', font: `700 40px/1 ${SANS}`, color: 'var(--az)' }, 'o seu vídeo');
|
|
272
|
+
P.fogo = emo(el, 'u1f525', GL + 9 * ST + D / 2 + 40, GT + 9 * ST + D / 2 - 330, 220); P.ta = PF('29', 'noventa') - 0.05; P.tb = PF('29', 'cento', true); P.tp = PF('29', 'dos') - 0.05;
|
|
273
|
+
ev(P.ta, 'contador', 0, 0.3); ev(P.tp, 'brilho', 0.3, 0.35);
|
|
274
|
+
}, (t, P) => {
|
|
275
|
+
const u = seg(t, P.ta, P.tb); P.n.textContent = Math.round(99 * IO(u)) + '%';
|
|
276
|
+
P.ds.forEach((d, i) => { if (i === 99) return; const k = seg(t, P.t0 + 0.15 + i * 0.011, P.t0 + 0.4 + i * 0.011), cinza = ILL(seg(t, P.tb - 0.2, P.tb + 0.3)); tf(d, { op: k * (1 - 0.35 * cinza), s: lerp(0.3, 1, ILL(k)) * (1 - 0.12 * cinza) }); });
|
|
277
|
+
const d = P.ds[99], k = nasce(t, P.t0 + 1.4, 0.4), j = ILL(seg(t, P.tp, P.tp + 0.6));
|
|
278
|
+
tf(d, { op: k.op, s: lerp(0.3, 1, k.k) * (1 + 1.6 * j), x: 40 * j, y: -170 * j - 40 * Math.sin(Math.PI * j) });
|
|
279
|
+
d.style.boxShadow = `0 0 ${(60 * j).toFixed(0)}px ${(18 * j).toFixed(0)}px rgba(47,85,244,${(0.55 * j).toFixed(2)})`;
|
|
280
|
+
const f = nasce(t, P.tp + 0.25, 0.45); tf(P.fogo, { op: f.op, s: f.s, y: f.y, blur: f.blur }); P.fogo.lot.toca(t, P.tp);
|
|
281
|
+
const r = palavraVida(t, P.tp + 0.35, true); tf(P.rot, { op: r.op, y: r.y, s: r.s, blur: r.blur });
|
|
282
|
+
});
|
|
283
|
+
})();
|
|
284
|
+
|
|
285
|
+
// ── 30–31 · ferramentas: um dock; cada logo nasce grande no instante falado e pula para o dock ──
|
|
286
|
+
legenda('30', ['As ferramentas que eu uso', 'estão aparecendo', 'na tela agora:']);
|
|
287
|
+
legenda('31', [['After', 'Effects'], 'e EffectCraft,', ['que é f|open', 'f|source.'], 'Remotion,', ['bem', 'c|raramente.'], 'ElevenLabs,', 'para gerar essa voz', 'aqui narrada.',
|
|
288
|
+
'Spotify,', 'para as playlists de música.', 'Mobbin,', 'um MCP para buscar', 'referência de tela', 'e de UI.#2', 'As referências de projetos', 'do Clube do Motion.',
|
|
289
|
+
'E bibliotecas Python,', 'como librosa, Whisper,', 'Essentia e Playwright.']);
|
|
290
|
+
const DOCK_Y = 1130; // centro do dock (2 fileiras de 6, ícones de 108 px), logo acima da legenda
|
|
291
|
+
const FER = [['after-effects-icon.svg', PF('31', 'After')], ['effectcraft-icon.svg', PF('31', 'EffectCraft')], ['remotion-favicon.png', PF('31', 'Remotion'), 'raro'],
|
|
292
|
+
['elevenlabs-icon.svg', PF('31', 'ElevenLabs')], ['spotify-icon.svg', PF('31', 'Spotify')], ['mobbin-icon.svg', PF('31', 'Mobbin')], ['clube-motion-app-icon.svg', PF('31', 'Clube')],
|
|
293
|
+
['python-icon.svg', PF('31', 'Python')], ['../assets/python/librosa.svg', PF('31', 'librosa'), 'larga'], ['openai-symbol.svg', PF('31', 'Whisper')],
|
|
294
|
+
['../assets/python/essentia.svg', PF('31', 'Essentia'), 'larga'], ['../assets/python/playwright.svg', PF('31', 'Playwright')]].map(([arq, t, tipo], i) => ({ arq, t: t - 0.12, tipo, i }));
|
|
295
|
+
const TFIM_FER = PF('32', 'Eu') - 0.15;
|
|
296
|
+
palco(F0('30') - 0.1, TFIM_FER, (el, P) => {
|
|
297
|
+
P.dock = caixa(el, { left: '130px', top: (DOCK_Y - 90) + 'px', width: '820px', height: '290px', borderRadius: '56px', background: 'rgba(255,255,255,.7)', boxShadow: '0 24px 60px -20px rgba(40,30,20,.25), 0 0 0 1.5px rgba(255,255,255,.9) inset', backdropFilter: 'blur(20px)' });
|
|
298
|
+
FER.forEach(f => { f.slot = { x: 225 + (f.i % 6) * 124, y: DOCK_Y + 35 + Math.floor(f.i / 6) * 125 - 60 + 30 };
|
|
299
|
+
f.el = caixa(el, { left: '0', top: '0', width: '420px', height: '420px', borderRadius: '92px', background: '#fff', boxShadow: '0 20px 40px -16px rgba(30,20,10,.3)', display: 'grid', placeItems: 'center', overflow: 'hidden' },
|
|
300
|
+
`<img src="${LOGO(f.arq)}" style="width:${f.tipo === 'larga' ? 370 : 290}px;height:${f.tipo === 'larga' ? 370 : 290}px;object-fit:contain">`);
|
|
301
|
+
const prox = FER[f.i + 1], cortes = [PF('31', 'que') - 0.05, PF('31', 'essa') - 0.05, PF('31', 'playlists') - 0.1].filter(c => c > f.t + 0.3);
|
|
302
|
+
f.tp = Math.max(f.t + 0.55, Math.min(prox ? prox.t - 0.32 : f.t + 1.2, f.t + 5, ...cortes.map(c => c - 0.35))); ev(f.t + 0.05, 'pop', 0, 0.22); ev(f.tp + 0.32, 'pouso', (f.slot.x - 540) / 500, 0.16); });
|
|
303
|
+
// cadeado que abre em "open source"
|
|
304
|
+
P.cadW = amplia(el, 1.6, 100); P.cad = caixa(P.cadW, { left: (540 - 130) + 'px', top: (PALCO_Y - 330) + 'px', width: '260px', height: '300px' }, `<svg width="260" height="300" viewBox="0 0 260 300"><g id="arco"><path d="M70 140 V95 a60 60 0 0 1 120 0 V140" fill="none" stroke="#141413" stroke-width="22" stroke-linecap="round"/></g><rect x="30" y="130" width="200" height="150" rx="34" fill="#2BB07F"/><circle cx="130" cy="195" r="18" fill="#fff"/><rect x="123" y="200" width="14" height="40" rx="7" fill="#fff"/></svg>`);
|
|
305
|
+
P.arco = P.cad.querySelector('#arco'); P.tO = PF('31', 'open'); ev(P.tO + 0.1, 'liga', 0, 0.35);
|
|
306
|
+
// prints reais (ElevenLabs e Spotify) numa janela curta acima do dock
|
|
307
|
+
P.prints = [['../assets/telas/elevenlabs-text-to-speech-minha-voz.png', PF('31', 'essa') - 0.05, PF('31', 'narrada', true) + 0.15], ['../assets/telas/spotify-playlist-internacional.png', PF('31', 'playlists') - 0.1, PF('31', 'musica', true) + 0.15]].map(([src, a, b]) => {
|
|
308
|
+
const j = cartaoBranco(el, { left: '84px', top: (PALCO_Y - 330) + 'px', width: '912px', height: '513px' }); const im = $('img', null, j, { position: 'absolute', left: '0', top: '0', width: '150%', height: 'auto' }); im.src = src; ev(a, 'abre', 0, 0.22); return { j, im, a, b }; });
|
|
309
|
+
}, (t, P) => {
|
|
310
|
+
const nd = nasce(t, P.t0 + 0.4, 0.5); tf(P.dock, { op: nd.op, y: (1 - nd.k) * 80, blur: nd.blur });
|
|
311
|
+
FER.forEach((f, i) => {
|
|
312
|
+
const n = nasce(t, f.t, 0.4), u = seg(t, f.tp, f.tp + 0.4), k = IO(u), x0 = 540, y0 = 610;
|
|
313
|
+
const raro = f.tipo === 'raro', kr = raro ? ILL(seg(t, PF('31', 'raramente') - 0.1, PF('31', 'raramente') + 0.4)) : 0;
|
|
314
|
+
const sFim = 108 / 420 * (raro ? 0.6 : 1), s = lerp(1, sFim, k) * n.s;
|
|
315
|
+
const x = lerp(x0, f.slot.x, k), y = lerp(y0, f.slot.y, k) - 260 * Math.sin(Math.PI * k);
|
|
316
|
+
const pulo = t > TFIM_FER - 2.2 && t < TFIM_FER - 0.4 ? Math.abs(Math.sin((t - i * 0.13) * 7)) * 18 * (1 - kr) : 0; // "trabalhando" na fala 32
|
|
317
|
+
tf(f.el, { x: x - 210, y: y - 210 - pulo, s, op: n.op, r: (1 - n.k) * -40, blur: n.blur + (u > 0 && u < 1 ? 3 : 0) });
|
|
318
|
+
f.el.style.filter = (f.el.style.filter === 'none' ? '' : f.el.style.filter) + (raro && kr > 0 ? ` grayscale(${kr.toFixed(2)}) opacity(${(1 - 0.35 * kr).toFixed(2)})` : '');
|
|
319
|
+
f.el.style.borderRadius = (92 + 30 * k).toFixed(0) + 'px';
|
|
320
|
+
});
|
|
321
|
+
// cadeado: nasce, abre o arco em "open" e sai em "Remotion"
|
|
322
|
+
const tc = PF('31', 'que') - 0.05, nc = nasce(t, tc, 0.45), sc = sai(t, PF('31', 'Remotion') - 0.2, 0.3);
|
|
323
|
+
tf(P.cad, { op: nc.op * sc.op, s: nc.s, y: nc.y + sc.y, blur: nc.blur + sc.blur }); P.arco.setAttribute('transform', `translate(${(36 * ILL(seg(t, P.tO, P.tO + 0.4))).toFixed(1)} ${(-36 * ILL(seg(t, P.tO, P.tO + 0.4))).toFixed(1)}) rotate(${(-28 * ILL(seg(t, P.tO, P.tO + 0.4))).toFixed(1)} 190 140)`);
|
|
324
|
+
P.prints.forEach(pr => { abreJanela(t, pr.j, pr.a, { lado: 0 }); const s = sai(t, pr.b, 0.35); if (t > pr.b - 0.35) tf(pr.j, { op: s.op, y: s.y, blur: s.blur, s: 1 }); pr.j.style.visibility = t > pr.a - 0.05 && t < pr.b + 0.05 ? 'visible' : 'hidden';
|
|
325
|
+
const u = seg(t, pr.a, pr.b); pr.im.style.transform = `translate(${(-30 * u).toFixed(1)}px,${(-20 * u).toFixed(1)}px) scale(${(1 + 0.04 * u).toFixed(4)})`; });
|
|
326
|
+
}, { sai: true });
|
|
327
|
+
|
|
328
|
+
// ── 32 · "E, obviamente, o Claude Code, que faz todo o trabalho pesado. Eu só penso no roteiro, basicamente." ──
|
|
329
|
+
legenda('32', ['E, obviamente, o', [logo('claude-symbol.svg'), 'o|Claude', 'o|Code,'], 'que faz todo o trabalho', ['f|pesado.', emoji('u1f4aa')], 'Eu só penso no', ['f|roteiro,', emoji('u1f4a1')], 'basicamente.']);
|
|
330
|
+
// Claude levantando um haltere sobre o dock que trabalha
|
|
331
|
+
palco(PF('32', 'Claude') - 0.15, TFIM_FER, (el, P) => {
|
|
332
|
+
el = amplia(el, 1.45, -70);
|
|
333
|
+
P.c = ima(el, LOGO('claude-symbol.svg'), 540, PALCO_Y + 30, 300);
|
|
334
|
+
P.h = caixa(el, { left: (540 - 260) + 'px', top: '0', width: '520px', height: '90px' }, `<svg width="520" height="90" viewBox="0 0 520 90"><rect x="40" y="38" width="440" height="14" rx="7" fill="#3A3833"/><rect x="0" y="5" width="40" height="80" rx="12" fill="#141413"/><rect x="44" y="15" width="30" height="60" rx="10" fill="#141413"/><rect x="446" y="15" width="30" height="60" rx="10" fill="#141413"/><rect x="480" y="5" width="40" height="80" rx="12" fill="#141413"/></svg>`);
|
|
335
|
+
P.m = emo(el, 'u1f4aa', 540 + 270, PALCO_Y + 140, 170); P.tp = PF('32', 'pesado'); ev(P.tp, 'peso', 0, 0.3);
|
|
336
|
+
}, (t, P) => {
|
|
337
|
+
const rep = Math.max(0, Math.sin((t - P.t0) * 4.2)), sq = 1 - 0.06 * rep;
|
|
338
|
+
tf(P.c, { r: (t - P.t0) * 30, s: 1 + 0.02 * Math.sin(t * 3), y: 10 * rep });
|
|
339
|
+
P.c.style.transform += ` scaleY(${sq.toFixed(3)})`;
|
|
340
|
+
const nh = nasce(t, P.t0 + 0.4, 0.45); tf(P.h, { op: nh.op, y: PALCO_Y - 230 - 120 * rep, r: 3 * Math.sin(t * 4.2), blur: nh.blur });
|
|
341
|
+
const nm = nasce(t, P.tp, 0.45); tf(P.m, { op: nm.op, s: nm.s, y: nm.y, blur: nm.blur }); P.m.lot.toca(t, P.tp);
|
|
342
|
+
});
|
|
343
|
+
// roteiro.md escrito a mão (o resto é com o Claude)
|
|
344
|
+
palco(TFIM_FER, F1('32') + 0.3, (el, P) => {
|
|
345
|
+
el = amplia(el, 1.3, 50);
|
|
346
|
+
P.f = cartaoBranco(el, { left: (540 - 300) + 'px', top: (PALCO_Y - 380) + 'px', width: '600px', height: '760px', borderRadius: '28px' });
|
|
347
|
+
let linhas = ''; for (let i = 0; i < 9; i++) { const y = 170 + i * 62, w = [440, 400, 470, 300, 450, 380, 460, 330, 260][i]; let d = `M50 ${y}`; for (let x = 50; x < 50 + w; x += 18) d += ` Q${x + 9} ${y - 14 * ((x / 18) % 2 ? 1 : -1) * 0.6} ${x + 18} ${y}`; linhas += `<path class="ln" d="${d}" fill="none" stroke="#3A3833" stroke-width="5" stroke-linecap="round"/>`; }
|
|
348
|
+
P.f.innerHTML = `<div style="position:absolute;left:50px;top:50px;font:700 46px ${SANS};color:#141413">roteiro.md</div><svg width="600" height="760" style="position:absolute;inset:0">${linhas}</svg>`;
|
|
349
|
+
P.ls = [...P.f.querySelectorAll('.ln')]; P.lap = emo(el, 'u270f', 0, 0, 150); P.ce = emo(el, 'u1f9e0', 540 + 300, PALCO_Y - 380, 170);
|
|
350
|
+
P.ta = PF('32', 'roteiro') - 0.2; P.tb = F1('32') + 0.1; ev(P.ta, 'desenha', 0, 0.3); ev(P.ta + 0.6, 'folha', 0, 0.2);
|
|
351
|
+
}, (t, P) => {
|
|
352
|
+
const u = seg(t, P.ta, P.tb) * P.ls.length; let ponta = null;
|
|
353
|
+
P.ls.forEach((l, i) => { const L = l.getTotalLength(), k = clamp(u - i); l.style.strokeDasharray = `${(L * k).toFixed(1)} ${L}`; if (k > 0 && k < 1) { const q = l.getPointAtLength(L * k); ponta = q; } });
|
|
354
|
+
const fx = 540 - 300, fy = PALCO_Y - 380; if (ponta) tf(P.lap, { x: fx + ponta.x - 20, y: fy + ponta.y - 130, r: 5 * Math.sin(t * 11) }); else tf(P.lap, { op: 0 });
|
|
355
|
+
P.lap.lot.toca(t, P.ta); const n = nasce(t, P.t0 + 0.2, 0.45); tf(P.ce, { op: n.op, s: n.s, y: n.y + 6 * Math.sin(t * 2), blur: n.blur }); P.ce.lot.toca(t, P.t0);
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
// ── 33 · "É isso. Tem alguma dúvida? Chama na DM!" + a DM do Instagram ──
|
|
359
|
+
legenda('33', ['É isso. Tem alguma dúvida?', ['Chama na', logo('instagram-icon.svg', { gira: false }), 'f|DM!', emoji('u1f970')]]);
|
|
360
|
+
palco(F0('33') - 0.1, 96.3, bloco({ h: 760, kind: 'abre',
|
|
361
|
+
monta(el, B) {
|
|
362
|
+
B.el = cartaoBranco(el, { inset: '0' });
|
|
363
|
+
B.el.innerHTML = `<div style="display:flex;align-items:center;gap:22px;padding:30px 36px;border-bottom:1.5px solid #EFEFEF">
|
|
364
|
+
<div style="width:92px;height:92px;border-radius:50%;padding:4px;background:conic-gradient(#F58529,#DD2A7B,#8134AF,#515BD4,#F58529)"><div style="width:100%;height:100%;border-radius:50%;border:4px solid #fff;box-sizing:border-box;background:linear-gradient(135deg,#7C5CFF,#D97757);display:flex;align-items:center;justify-content:center;font:700 38px ${SANS};color:#fff">J</div></div>
|
|
365
|
+
<div><div style="font:700 36px ${SANS};color:#111">seu.perfil <span style="color:#1D9BF0">✔</span></div><div style="font:500 28px ${SANS};color:#8E8E8E">Online agora</div></div></div>
|
|
366
|
+
<div id="msg" style="position:absolute;right:36px;top:300px;max-width:600px;padding:24px 34px;border-radius:44px;background:linear-gradient(135deg,#3B5BFF,#9B3BFF);color:#fff;font:500 36px/48px ${SANS}">Oi! Tenho uma dúvida sobre o vídeo 👀</div>
|
|
367
|
+
<div id="visto" style="position:absolute;right:44px;top:426px;font:500 26px ${SANS};color:#8E8E8E">Visto agora</div>
|
|
368
|
+
<div style="position:absolute;left:30px;right:30px;bottom:30px;height:96px;border-radius:48px;border:1.5px solid #E3E3E3;display:flex;align-items:center;padding:0 34px;font:500 34px ${SANS};color:#9A9A9A;box-sizing:border-box"><span id="cx">Mensagem…</span></div>`;
|
|
369
|
+
B.msg = el.querySelector('#msg'); B.visto = el.querySelector('#visto'); B.cx = el.querySelector('#cx'); B.txt = 'Oi! Tenho uma dúvida sobre o vídeo 👀';
|
|
370
|
+
B.ini = PF('33', 'Chama') - 0.1; B.env = PF('33', 'DM') + 0.75; ev(B.env, 'imessage', 0.2, 0.7);
|
|
371
|
+
},
|
|
372
|
+
seek(t, B) {
|
|
373
|
+
abreJanela(t, B.el, B.t, { lado: 0 });
|
|
374
|
+
const nd = Math.floor(clamp((t - B.ini) / 0.9) * [...B.txt].length), env = t >= B.env;
|
|
375
|
+
B.cx.innerHTML = env || nd === 0 ? 'Mensagem…' : `<span style="color:#111">${[...B.txt].slice(0, nd).join('')}</span><span style="display:inline-block;width:3px;height:36px;background:#3B5BFF;vertical-align:-6px;margin-left:2px"></span>`;
|
|
376
|
+
const u = seg(t, B.env, B.env + 0.45), k = ILL(u); tf(B.msg, { op: clamp(u * 3), y: (1 - k) * 210, s: lerp(0.85, 1, k), blur: (1 - k) * 10 });
|
|
377
|
+
const v = seg(t, B.env + 0.7, B.env + 0.95); tf(B.visto, { op: v, y: (1 - ILL(v)) * 10 });
|
|
378
|
+
} }), { y: 860 });
|
|
379
|
+
})();
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""video.py — prepara um VÍDEO MOSTRADO para a comp-a: quadros (img/filmes/<nome>/0001.jpg…) + som (audio/videos/<nome>.wav)
|
|
2
|
+
+ ficha (info.json) + índice para o HTML (img/filmes/_indice.js → window.FILMES_INFO).
|
|
3
|
+
|
|
4
|
+
python comp-a/video.py <fonte.mp4> --nome glass-hub --de 12.4 --dur 3.2 --w 960
|
|
5
|
+
python comp-a/video.py <fonte.mp4> --nome ai-opener-vertical --de 4 --dur 4 --crop 608:1080:656:0 --w 440 --sem-som
|
|
6
|
+
python comp-a/video.py --indice # só refaz o _indice.js
|
|
7
|
+
|
|
8
|
+
No HTML: janela('glass-hub', {som: true}) toca os quadros e registra o trecho de som; o audio_p1.py/audio_p2.py lê o
|
|
9
|
+
__events().videos e põe audio/videos/<nome>.wav no tempo certo (trilha para ou segue, conforme a janela).
|
|
10
|
+
"""
|
|
11
|
+
import argparse, json, subprocess, sys
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
AQUI = Path(__file__).resolve().parent
|
|
15
|
+
FIL, SOM = AQUI / "img" / "filmes", AQUI / "audio" / "videos"
|
|
16
|
+
sys.stdout.reconfigure(encoding="utf-8")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def indice():
|
|
20
|
+
info = {}
|
|
21
|
+
for d in sorted(FIL.iterdir()):
|
|
22
|
+
f = d / "info.json"
|
|
23
|
+
if d.is_dir() and f.exists():
|
|
24
|
+
info[d.name] = json.load(open(f, encoding="utf-8"))
|
|
25
|
+
(FIL / "_indice.js").write_text("window.FILMES_INFO = " + json.dumps(info, ensure_ascii=False, indent=0) + ";\n", encoding="utf-8")
|
|
26
|
+
print(len(info), "filmes no índice")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def tem_audio(fonte):
|
|
30
|
+
r = subprocess.run(["ffprobe", "-v", "error", "-select_streams", "a", "-show_entries", "stream=index", "-of", "csv=p=0", str(fonte)], capture_output=True, text=True)
|
|
31
|
+
return bool(r.stdout.strip())
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def main():
|
|
35
|
+
ap = argparse.ArgumentParser()
|
|
36
|
+
ap.add_argument("fonte", nargs="?"); ap.add_argument("--nome"); ap.add_argument("--de", type=float, default=0.0)
|
|
37
|
+
ap.add_argument("--dur", type=float, default=4.0); ap.add_argument("--w", type=int, default=960); ap.add_argument("--crop")
|
|
38
|
+
ap.add_argument("--fps", type=int, default=30); ap.add_argument("--q", type=int, default=3)
|
|
39
|
+
ap.add_argument("--sem-som", action="store_true"); ap.add_argument("--indice", action="store_true")
|
|
40
|
+
a = ap.parse_args()
|
|
41
|
+
if a.indice:
|
|
42
|
+
return indice()
|
|
43
|
+
fonte = Path(a.fonte).resolve(); out = FIL / a.nome
|
|
44
|
+
out.mkdir(parents=True, exist_ok=True)
|
|
45
|
+
for f in out.glob("*.jpg"):
|
|
46
|
+
f.unlink()
|
|
47
|
+
vf = (f"crop={a.crop}," if a.crop else "") + f"fps={a.fps},scale={a.w}:-2"
|
|
48
|
+
subprocess.run(["ffmpeg", "-v", "error", "-y", "-ss", str(a.de), "-t", str(a.dur), "-i", str(fonte), "-vf", vf, "-q:v", str(a.q), str(out / "%04d.jpg")], check=True)
|
|
49
|
+
n = len(list(out.glob("*.jpg")))
|
|
50
|
+
som = None
|
|
51
|
+
if not a.sem_som and tem_audio(fonte):
|
|
52
|
+
SOM.mkdir(parents=True, exist_ok=True); som = SOM / f"{a.nome}.wav"
|
|
53
|
+
subprocess.run(["ffmpeg", "-v", "error", "-y", "-ss", str(a.de), "-t", str(a.dur), "-i", str(fonte), "-vn", "-ac", "2", "-ar", "48000", str(som)], check=True)
|
|
54
|
+
from PIL import Image
|
|
55
|
+
w, h = Image.open(out / "0001.jpg").size
|
|
56
|
+
ficha = {"n": n, "fps": a.fps, "w": w, "h": h, "dur": round(n / a.fps, 3), "som": bool(som), "fonte": str(fonte), "de": a.de}
|
|
57
|
+
json.dump(ficha, open(out / "info.json", "w", encoding="utf-8"), ensure_ascii=False, indent=1)
|
|
58
|
+
print(a.nome, ficha); indice()
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
if __name__ == "__main__":
|
|
62
|
+
main()
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Motor do take único (HTML seek(t) + obturador + oráculo)
|
|
2
|
+
|
|
3
|
+
> Data: 2026-10-12 · Tipo: guia · Status: vigente
|
|
4
|
+
|
|
5
|
+
**Resumo:** motor para vídeos em take único (lousa contínua, sem corte entre cenas): biblioteca de movimentos `take.js`, render com desfoque de movimento de obturador real `render_take.py` e verificação automática `oraculo_take.py`. Método no [tutorial](../../linguagem-edicao/tutorial-take-unico.md); estudo de origem em [onetake](../../../05-pesquisa/referencias/2026-10-12-onetake-take-unico.md). Primeiro uso: abertura da Shelby ep. 1 ([projeto](../../projetos/2026-10-12-take-unico-onetake/README.md)).
|
|
6
|
+
|
|
7
|
+
## Arquivos
|
|
8
|
+
|
|
9
|
+
| Arquivo | O que faz |
|
|
10
|
+
|---|---|
|
|
11
|
+
| `take.js` | `window.TK`: `chain` e `carryTela` (13/10, da clínica: carry interpolado na tela, que não fica para trás no chicote), curvas (`board`, `saas`, expo, cúbicas, `bezier`), mola em forma fechada, entradas (`wordRise`, `letterDrop`, `popIn`, `drawOn`), carries (`morphRect`, `iris`, `zoomThrough`, `hop`, `gather`), contato (`impact`, `click`, `anelClique` + `desenhaAnel` do Click VFX, `shake`), câmera (`operador`, `alvos`, `flutuar`, `view`, `project`, `projectBox`, `screenTravel`, `lattice`) |
|
|
12
|
+
| `pecas.js` | **rascunho não adotado (13/10), só consulta.** `window.PK`: 16 saídas de cena e 12 microtruques do [catálogo](../../recursos/saidas-e-truques.md) como funções puras do tempo (`saida`/`entrada`, `colapsa`, `flash`, `estoura`, `zDive`, `flip`, `succao`, `glitchWipe`, `wipeBlur`, `cacaNiquel`, `tranco`, `playhead`, `stepper`…), `PK.aplica(el, estado)` e `PK.eventos(peça, t0, x)` para o som. Galeria: `projetos/2026-10-13-pecas-prontas/comp/` |
|
|
13
|
+
| `render_take.py` | renderiza a composição: cada quadro é a média, em luz linear, de 3–16 capturas no obturador de 180°; o número de capturas vem de `__motion`; quadro parado sai sem custo; corte seco continua seco; vários Chromium em paralelo |
|
|
14
|
+
| `oraculo_take.py` | continuidade por fronteira (carried/anchored/bare), enquadramento, velocidade, respiros, saltos e loudness; `--eventos` exporta `__events()` para a trilha |
|
|
15
|
+
|
|
16
|
+
## Contrato da composição (`comp/index.html`)
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
window.__meta = {dur, w: 1080, h: 1920, fps, batidas: [t…], inFrame: {id: [t0, t1]}};
|
|
20
|
+
window.__ready = Promise // fontes carregadas, imagens decodificadas, medidas feitas, câmera integrada
|
|
21
|
+
window.__seek = async t => { seek(t); return true; } // puro: 6,4 → 2,0 → 6,4 dá o mesmo quadro
|
|
22
|
+
window.__track = t => ({id: {x, y, w, h}}) // caixas NA TELA, só do que está visível
|
|
23
|
+
window.__motion = (t0, t1) => px // o máximo que algo anda na tela (câmera incluída)
|
|
24
|
+
window.__events = () => ({dur, events: [{t, kind, pan, v}]}) // para a trilha
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Query: `?t=6.4&hud` congela um quadro com o tempo e o zoom; `?play` roda ao vivo.
|
|
28
|
+
|
|
29
|
+
## Comandos
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
python render_take.py comp/index.html --stills 0.5,3,6.2 --cols 6 --out revisao/stills.png # olhar antes de renderizar
|
|
33
|
+
python oraculo_take.py comp/index.html # antes do render
|
|
34
|
+
python render_take.py comp/index.html --out render/previa.mp4 --previa --audio audio/mix.wav # PRÉVIA: ~22 s para 62 s de vídeo
|
|
35
|
+
python render_take.py comp/index.html --out render/final.mp4 --fps 60 --trechos 4 --turbo --audio audio/mix.wav # final: refaz só o que mudou
|
|
36
|
+
python oraculo_take.py comp/index.html --video render/final.mp4 # depois
|
|
37
|
+
python oraculo_take.py comp/index.html --eventos audio/eventos.json # eventos → trilha
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Velocidade: prévia, trechos e turbo (13/10)
|
|
41
|
+
|
|
42
|
+
Medido no Reel Soldier (62 s) no PC do autor; detalhes em [video-mais-rapido](../../projetos/2026-10-13-video-mais-rapido/README.md).
|
|
43
|
+
|
|
44
|
+
| Modo | Para quê | Tempo (62 s de vídeo) | Antes |
|
|
45
|
+
|---|---|---|---|
|
|
46
|
+
| `--previa` | ver a montagem, o tempo e o som; sem desfoque de movimento, JPEG, resolução cheia | **~22 s** | rascunho a 30 qps: 455–480 s |
|
|
47
|
+
| `--fps 60 --trechos 4 --turbo`, 1ª vez | final completo com obturador de 180° | **~6,5 min** (82 s são as digitais) | ~15 min (4 navegadores, em duas metades) |
|
|
48
|
+
| o mesmo, depois de uma mudança | só os trechos de 4 s que mudaram são refeitos; o resto vem de `render/_trechos/` | ver projeto | o final inteiro de novo |
|
|
49
|
+
|
|
50
|
+
- **Fluxo:** prévia a cada ajuste → final com `--trechos 4` uma vez → ajuste pequeno → o mesmo comando de novo (só refaz o trecho mexido). Não troque `--fps`, `--crf` nem a escala entre versões: o cache é por configuração.
|
|
51
|
+
- **Captura pelo CDP** (`optimizeForSpeed`): PNG sem perda, idêntica bit a bit à do Playwright. **GPU ligada por padrão** (Chromium com ANGLE/D3D11 na RTX 5070): ~1,6× mais rápida; a olho nu igual (PSNR ≥ 45 dB; varia só a reamostragem de foto). `--sem-gpu` volta ao SwiftShader.
|
|
52
|
+
- **Digitais:** hash de cada quadro numa passada na CPU, em escala 1, JPEG, sempre com 8 navegadores. Na GPU, em escala reduzida ou com outro número de navegadores o mesmo quadro sai um pouco diferente e o cache refaria trechos à toa (medido).
|
|
53
|
+
- **Memória:** cada navegador do final compromete ~1,8 GB. O script olha a memória livre do Windows e usa menos navegadores quando falta (reserva 6 GB). Acima de ~12 navegadores não fica mais rápido e alguns travam; `--turbo` = 12.
|
|
54
|
+
- Navegador que cai num render longo reabre sozinho e continua de onde parou (até 4 tentativas).
|
|
55
|
+
|
|
56
|
+
## O que mordeu (12/10)
|
|
57
|
+
|
|
58
|
+
- O ponto saiu do quadro aos 2,0 s: a pena andava mais rápido que a mola da câmera. Resolvido com a chave da câmera 0,2 s **antes** e a mola mais rápida (ω 4,6). O oráculo pega isso pelo `inFrame`.
|
|
59
|
+
- `__track` com objeto invisível engana a continuidade: o ponto "teleportava" escondido e o `__motion` pedia 16 capturas à toa. Só rastreie o que está visível.
|
|
60
|
+
- O interior da barbearia mostrava o logo antes da revelação: corte a foto pelo enquadramento (`width:200%`), sem trocar o asset.
|
|
61
|
+
- Foto que encolhe até virar avatar deixa a tela vazia: o carry precisa terminar **grande** (a foto virou o card "sua empresa").
|
|
62
|
+
- Texto DOM até ~11× de zoom ficou nítido (o Chrome rasteriza de novo a cada quadro sem `will-change`); não use `filter: blur()` em elemento que sai muito da tela.
|
|
63
|
+
- (13/10) Oráculo lia o vídeo inteiro na memória e adivinhava o fps pelo número de quadros: agora lê em fluxo e pega o fps do arquivo. `render_take.py --audio` cortava o vídeo no fim da voz (`-shortest`): agora o áudio é completado com silêncio (`apad`).
|
|
64
|
+
- (13/10) Grão de tela cheia em canvas deixava cada captura ~4× mais lenta: grão vai na junção (`ffmpeg -vf noise=c0s=3`). Telegram aceita até 50 MB: gerar `-leve.mp4` em dois passes.
|
|
65
|
+
- Terminal do Windows em cp1252: os scripts chamam `sys.stdout.reconfigure(encoding="utf-8")`.
|