primocode 8.12.0 → 8.14.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/lib/claude-engine.js +114 -7
- package/package.json +2 -2
- package/studio/ferramentas/01-modelos.md +47 -2
- package/studio/ferramentas/corte.md +27 -0
- package/studio/server.py +114 -2
- package/studio/web/core.css +133 -2
- package/studio/web/js/editor.js +614 -99
- package/studio/web/js/ferramentas.js +4 -1
- package/studio/web/js/render.js +283 -7
- package/studio/web/render.css +47 -0
package/studio/web/js/editor.js
CHANGED
|
@@ -13,6 +13,220 @@
|
|
|
13
13
|
var ARQ = null, doc = null;
|
|
14
14
|
var iCena = 0, iAba = 0, sel = null; // sel = índice do elemento/bloco
|
|
15
15
|
var escala = 1, historico = [], futuro = [], tSalvar = null, sujo = false;
|
|
16
|
+
var chaveSel = 0; // chave de animação em edição
|
|
17
|
+
|
|
18
|
+
/* ------------------------------------------------------------- vocabulário
|
|
19
|
+
Os menus mostravam o valor cru do documento: "grao", "kb-aproxima", e uma
|
|
20
|
+
opção vazia no topo que não dizia nada. Quem abria o editor via três
|
|
21
|
+
caixas em branco e não tinha como adivinhar o que havia dentro. Cada
|
|
22
|
+
opção agora é [valor, nome em português], e o vazio virou uma frase.
|
|
23
|
+
|
|
24
|
+
Os valores NÃO mudam — são os mesmos que o render.css entende. O que
|
|
25
|
+
muda é só o que a pessoa lê. */
|
|
26
|
+
var OPC = {
|
|
27
|
+
transicao: [["", "Nenhuma (corte seco)"], ["fade", "Suave"], ["deslizar", "Deslizar para o lado"],
|
|
28
|
+
["subir", "Subir"], ["zoom", "Aproximar"], ["limpar", "Varrer"],
|
|
29
|
+
["persiana", "Persiana"], ["flash", "Flash"]],
|
|
30
|
+
fundo: [["", "Sem fundo"], ["malha", "Malha"], ["aurora", "Aurora"], ["raios", "Raios"],
|
|
31
|
+
["brilho", "Brilho"], ["ondas", "Ondas"], ["grade", "Grade"], ["pontos", "Pontos"],
|
|
32
|
+
["vinheta", "Vinheta"], ["ruido", "Ruído"]],
|
|
33
|
+
acabamento: [["", "Nenhum"], ["grao", "Granulado"], ["vinheta", "Vinheta"],
|
|
34
|
+
["scanlines", "Linhas de TV"], ["luz", "Vazamento de luz"], ["brilho", "Brilho"],
|
|
35
|
+
["moldura", "Moldura"]],
|
|
36
|
+
animacao: [["", "Nenhuma"], ["subir", "Subir"], ["surgir", "Surgir"], ["esq", "Da esquerda"],
|
|
37
|
+
["dir", "Da direita"], ["zoom", "Aproximar"], ["desfoque", "Sair do desfoque"],
|
|
38
|
+
["cair", "Cair"], ["girar", "Girar"], ["saltar", "Saltar"], ["revelar", "Revelar"],
|
|
39
|
+
["cortina", "Cortina"], ["elastico", "Elástico"]],
|
|
40
|
+
kenburns: [["", "Parado"], ["aproxima", "Aproximar"], ["afasta", "Afastar"],
|
|
41
|
+
["esquerda", "Correr para a esquerda"], ["direita", "Correr para a direita"],
|
|
42
|
+
["cima", "Subir devagar"]],
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/* Menu com nome legível. Aceita "valor" ou ["valor", "Nome"]. */
|
|
46
|
+
function menu(opcoes, atual, aoMudar) {
|
|
47
|
+
var s = el("select", "campo");
|
|
48
|
+
opcoes.forEach(function (o) {
|
|
49
|
+
var valor = Array.isArray(o) ? o[0] : o;
|
|
50
|
+
var nome = Array.isArray(o) ? o[1] : String(o);
|
|
51
|
+
var op = el("option", "", nome);
|
|
52
|
+
op.value = valor;
|
|
53
|
+
if (String(valor) === String(atual == null ? "" : atual)) op.selected = true;
|
|
54
|
+
s.appendChild(op);
|
|
55
|
+
});
|
|
56
|
+
s.onchange = function () { aoMudar(s.value); };
|
|
57
|
+
return s;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* ---------------------------------------------------------------- arrastar
|
|
61
|
+
Reordenar não existia. As camadas e as cenas só respondiam a clique, e
|
|
62
|
+
quem tentava arrastar — que é o gesto óbvio — não acontecia nada. Este
|
|
63
|
+
é o mesmo comportamento para as duas listas: pega, mostra onde vai cair,
|
|
64
|
+
solta, reordena e salva. */
|
|
65
|
+
function arrastavel(no, indice, mover) {
|
|
66
|
+
no.draggable = true;
|
|
67
|
+
no.addEventListener("dragstart", function (ev) {
|
|
68
|
+
ev.dataTransfer.effectAllowed = "move";
|
|
69
|
+
ev.dataTransfer.setData("text/plain", String(indice));
|
|
70
|
+
no.classList.add("arrastando");
|
|
71
|
+
});
|
|
72
|
+
no.addEventListener("dragend", function () {
|
|
73
|
+
no.classList.remove("arrastando");
|
|
74
|
+
Array.prototype.forEach.call(document.querySelectorAll(".alvo-solta"),
|
|
75
|
+
function (n) { n.classList.remove("alvo-solta"); });
|
|
76
|
+
});
|
|
77
|
+
no.addEventListener("dragover", function (ev) {
|
|
78
|
+
ev.preventDefault();
|
|
79
|
+
ev.dataTransfer.dropEffect = "move";
|
|
80
|
+
no.classList.add("alvo-solta");
|
|
81
|
+
});
|
|
82
|
+
no.addEventListener("dragleave", function () { no.classList.remove("alvo-solta"); });
|
|
83
|
+
no.addEventListener("drop", function (ev) {
|
|
84
|
+
ev.preventDefault();
|
|
85
|
+
ev.stopPropagation();
|
|
86
|
+
no.classList.remove("alvo-solta");
|
|
87
|
+
var de = parseInt(ev.dataTransfer.getData("text/plain"), 10);
|
|
88
|
+
if (isNaN(de) || de === indice) return;
|
|
89
|
+
mover(de, indice);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/* Tira o item de uma posição e põe na outra, na mesma lista. */
|
|
94
|
+
function reordenar(lista, de, para) {
|
|
95
|
+
var item = lista.splice(de, 1)[0];
|
|
96
|
+
lista.splice(para, 0, item);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function nomeDaOpcao(opcoes, valor) {
|
|
100
|
+
for (var i = 0; i < opcoes.length; i++) {
|
|
101
|
+
if (String(opcoes[i][0]) === String(valor)) return opcoes[i][1];
|
|
102
|
+
}
|
|
103
|
+
return String(valor);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/* ----------------------------------------------------------------- modelos
|
|
107
|
+
Cena pronta, com texto de exemplo, para pôr e trocar as palavras.
|
|
108
|
+
|
|
109
|
+
"Nova cena" dava uma cena VAZIA, e cena vazia é a tela em branco: quem
|
|
110
|
+
abre não sabe por onde começar e o resultado sai desalinhado do resto.
|
|
111
|
+
Estes modelos são os mesmos arranjos que a spec ensina à IA — a pessoa
|
|
112
|
+
agora alcança os mesmos, com um clique. */
|
|
113
|
+
var MODELOS = [
|
|
114
|
+
{ id: "capa", nome: "Abertura", para: ["corte", "prisma", "tela"],
|
|
115
|
+
cena: { layout: "capa", duracao: 4, fundo: "malha", transicao: "fade", elementos: [
|
|
116
|
+
{ tipo: "texto", papel: "chapeu", texto: "NOME DA EMPRESA", an: "surgir" },
|
|
117
|
+
{ tipo: "texto", papel: "titulo", texto: "O título que abre", an: "subir" },
|
|
118
|
+
{ tipo: "texto", papel: "apoio", texto: "Uma frase que explica do que se trata.", an: "subir" }] } },
|
|
119
|
+
{ id: "titulo-lista", nome: "Título e tópicos", para: ["corte", "prisma"],
|
|
120
|
+
cena: { layout: "titulo", duracao: 6, transicao: "deslizar", elementos: [
|
|
121
|
+
{ tipo: "texto", papel: "titulo", texto: "Uma ideia por cena" },
|
|
122
|
+
{ tipo: "lista", itens: ["Primeiro ponto", "Segundo ponto", "Terceiro ponto"],
|
|
123
|
+
revelar: true, intervalo: 0.9 }] } },
|
|
124
|
+
{ id: "numeros", nome: "Números que impressionam", para: ["corte", "prisma", "tela"],
|
|
125
|
+
cena: { layout: "centro", duracao: 5, fundo: "brilho", transicao: "zoom", elementos: [
|
|
126
|
+
{ tipo: "numeros", itens: [
|
|
127
|
+
{ valor: "3x", rotulo: "mais rápido" },
|
|
128
|
+
{ valor: "40%", rotulo: "menos custo" },
|
|
129
|
+
{ valor: "24h", rotulo: "no ar" }], an: "saltar" }] } },
|
|
130
|
+
{ id: "citacao", nome: "Citação", para: ["corte", "prisma", "tela"],
|
|
131
|
+
cena: { layout: "citacao", duracao: 5, tema: "papel", transicao: "limpar", elementos: [
|
|
132
|
+
{ tipo: "citacao", texto: "A frase que alguém disse e vale repetir.",
|
|
133
|
+
autor: "Quem disse", an: "desfoque" }] } },
|
|
134
|
+
{ id: "cartoes", nome: "Três cartões", para: ["corte", "prisma"],
|
|
135
|
+
cena: { layout: "titulo", duracao: 6, transicao: "subir", elementos: [
|
|
136
|
+
{ tipo: "texto", papel: "titulo", texto: "O que fazemos" },
|
|
137
|
+
{ tipo: "cartoes", itens: [
|
|
138
|
+
{ titulo: "Primeiro", texto: "Uma linha explicando." },
|
|
139
|
+
{ titulo: "Segundo", texto: "Uma linha explicando." },
|
|
140
|
+
{ titulo: "Terceiro", texto: "Uma linha explicando." }], revelar: true }] } },
|
|
141
|
+
{ id: "secao", nome: "Divisor de seção", para: ["corte", "prisma"],
|
|
142
|
+
cena: { layout: "secao", duracao: 2.5, fundo: "aurora", transicao: "flash", elementos: [
|
|
143
|
+
{ tipo: "texto", papel: "titulo", texto: "Parte dois", an: "zoom" }] } },
|
|
144
|
+
{ id: "foto", nome: "Foto com frase", para: ["corte", "prisma", "tela"],
|
|
145
|
+
cena: { layout: "capa", duracao: 5, acabamento: "vinheta", transicao: "fade",
|
|
146
|
+
fundo: "vinheta", kenburns: "aproxima", elementos: [
|
|
147
|
+
{ tipo: "texto", papel: "titulo", texto: "A frase por cima da imagem", an: "subir" }] } },
|
|
148
|
+
{ id: "grafico", nome: "Gráfico", para: ["corte", "prisma"],
|
|
149
|
+
cena: { layout: "titulo", duracao: 6, elementos: [
|
|
150
|
+
{ tipo: "texto", papel: "titulo", texto: "Como evoluiu" },
|
|
151
|
+
{ tipo: "grafico", grafico: "barras",
|
|
152
|
+
dados: [["Jan", 30], ["Fev", 52], ["Mar", 71], ["Abr", 96]] }] } },
|
|
153
|
+
{ id: "fecho", nome: "Fecho / chamada", para: ["corte", "prisma", "tela"],
|
|
154
|
+
cena: { layout: "centro", duracao: 4, fundo: "raios", transicao: "zoom", elementos: [
|
|
155
|
+
{ tipo: "texto", papel: "titulo", texto: "Vamos conversar", an: "zoom" },
|
|
156
|
+
{ tipo: "texto", papel: "apoio", texto: "seu@email.com", an: "surgir" }] } },
|
|
157
|
+
];
|
|
158
|
+
|
|
159
|
+
function modelosDaFerramenta() {
|
|
160
|
+
return MODELOS.filter(function (m) { return m.para.indexOf(SLUG) !== -1; });
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* A galeria: cada modelo desenhado de verdade, com o renderizador do
|
|
164
|
+
produto. Miniatura falsa mentiria sobre o resultado. */
|
|
165
|
+
function abrirModelos() {
|
|
166
|
+
var fundo = el("div", "folha on");
|
|
167
|
+
var caixa = el("div", "caixa larga");
|
|
168
|
+
caixa.appendChild(el("h3", "", "Escolha um começo"));
|
|
169
|
+
caixa.appendChild(el("p", "sub",
|
|
170
|
+
"Cada um entra como cena nova, com texto de exemplo para você trocar."));
|
|
171
|
+
|
|
172
|
+
var grade = el("div", "modelos");
|
|
173
|
+
var W = doc.largura || 1920, H = doc.altura || 1080;
|
|
174
|
+
var pendentes = [];
|
|
175
|
+
|
|
176
|
+
// Primeiro a cena em branco: quem quer começar do zero não deve ter de
|
|
177
|
+
// procurar.
|
|
178
|
+
var vazio = el("button", "modelo");
|
|
179
|
+
var cxV = el("div", "caixa-mini vazia", svgIcone("mais"));
|
|
180
|
+
vazio.appendChild(cxV);
|
|
181
|
+
vazio.appendChild(el("span", "", "Cena em branco"));
|
|
182
|
+
vazio.onclick = function () { fechar(); novaCena(null); };
|
|
183
|
+
grade.appendChild(vazio);
|
|
184
|
+
|
|
185
|
+
modelosDaFerramenta().forEach(function (m) {
|
|
186
|
+
var b = el("button", "modelo");
|
|
187
|
+
var cx = el("div", "caixa-mini");
|
|
188
|
+
var mini = renderCena(m.cena, doc, {});
|
|
189
|
+
cx.appendChild(mini);
|
|
190
|
+
b.appendChild(cx);
|
|
191
|
+
b.appendChild(el("span", "", m.nome));
|
|
192
|
+
b.onclick = function () { fechar(); novaCena(m); };
|
|
193
|
+
grade.appendChild(b);
|
|
194
|
+
pendentes.push([cx, mini]);
|
|
195
|
+
});
|
|
196
|
+
caixa.appendChild(grade);
|
|
197
|
+
|
|
198
|
+
var rodape = el("div", "rodape");
|
|
199
|
+
var bFechar = el("button", "btn", "Fechar");
|
|
200
|
+
bFechar.onclick = fechar;
|
|
201
|
+
rodape.appendChild(bFechar);
|
|
202
|
+
caixa.appendChild(rodape);
|
|
203
|
+
fundo.appendChild(caixa);
|
|
204
|
+
document.body.appendChild(fundo);
|
|
205
|
+
|
|
206
|
+
// A escala só dá para calcular depois que o nó está no documento e tem
|
|
207
|
+
// largura de verdade.
|
|
208
|
+
pendentes.forEach(function (p) {
|
|
209
|
+
var larg = p[0].clientWidth || 220;
|
|
210
|
+
p[0].style.height = Math.round(larg * H / W) + "px";
|
|
211
|
+
p[1].style.transform = "scale(" + (larg / W) + ")";
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
function fechar() { fundo.remove(); document.removeEventListener("keydown", esc); }
|
|
215
|
+
function esc(ev) { if (ev.key === "Escape") fechar(); }
|
|
216
|
+
document.addEventListener("keydown", esc);
|
|
217
|
+
fundo.onclick = function (ev) { if (ev.target === fundo) fechar(); };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function novaCena(modelo) {
|
|
221
|
+
var base = modelo && modelo.cena
|
|
222
|
+
? JSON.parse(JSON.stringify(modelo.cena))
|
|
223
|
+
: { layout: F.layoutPadrao, duracao: 4, elementos: [] };
|
|
224
|
+
if (!F.tempo) delete base.duracao;
|
|
225
|
+
mudar(function () {
|
|
226
|
+
doc.cenas.splice(iCena + 1, 0, base);
|
|
227
|
+
iCena++; sel = null;
|
|
228
|
+
});
|
|
229
|
+
}
|
|
16
230
|
|
|
17
231
|
/* ------------------------------------------------------------ utilidades */
|
|
18
232
|
|
|
@@ -236,48 +450,80 @@
|
|
|
236
450
|
lista.innerHTML = "";
|
|
237
451
|
|
|
238
452
|
if (F.modelo === "cena") {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
453
|
+
// No Corte as cenas já vivem na tira do rodapé. Repetir a mesma lista
|
|
454
|
+
// aqui era o pior tipo de duplicação: duas coisas iguais, lado a lado,
|
|
455
|
+
// sem dizer qual manda. Aqui ficam só as camadas — que é o que faltava
|
|
456
|
+
// ter espaço.
|
|
457
|
+
var soCamadas = !!F.tempo;
|
|
458
|
+
if (!soCamadas) {
|
|
459
|
+
$("#painel-rotulo").textContent = SLUG === "prisma" ? "Slides" : "Pranchas";
|
|
460
|
+
var W = doc.largura || 1920, H = doc.altura || 1080;
|
|
461
|
+
var minis = [];
|
|
462
|
+
(doc.cenas || []).forEach(function (c, i) {
|
|
463
|
+
var m = el("div", "miniatura" + (i === iCena ? " ativa" : ""));
|
|
464
|
+
var caixa = el("div", "caixa");
|
|
465
|
+
var mini = renderCena(c, doc, {});
|
|
466
|
+
caixa.appendChild(mini);
|
|
467
|
+
m.appendChild(caixa);
|
|
468
|
+
m.appendChild(el("div", "num", String(i + 1)));
|
|
469
|
+
m.onclick = function () { iCena = i; sel = null; desenhar(); };
|
|
470
|
+
arrastavel(m, i, function (de, para) {
|
|
471
|
+
mudar(function () { reordenar(doc.cenas, de, para); });
|
|
472
|
+
iCena = para; sel = null; desenhar();
|
|
473
|
+
});
|
|
474
|
+
lista.appendChild(m);
|
|
475
|
+
minis.push([caixa, mini]);
|
|
476
|
+
});
|
|
477
|
+
// a escala vem da largura real da caixa, que depende do painel
|
|
478
|
+
minis.forEach(function (par) {
|
|
479
|
+
var larg = par[0].clientWidth || 160;
|
|
480
|
+
par[0].style.height = Math.round(larg * H / W) + "px";
|
|
481
|
+
par[1].style.transform = "scale(" + (larg / W) + ")";
|
|
482
|
+
});
|
|
483
|
+
}
|
|
259
484
|
|
|
260
|
-
|
|
485
|
+
var els = cena().elementos || [];
|
|
486
|
+
$("#painel-rotulo").textContent = soCamadas ? "Camadas da cena" : $("#painel-rotulo").textContent;
|
|
487
|
+
if (!soCamadas && els.length) {
|
|
261
488
|
var tit = el("div", "rotulo");
|
|
262
|
-
tit.style.cssText = "padding:
|
|
489
|
+
tit.style.cssText = "padding:14px 4px 6px";
|
|
263
490
|
tit.textContent = "Camadas";
|
|
264
491
|
lista.appendChild(tit);
|
|
265
|
-
(cena().elementos || []).forEach(function (e2, i) {
|
|
266
|
-
var c = el("div", "camada" + (i === sel ? " ativa" : ""),
|
|
267
|
-
svgIcone(iconeDe(e2)) + "<span>" + rotuloDe(e2) + "</span>");
|
|
268
|
-
c.onclick = function () { sel = i; desenhar(); };
|
|
269
|
-
lista.appendChild(c);
|
|
270
|
-
});
|
|
271
492
|
}
|
|
493
|
+
if (!els.length) {
|
|
494
|
+
lista.appendChild(el("div", "nada", soCamadas
|
|
495
|
+
? "Cena vazia. Use a barra da esquerda para pôr texto, lista, número ou imagem."
|
|
496
|
+
: "Nada nesta prancha ainda."));
|
|
497
|
+
}
|
|
498
|
+
// De cima para baixo na lista = da frente para trás na tela, que é como
|
|
499
|
+
// todo editor de camada se comporta. O documento guarda o contrário.
|
|
500
|
+
els.slice().reverse().forEach(function (e2, ordem) {
|
|
501
|
+
var i = els.length - 1 - ordem;
|
|
502
|
+
var c = el("div", "camada" + (i === sel ? " ativa" : ""),
|
|
503
|
+
svgIcone(iconeDe(e2)) + "<span>" + rotuloDe(e2) + "</span>");
|
|
504
|
+
c.appendChild(botoesDaCamada(els, i));
|
|
505
|
+
c.onclick = function () { sel = i; desenhar(); };
|
|
506
|
+
arrastavel(c, i, function (de, para) {
|
|
507
|
+
mudar(function () { reordenar(els, de, para); });
|
|
508
|
+
sel = para; desenhar();
|
|
509
|
+
});
|
|
510
|
+
lista.appendChild(c);
|
|
511
|
+
});
|
|
272
512
|
return;
|
|
273
513
|
}
|
|
274
514
|
|
|
275
515
|
if (F.modelo === "documento") {
|
|
276
516
|
$("#painel-rotulo").textContent = "Blocos";
|
|
277
|
-
|
|
517
|
+
var blocos = doc.blocos || [];
|
|
518
|
+
blocos.forEach(function (b, i) {
|
|
278
519
|
var c = el("div", "camada" + (i === sel ? " ativa" : ""),
|
|
279
520
|
svgIcone(iconeDe(b)) + "<span>" + rotuloDe(b) + "</span>");
|
|
521
|
+
c.appendChild(botoesDaCamada(blocos, i));
|
|
280
522
|
c.onclick = function () { sel = i; desenhar(); };
|
|
523
|
+
arrastavel(c, i, function (de, para) {
|
|
524
|
+
mudar(function () { reordenar(blocos, de, para); });
|
|
525
|
+
sel = para; desenhar();
|
|
526
|
+
});
|
|
281
527
|
lista.appendChild(c);
|
|
282
528
|
});
|
|
283
529
|
return;
|
|
@@ -292,6 +538,41 @@
|
|
|
292
538
|
});
|
|
293
539
|
}
|
|
294
540
|
|
|
541
|
+
/* Subir, descer, duplicar e apagar direto na camada.
|
|
542
|
+
|
|
543
|
+
Arrastar é o gesto natural, mas não é o único: num item pequeno o
|
|
544
|
+
arrasto erra o alvo, e teclado não arrasta. Os botões fazem o mesmo
|
|
545
|
+
trabalho sem depender de pontaria. */
|
|
546
|
+
function botoesDaCamada(lista, i) {
|
|
547
|
+
var caixa = el("div", "camada-acoes");
|
|
548
|
+
function bt(icone, titulo, fn) {
|
|
549
|
+
var b = el("button", "", svgIcone(icone));
|
|
550
|
+
b.title = titulo;
|
|
551
|
+
b.onclick = function (ev) { ev.stopPropagation(); fn(); };
|
|
552
|
+
caixa.appendChild(b);
|
|
553
|
+
return b;
|
|
554
|
+
}
|
|
555
|
+
bt("subir", "Trazer para a frente", function () {
|
|
556
|
+
if (i >= lista.length - 1) return;
|
|
557
|
+
mudar(function () { reordenar(lista, i, i + 1); });
|
|
558
|
+
sel = i + 1; desenhar();
|
|
559
|
+
}).disabled = i >= lista.length - 1;
|
|
560
|
+
bt("descer", "Mandar para trás", function () {
|
|
561
|
+
if (i <= 0) return;
|
|
562
|
+
mudar(function () { reordenar(lista, i, i - 1); });
|
|
563
|
+
sel = i - 1; desenhar();
|
|
564
|
+
}).disabled = i <= 0;
|
|
565
|
+
bt("duplicar", "Duplicar", function () {
|
|
566
|
+
mudar(function () { lista.splice(i + 1, 0, JSON.parse(JSON.stringify(lista[i]))); });
|
|
567
|
+
sel = i + 1; desenhar();
|
|
568
|
+
});
|
|
569
|
+
bt("lixeira", "Apagar", function () {
|
|
570
|
+
mudar(function () { lista.splice(i, 1); });
|
|
571
|
+
sel = null; desenhar();
|
|
572
|
+
});
|
|
573
|
+
return caixa;
|
|
574
|
+
}
|
|
575
|
+
|
|
295
576
|
function iconeDe(e) {
|
|
296
577
|
return { texto: "texto", lista: "lista", cartoes: "cartao", numeros: "numero",
|
|
297
578
|
tabela: "tabela", codigo: "codigo", citacao: "citacao", forma: "quadrado",
|
|
@@ -333,13 +614,20 @@
|
|
|
333
614
|
wrap.appendChild(quadro);
|
|
334
615
|
|
|
335
616
|
/* seleção e interação */
|
|
336
|
-
|
|
617
|
+
// Índice do documento -> nó na tela, pelo carimbo que o renderizador
|
|
618
|
+
// deixa em data-i.
|
|
619
|
+
//
|
|
620
|
+
// Antes isto era deduzido pela ordem: o k-ésimo nó era o k-ésimo
|
|
621
|
+
// elemento. A dedução quebra em todo layout de fluxo, porque eles
|
|
622
|
+
// desenham o cabeçalho (chapéu e título) antes do corpo — a ordem na
|
|
623
|
+
// tela não é a do documento. Quem clicava num texto via o editor
|
|
624
|
+
// selecionar outro, e editava o errado. Foi medido: nas três camadas da
|
|
625
|
+
// cena de teste, as três apontavam para o elemento vizinho.
|
|
337
626
|
var lista = cena().elementos || [];
|
|
338
|
-
var mapa =
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
if (
|
|
342
|
-
mapa.push(els[k++] || null);
|
|
627
|
+
var mapa = lista.map(function () { return null; });
|
|
628
|
+
Array.prototype.forEach.call(cn.querySelectorAll(".el[data-i]"), function (no) {
|
|
629
|
+
var i = +no.dataset.i;
|
|
630
|
+
if (i >= 0 && i < mapa.length && !mapa[i]) mapa[i] = no;
|
|
343
631
|
});
|
|
344
632
|
|
|
345
633
|
mapa.forEach(function (no, i) {
|
|
@@ -463,13 +751,8 @@
|
|
|
463
751
|
var esp = el("div"); esp.style.flex = "1"; b.appendChild(esp);
|
|
464
752
|
|
|
465
753
|
if (F.modelo === "cena") {
|
|
466
|
-
var addC = el("button", "btn", svgIcone("mais") + " Nova cena");
|
|
467
|
-
addC.onclick =
|
|
468
|
-
mudar(function () {
|
|
469
|
-
doc.cenas.splice(iCena + 1, 0, { layout: F.layoutPadrao, elementos: [] });
|
|
470
|
-
iCena++; sel = null;
|
|
471
|
-
});
|
|
472
|
-
};
|
|
754
|
+
var addC = el("button", "btn primo", svgIcone("mais") + " Nova cena");
|
|
755
|
+
addC.onclick = abrirModelos;
|
|
473
756
|
var dup = el("button", "btn", svgIcone("duplicar"));
|
|
474
757
|
dup.title = "Duplicar cena";
|
|
475
758
|
dup.onclick = function () {
|
|
@@ -610,12 +893,28 @@
|
|
|
610
893
|
caixa.appendChild(mini);
|
|
611
894
|
clipe.appendChild(caixa);
|
|
612
895
|
clipe.appendChild(el("div", "dur", dur + "s"));
|
|
896
|
+
// A transição aparece NA tira, entre um clipe e outro, que é onde ela
|
|
897
|
+
// acontece. Escondida num menu do inspetor, ninguém achava.
|
|
898
|
+
if (c.transicao && c.transicao !== "corte") {
|
|
899
|
+
clipe.appendChild(el("div", "tr-marca", nomeDaOpcao(OPC.transicao, c.transicao)));
|
|
900
|
+
}
|
|
613
901
|
clipe.onclick = function () { iCena = i; sel = null; desenhar(); };
|
|
902
|
+
clipe.ondblclick = function () { Player && Player.tocar && Player.tocar(); };
|
|
903
|
+
arrastavel(clipe, i, function (de, para) {
|
|
904
|
+
mudar(function () { reordenar(doc.cenas, de, para); });
|
|
905
|
+
iCena = para; sel = null; desenhar();
|
|
906
|
+
});
|
|
614
907
|
t.appendChild(clipe);
|
|
615
908
|
});
|
|
909
|
+
|
|
910
|
+
var mais = el("button", "clipe-mais", svgIcone("mais"));
|
|
911
|
+
mais.title = "Nova cena";
|
|
912
|
+
mais.onclick = abrirModelos;
|
|
913
|
+
t.appendChild(mais);
|
|
914
|
+
|
|
616
915
|
var info = el("div");
|
|
617
916
|
info.style.cssText = "margin-left:12px;font-size:12px;color:var(--fraco2);white-space:nowrap";
|
|
618
|
-
info.textContent = "total " + total.toFixed(1) + "s";
|
|
917
|
+
info.textContent = "total " + total.toFixed(1) + "s · arraste para reordenar";
|
|
619
918
|
t.appendChild(info);
|
|
620
919
|
}
|
|
621
920
|
|
|
@@ -790,10 +1089,44 @@
|
|
|
790
1089
|
return t;
|
|
791
1090
|
}
|
|
792
1091
|
|
|
793
|
-
|
|
1092
|
+
/* O menu escreve em português.
|
|
1093
|
+
|
|
1094
|
+
Antes ele mostrava o valor cru do documento — "grao", "kb-aproxima",
|
|
1095
|
+
"left" — e uma opção vazia no topo, sem texto nenhum. Três caixas em
|
|
1096
|
+
branco no inspetor, e nada dizendo o que havia dentro delas.
|
|
1097
|
+
|
|
1098
|
+
O dicionário traduz na hora de mostrar; o valor gravado continua o
|
|
1099
|
+
mesmo, porque é ele que o render.css entende. `vazio` é o texto da
|
|
1100
|
+
opção "", que muda com o contexto: em Fundo é "Sem fundo", em
|
|
1101
|
+
Acabamento é "Nenhum". */
|
|
1102
|
+
var NOMES = {
|
|
1103
|
+
malha: "Malha", brilho: "Brilho", grade: "Grade", pontos: "Pontos", suave: "Suave",
|
|
1104
|
+
vinheta: "Vinheta", aurora: "Aurora", raios: "Raios", ondas: "Ondas", ruido: "Ruído",
|
|
1105
|
+
grao: "Granulado", scanlines: "Linhas de TV", luz: "Vazamento de luz", moldura: "Moldura",
|
|
1106
|
+
horizontal: "Na horizontal", vertical: "Na vertical",
|
|
1107
|
+
chapeu: "Chapéu", titulo: "Título", subtitulo: "Subtítulo", apoio: "Apoio",
|
|
1108
|
+
corpo: "Corpo", legenda: "Legenda",
|
|
1109
|
+
left: "À esquerda", center: "Ao centro", right: "À direita",
|
|
1110
|
+
cobrir: "Preencher", conter: "Caber inteira",
|
|
1111
|
+
cinza: "Preto e branco", sepia: "Sépia", contraste: "Contraste", desbotado: "Desbotado",
|
|
1112
|
+
aproximar: "Aproximar", afastar: "Afastar", esquerda: "Para a esquerda",
|
|
1113
|
+
direita: "Para a direita", cima: "Para cima",
|
|
1114
|
+
retangulo: "Retângulo", circulo: "Círculo", triangulo: "Triângulo",
|
|
1115
|
+
linha: "Linha", seta: "Seta",
|
|
1116
|
+
barras: "Barras", area: "Área", pizza: "Pizza", rosca: "Rosca",
|
|
1117
|
+
curva: "Curva", reta: "Reta", cotovelo: "Cotovelo",
|
|
1118
|
+
texto: "Texto", inteiro: "Número inteiro", decimal: "Decimal",
|
|
1119
|
+
moeda: "Dinheiro", percentual: "Porcentagem",
|
|
1120
|
+
"não": "Não", sim: "Sim",
|
|
1121
|
+
300: "Fino", 400: "Normal", 500: "Médio", 600: "Semi-negrito",
|
|
1122
|
+
700: "Negrito", 800: "Extra-negrito",
|
|
1123
|
+
};
|
|
1124
|
+
|
|
1125
|
+
function selecao(valores, atual, aoMudar, vazio) {
|
|
794
1126
|
var s = el("select", "campo");
|
|
795
1127
|
valores.forEach(function (v) {
|
|
796
|
-
var
|
|
1128
|
+
var rotulo = v === "" ? (vazio || "Automático") : (NOMES[v] || String(v));
|
|
1129
|
+
var o = el("option", "", rotulo);
|
|
797
1130
|
o.value = v;
|
|
798
1131
|
if (String(v) === String(atual)) o.selected = true;
|
|
799
1132
|
s.appendChild(o);
|
|
@@ -802,18 +1135,32 @@
|
|
|
802
1135
|
return s;
|
|
803
1136
|
}
|
|
804
1137
|
|
|
1138
|
+
/* As amostras e o campo hex na MESMA faixa.
|
|
1139
|
+
|
|
1140
|
+
Empilhados, cada cor ocupava três linhas do painel; com dois seletores
|
|
1141
|
+
de cor na mesma tela, sobrava painel só para eles. Agora as amostras
|
|
1142
|
+
rolam numa faixa e o hex fica ao lado — e a amostra escolhida aparece
|
|
1143
|
+
marcada, coisa que antes não dava para saber. */
|
|
805
1144
|
function seletorCor(valor, aoMudar) {
|
|
806
|
-
var caixa = el("div");
|
|
807
|
-
caixa.style.cssText = "display:grid;gap:6px";
|
|
1145
|
+
var caixa = el("div", "cor-faixa");
|
|
808
1146
|
var p = el("div", "cores");
|
|
1147
|
+
var limpar = el("button", "sem-cor");
|
|
1148
|
+
limpar.title = "Sem cor";
|
|
1149
|
+
limpar.onclick = function () { aoMudar(""); };
|
|
1150
|
+
p.appendChild(limpar);
|
|
809
1151
|
PALETA.forEach(function (c) {
|
|
810
1152
|
var b = el("button");
|
|
811
1153
|
b.style.background = c;
|
|
1154
|
+
if (String(valor).toLowerCase() === String(c).toLowerCase()) b.classList.add("ativa");
|
|
1155
|
+
b.title = c;
|
|
812
1156
|
b.onclick = function () { aoMudar(c); };
|
|
813
1157
|
p.appendChild(b);
|
|
814
1158
|
});
|
|
815
1159
|
caixa.appendChild(p);
|
|
816
|
-
|
|
1160
|
+
var hex = inp(valor, aoMudar);
|
|
1161
|
+
hex.placeholder = "#hex";
|
|
1162
|
+
hex.className = "campo hex";
|
|
1163
|
+
caixa.appendChild(hex);
|
|
817
1164
|
return caixa;
|
|
818
1165
|
}
|
|
819
1166
|
|
|
@@ -837,13 +1184,21 @@
|
|
|
837
1184
|
var c = cena();
|
|
838
1185
|
g0.appendChild(linha("Layout", selecao(F.layouts, c.layout || F.layoutPadrao,
|
|
839
1186
|
function (v) { mudar(function () { c.layout = v; }); })));
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
1187
|
+
// UM controle para o fundo, não três.
|
|
1188
|
+
//
|
|
1189
|
+
// Havia "Fundo", "Fundo (extra)" e "Cor fundo", lado a lado, e os três
|
|
1190
|
+
// gravavam na MESMA propriedade: escolher num apagava o outro, e dois
|
|
1191
|
+
// deles apareciam vazios porque só um podia estar preenchido por vez.
|
|
1192
|
+
// Era a parte mais confusa do inspetor. Agora é uma lista só, com tudo
|
|
1193
|
+
// que existe, e a cor entra por baixo quando é cor que se quer.
|
|
1194
|
+
var padroes = ["", "malha", "brilho", "grade", "pontos", "suave", "vinheta",
|
|
1195
|
+
"aurora", "raios", "ondas", "nevoa", "xadrez"];
|
|
1196
|
+
var fundoAtual = typeof c.fundo === "string" && c.fundo.charAt(0) !== "#" ? c.fundo : "";
|
|
1197
|
+
g0.appendChild(linha("Fundo", selecao(padroes, fundoAtual, function (v) {
|
|
1198
|
+
mudar(function () { if (v) c.fundo = v; else delete c.fundo; });
|
|
1199
|
+
}, "Sem fundo")));
|
|
1200
|
+
g0.appendChild(linha("Cor do fundo", seletorCor(
|
|
1201
|
+
typeof c.fundo === "string" && c.fundo.charAt(0) === "#" ? c.fundo : "",
|
|
847
1202
|
function (v) { mudar(function () { if (v) c.fundo = v; else delete c.fundo; }); })));
|
|
848
1203
|
|
|
849
1204
|
var bFoto = el("button", "btn", svgIcone("imagem") + " Foto de fundo");
|
|
@@ -855,16 +1210,17 @@
|
|
|
855
1210
|
};
|
|
856
1211
|
g0.appendChild(linha("Imagem", bFoto));
|
|
857
1212
|
|
|
858
|
-
g0.appendChild(linha("
|
|
859
|
-
c.acabamento || "", function (v) { mudar(function () { if (v) c.acabamento = v; else delete c.acabamento; }); })));
|
|
860
|
-
g0.appendChild(linha("
|
|
1213
|
+
g0.appendChild(linha("Acabamento", selecao(["", "grao", "vinheta", "scanlines", "luz", "brilho", "moldura"],
|
|
1214
|
+
c.acabamento || "", function (v) { mudar(function () { if (v) c.acabamento = v; else delete c.acabamento; }); }, "Nenhum")));
|
|
1215
|
+
g0.appendChild(linha("Cor de destaque", seletorCor(c.tom || "",
|
|
861
1216
|
function (v) { mudar(function () { if (v) c.tom = v; else delete c.tom; }); })));
|
|
1217
|
+
g0.appendChild(linha("Tema da cena", selecao([""].concat(TEMAS), c.tema || "",
|
|
1218
|
+
function (v) { mudar(function () { if (v) c.tema = v; else delete c.tema; }); }, "Igual ao documento")));
|
|
862
1219
|
|
|
863
1220
|
if (F.tempo) {
|
|
864
|
-
g0.appendChild(linha("Duração", inp(c.duracao || 4, function (v) { mudar(function () { c.duracao = v || 4; }); }, "number")));
|
|
865
|
-
g0.appendChild(linha("
|
|
866
|
-
|
|
867
|
-
c.transicao || "", function (v) { mudar(function () { if (v) c.transicao = v; else delete c.transicao; }); })));
|
|
1221
|
+
g0.appendChild(linha("Duração (s)", inp(c.duracao || 4, function (v) { mudar(function () { c.duracao = v || 4; }); }, "number")));
|
|
1222
|
+
g0.appendChild(linha("Como entra", menu(OPC.transicao, c.transicao || "",
|
|
1223
|
+
function (v) { mudar(function () { if (v) c.transicao = v; else delete c.transicao; }); })));
|
|
868
1224
|
}
|
|
869
1225
|
if (SLUG === "traco") {
|
|
870
1226
|
g0.appendChild(linha("Arranjo", selecao(["", "horizontal", "vertical"], c.auto || "",
|
|
@@ -932,12 +1288,10 @@
|
|
|
932
1288
|
la.appendChild(inp(e.l, function (v) { edita("l", v); }, "number"));
|
|
933
1289
|
la.appendChild(inp(e.a, function (v) { edita("a", v); }, "number"));
|
|
934
1290
|
gp.appendChild(linha("L / A", la));
|
|
935
|
-
gp.appendChild(linha("Girar", inp(e.rotacao || 0, function (v) { edita("rotacao", v); }, "number")));
|
|
936
|
-
gp.appendChild(linha("
|
|
937
|
-
gp.appendChild(linha("
|
|
938
|
-
|
|
939
|
-
"girar", "saltar", "revelar", "cortina", "elastico", "palavra", "nenhuma"],
|
|
940
|
-
e.anim || "", function (v) { edita("anim", v); })));
|
|
1291
|
+
gp.appendChild(linha("Girar (graus)", inp(e.rotacao || 0, function (v) { edita("rotacao", v); }, "number")));
|
|
1292
|
+
gp.appendChild(linha("Opacidade", inp(e.opacidade != null ? e.opacidade : 1, function (v) { edita("opacidade", v); }, "number")));
|
|
1293
|
+
gp.appendChild(linha("Como entra", menu(OPC.animacao, e.anim || "",
|
|
1294
|
+
function (v) { edita("anim", v); })));
|
|
941
1295
|
if (e.tipo === "numeros") {
|
|
942
1296
|
gp.appendChild(linha("Contar", selecao(["não", "sim"], e.contar ? "sim" : "não",
|
|
943
1297
|
function (v) { edita("contar", v === "sim" ? true : ""); })));
|
|
@@ -948,7 +1302,133 @@
|
|
|
948
1302
|
dica.style.padding = "10px 16px";
|
|
949
1303
|
ins.appendChild(dica);
|
|
950
1304
|
}
|
|
1305
|
+
if (F.tempo) chavesInspetor(ins, e);
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
/* ─────────────────────────────────────────────────────────── chaves ─────
|
|
1310
|
+
Antes um elemento só sabia ENTRAR: escolhia-se um efeito e acabava ali.
|
|
1311
|
+
Nada percorria a cena, crescia devagar nem sumia no fim.
|
|
1312
|
+
|
|
1313
|
+
Uma chave é um instante: onde o elemento está aos 0%, aos 50%, aos
|
|
1314
|
+
100% da cena. Duas ou mais viram animação de verdade. A régua embaixo
|
|
1315
|
+
mostra as chaves na linha do tempo da cena — não é enfeite, é o único
|
|
1316
|
+
jeito de ver que existem três chaves e que a do meio está adiantada. */
|
|
1317
|
+
var PRESETS_CHAVE = [
|
|
1318
|
+
{ nome: "Atravessar", chaves: [{ t: 0, x: -220, opacidade: 0 }, { t: 0.18, x: 0, opacidade: 1 },
|
|
1319
|
+
{ t: 0.82, x: 0, opacidade: 1 }, { t: 1, x: 220, opacidade: 0 }] },
|
|
1320
|
+
{ nome: "Crescer devagar", chaves: [{ t: 0, escala: 1 }, { t: 1, escala: 1.18 }] },
|
|
1321
|
+
{ nome: "Aparecer e sumir", chaves: [{ t: 0, opacidade: 0 }, { t: 0.15, opacidade: 1 },
|
|
1322
|
+
{ t: 0.85, opacidade: 1 }, { t: 1, opacidade: 0 }] },
|
|
1323
|
+
{ nome: "Flutuar", chaves: [{ t: 0, y: 0 }, { t: 0.5, y: -22 }, { t: 1, y: 0 }] },
|
|
1324
|
+
{ nome: "Sair do desfoque", chaves: [{ t: 0, desfoque: 18, opacidade: 0, escala: 1.06 },
|
|
1325
|
+
{ t: 0.35, desfoque: 0, opacidade: 1, escala: 1 }, { t: 1, desfoque: 0, opacidade: 1, escala: 1 }] },
|
|
1326
|
+
];
|
|
1327
|
+
|
|
1328
|
+
function chavesInspetor(ins, e) {
|
|
1329
|
+
var g = grupo("Animação por chaves");
|
|
1330
|
+
var chaves = e.chaves || [];
|
|
1331
|
+
|
|
1332
|
+
if (!chaves.length) {
|
|
1333
|
+
g.appendChild(el("div", "nada",
|
|
1334
|
+
"Sem chaves: o elemento fica parado depois de entrar."));
|
|
1335
|
+
var pr = el("div", "presets");
|
|
1336
|
+
PRESETS_CHAVE.forEach(function (p) {
|
|
1337
|
+
var b = el("button", "", p.nome);
|
|
1338
|
+
b.onclick = function () {
|
|
1339
|
+
edita("chaves", JSON.parse(JSON.stringify(p.chaves)));
|
|
1340
|
+
};
|
|
1341
|
+
pr.appendChild(b);
|
|
1342
|
+
});
|
|
1343
|
+
g.appendChild(pr);
|
|
1344
|
+
ins.appendChild(g);
|
|
1345
|
+
return;
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
// A régua: cada chave é um pino na duração da cena.
|
|
1349
|
+
var regua = el("div", "regua-chaves");
|
|
1350
|
+
var trilho = el("div", "trilho-chaves");
|
|
1351
|
+
regua.appendChild(trilho);
|
|
1352
|
+
chaves.forEach(function (k, i) {
|
|
1353
|
+
var pino = el("button", "pino" + (i === chaveSel ? " ativo" : ""));
|
|
1354
|
+
pino.style.left = (Math.max(0, Math.min(1, k.t || 0)) * 100) + "%";
|
|
1355
|
+
pino.title = ((k.t || 0) * (cena().duracao || 4)).toFixed(1) + "s";
|
|
1356
|
+
pino.onclick = function () { chaveSel = i; desenharInspetor(); };
|
|
1357
|
+
trilho.appendChild(pino);
|
|
1358
|
+
});
|
|
1359
|
+
g.appendChild(regua);
|
|
1360
|
+
g.appendChild(el("div", "sub-regua",
|
|
1361
|
+
"0s" + " · " + (cena().duracao || 4) + "s no fim"));
|
|
1362
|
+
|
|
1363
|
+
if (chaveSel >= chaves.length) chaveSel = chaves.length - 1;
|
|
1364
|
+
var k = chaves[chaveSel] || chaves[0];
|
|
1365
|
+
|
|
1366
|
+
var nav = el("div", "opcoes");
|
|
1367
|
+
chaves.forEach(function (kk, i) {
|
|
1368
|
+
var b = el("button", i === chaveSel ? "ativo" : "", String(i + 1));
|
|
1369
|
+
b.onclick = function () { chaveSel = i; desenharInspetor(); };
|
|
1370
|
+
nav.appendChild(b);
|
|
1371
|
+
});
|
|
1372
|
+
var bMais = el("button", "", svgIcone("mais"));
|
|
1373
|
+
bMais.title = "Nova chave no fim";
|
|
1374
|
+
bMais.onclick = function () {
|
|
1375
|
+
mudar(function () {
|
|
1376
|
+
var ultima = chaves[chaves.length - 1] || {};
|
|
1377
|
+
var nova = JSON.parse(JSON.stringify(ultima));
|
|
1378
|
+
nova.t = Math.min(1, (ultima.t || 0) + 0.25);
|
|
1379
|
+
chaves.push(nova);
|
|
1380
|
+
chaves.sort(function (a, b2) { return (a.t || 0) - (b2.t || 0); });
|
|
1381
|
+
});
|
|
1382
|
+
chaveSel = chaves.length - 1;
|
|
1383
|
+
desenharInspetor();
|
|
1384
|
+
};
|
|
1385
|
+
nav.appendChild(bMais);
|
|
1386
|
+
g.appendChild(nav);
|
|
1387
|
+
|
|
1388
|
+
function campoChave(rotulo, campo, padrao, passo) {
|
|
1389
|
+
var i = inp(k[campo] != null ? k[campo] : padrao, function (v) {
|
|
1390
|
+
mudar(function () {
|
|
1391
|
+
if (v == null || v === "") delete k[campo];
|
|
1392
|
+
else k[campo] = v;
|
|
1393
|
+
});
|
|
1394
|
+
}, "number");
|
|
1395
|
+
if (passo) i.step = passo;
|
|
1396
|
+
g.appendChild(linha(rotulo, i));
|
|
951
1397
|
}
|
|
1398
|
+
|
|
1399
|
+
var tSeg = inp(+((k.t || 0) * (cena().duracao || 4)).toFixed(2), function (v) {
|
|
1400
|
+
mudar(function () {
|
|
1401
|
+
k.t = Math.max(0, Math.min(1, (v || 0) / (cena().duracao || 4)));
|
|
1402
|
+
chaves.sort(function (a, b2) { return (a.t || 0) - (b2.t || 0); });
|
|
1403
|
+
});
|
|
1404
|
+
}, "number");
|
|
1405
|
+
tSeg.step = "0.1";
|
|
1406
|
+
g.appendChild(linha("Momento (s)", tSeg));
|
|
1407
|
+
|
|
1408
|
+
campoChave("Deslocar X", "x", 0);
|
|
1409
|
+
campoChave("Deslocar Y", "y", 0);
|
|
1410
|
+
campoChave("Escala", "escala", 1, "0.05");
|
|
1411
|
+
campoChave("Girar (graus)", "giro", 0);
|
|
1412
|
+
campoChave("Opacidade", "opacidade", 1, "0.1");
|
|
1413
|
+
campoChave("Desfoque (px)", "desfoque", 0);
|
|
1414
|
+
|
|
1415
|
+
var acoes = el("div", "opcoes");
|
|
1416
|
+
var bRep = el("button", e.repetir ? "ativo" : "", svgIcone("refazer"));
|
|
1417
|
+
bRep.title = "Repetir sem parar";
|
|
1418
|
+
bRep.onclick = function () { edita("repetir", e.repetir ? "" : true); };
|
|
1419
|
+
var bLin = el("button", e.suavizar === false ? "ativo" : "", svgIcone("linha"));
|
|
1420
|
+
bLin.title = "Movimento constante (sem suavizar)";
|
|
1421
|
+
bLin.onclick = function () { edita("suavizar", e.suavizar === false ? "" : false); };
|
|
1422
|
+
var bDel = el("button", "", svgIcone("lixeira"));
|
|
1423
|
+
bDel.title = "Apagar esta chave";
|
|
1424
|
+
bDel.onclick = function () {
|
|
1425
|
+
mudar(function () { chaves.splice(chaveSel, 1); if (!chaves.length) delete e.chaves; });
|
|
1426
|
+
chaveSel = 0; desenharInspetor();
|
|
1427
|
+
};
|
|
1428
|
+
acoes.appendChild(bRep); acoes.appendChild(bLin); acoes.appendChild(bDel);
|
|
1429
|
+
g.appendChild(acoes);
|
|
1430
|
+
|
|
1431
|
+
ins.appendChild(g);
|
|
952
1432
|
}
|
|
953
1433
|
|
|
954
1434
|
function camposDoTipo(ins, e) {
|
|
@@ -1130,27 +1610,45 @@
|
|
|
1130
1610
|
|
|
1131
1611
|
/* --- 3. escolha da voz, já ranqueada -------------------------------- */
|
|
1132
1612
|
|
|
1613
|
+
// As vozes vêm do servidor, que é quem sintetiza. Cada uma traz a
|
|
1614
|
+
// frequência MEDIDA — quem quer voz de locução escolhe pelo número, não
|
|
1615
|
+
// pelo palpite.
|
|
1133
1616
|
var voz = doc.voz || {};
|
|
1134
|
-
var boas = Narracao.vozes();
|
|
1135
1617
|
var sel2 = el("select", "campo");
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1618
|
+
function encherVozes(lista) {
|
|
1619
|
+
sel2.innerHTML = "";
|
|
1620
|
+
var opAuto = el("option", "", "Padrão (a mais grave)");
|
|
1621
|
+
opAuto.value = "";
|
|
1622
|
+
sel2.appendChild(opAuto);
|
|
1623
|
+
lista.forEach(function (v) {
|
|
1624
|
+
var o = el("option", "", v.nome + " · " + v.tom
|
|
1625
|
+
+ (v.hz ? " · " + v.hz + " Hz" : "")
|
|
1626
|
+
+ (v.instalada ? "" : " · baixa ao usar"));
|
|
1627
|
+
o.value = v.id;
|
|
1628
|
+
if (v.id === voz.voz) o.selected = true;
|
|
1629
|
+
sel2.appendChild(o);
|
|
1630
|
+
});
|
|
1631
|
+
}
|
|
1632
|
+
encherVozes(VozNeural.vozes());
|
|
1633
|
+
VozNeural.status().then(function () { encherVozes(VozNeural.vozes()); });
|
|
1145
1634
|
sel2.onchange = function () {
|
|
1146
1635
|
mudar(function () { doc.voz = doc.voz || {}; doc.voz.voz = sel2.value; });
|
|
1147
1636
|
Narracao.falar("Assim que esta voz vai narrar o seu vídeo.",
|
|
1148
|
-
{ voz: sel2.value, velocidade: voz.velocidade });
|
|
1637
|
+
{ voz: sel2.value, grave: voz.grave, velocidade: voz.velocidade });
|
|
1149
1638
|
};
|
|
1150
1639
|
gn.appendChild(linha("Voz", sel2));
|
|
1151
1640
|
|
|
1152
|
-
|
|
1153
|
-
|
|
1641
|
+
var bGrave = el("button", "btn" + (voz.grave ? " primo" : ""),
|
|
1642
|
+
(voz.grave ? "✓ " : "") + "Registro grave, de cinema");
|
|
1643
|
+
bGrave.style.cssText = "width:100%;margin:2px 0 8px";
|
|
1644
|
+
bGrave.title = "Abaixa o tom uns 10% sem mudar a duração";
|
|
1645
|
+
bGrave.onclick = function () {
|
|
1646
|
+
mudar(function () { doc.voz = doc.voz || {}; doc.voz.grave = !voz.grave; });
|
|
1647
|
+
};
|
|
1648
|
+
gn.appendChild(bGrave);
|
|
1649
|
+
|
|
1650
|
+
gn.appendChild(linha("Velocidade", inp(voz.velocidade != null ? voz.velocidade : 1,
|
|
1651
|
+
function (v) { mudar(function () { doc.voz = doc.voz || {}; doc.voz.velocidade = v || 1; }); },
|
|
1154
1652
|
"number")));
|
|
1155
1653
|
|
|
1156
1654
|
if (F.tempo) {
|
|
@@ -1160,24 +1658,41 @@
|
|
|
1160
1658
|
gn.appendChild(bTodas);
|
|
1161
1659
|
}
|
|
1162
1660
|
|
|
1163
|
-
/* --- 4.
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
"
|
|
1179
|
-
|
|
1180
|
-
|
|
1661
|
+
/* --- 4. o estado real do motor -------------------------------------- */
|
|
1662
|
+
|
|
1663
|
+
// Isto aqui mandava o usuário em "Ajustes do Sistema → Acessibilidade →
|
|
1664
|
+
// ... baixe as vozes da Apple". Era verdade quando a narração dependia
|
|
1665
|
+
// do TTS do sistema. Hoje a síntese é neural e roda no servidor, então o
|
|
1666
|
+
// texto virou instrução para o lugar errado — e num Windows, para um
|
|
1667
|
+
// lugar que nem existe. O que fica é o estado de verdade do motor.
|
|
1668
|
+
var aviso = el("div", "dica-voz");
|
|
1669
|
+
aviso.style.display = "none";
|
|
1670
|
+
gn.appendChild(aviso);
|
|
1671
|
+
VozNeural.status(true).then(function (s) {
|
|
1672
|
+
var p = s.piper || {};
|
|
1673
|
+
if (p.pronta) return;
|
|
1674
|
+
aviso.style.display = "";
|
|
1675
|
+
if (p.baixando && p.baixando.ativo) {
|
|
1676
|
+
aviso.innerHTML = "<b>Preparando a voz neural…</b><p>" + p.baixando.etapa
|
|
1677
|
+
+ " · " + p.baixando.pct + "%. Enquanto isso a narração sai por uma voz "
|
|
1678
|
+
+ "mais simples; o vídeo não fica mudo.</p>";
|
|
1679
|
+
setTimeout(function () { if (document.body.contains(aviso)) desenharInspetor(); }, 3000);
|
|
1680
|
+
} else if (!p.suportado) {
|
|
1681
|
+
aviso.innerHTML = "<b>Sem voz neural para este sistema.</b>"
|
|
1682
|
+
+ "<p>A narração sai pela voz de reserva. Gravar a sua própria voz, no botão "
|
|
1683
|
+
+ "acima, continua sendo o melhor resultado.</p>";
|
|
1684
|
+
} else {
|
|
1685
|
+
aviso.innerHTML = "<b>A voz neural ainda não foi baixada.</b>"
|
|
1686
|
+
+ "<p>São ~90 MB, uma vez só, e depois funciona sem internet.</p>";
|
|
1687
|
+
var bJa = el("button", "btn primo", "Baixar agora");
|
|
1688
|
+
bJa.style.cssText = "width:100%;margin-top:8px";
|
|
1689
|
+
bJa.onclick = function () {
|
|
1690
|
+
bJa.textContent = "baixando…";
|
|
1691
|
+
VozNeural.instalar(voz.voz).then(function () { desenharInspetor(); });
|
|
1692
|
+
};
|
|
1693
|
+
aviso.appendChild(bJa);
|
|
1694
|
+
}
|
|
1695
|
+
});
|
|
1181
1696
|
|
|
1182
1697
|
ins.appendChild(gn);
|
|
1183
1698
|
}
|