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
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
ferramentas.js — o que cada ferramenta oferece na interface.
|
|
3
3
|
========================================================================== */
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
// Os dez que o render.css define. A lista tinha seis: quatro temas existiam
|
|
6
|
+
// no CSS, a IA podia usá-los, e o editor não os oferecia a ninguém.
|
|
7
|
+
var TEMAS = ["meia-noite", "claro", "papel", "neon", "brasa", "floresta",
|
|
8
|
+
"retro", "oceano", "doce", "mono"];
|
|
6
9
|
|
|
7
10
|
var PALETA = ["#ff5a36", "#ffb03a", "#f4e04d", "#2fbf7f", "#1f8fe0", "#8b5cf6",
|
|
8
11
|
"#f472b6", "#ffffff", "#9aa4b2", "#111318"];
|
package/studio/web/js/render.js
CHANGED
|
@@ -47,8 +47,16 @@ function _porPalavra(html) {
|
|
|
47
47
|
function aplicarFundo(no, fundo) {
|
|
48
48
|
if (!fundo) return;
|
|
49
49
|
if (typeof fundo === "string") {
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
// Os ONZE que o render.css define.
|
|
51
|
+
//
|
|
52
|
+
// A lista aqui tinha seis. Os outros cinco — aurora, raios, ondas,
|
|
53
|
+
// nevoa, xadrez — caíam no `else` e viravam `background: "aurora"`:
|
|
54
|
+
// CSS inválido, fundo NENHUM. E são justamente os que a spec recomenda
|
|
55
|
+
// para vídeo, então toda cena que pedia aurora ou raios saía chapada.
|
|
56
|
+
// Era metade do "os vídeos ficaram todos iguais".
|
|
57
|
+
if (/^(malha|brilho|grade|pontos|suave|vinheta|aurora|nevoa|ondas|raios|xadrez)$/.test(fundo)) {
|
|
58
|
+
no.setAttribute("data-fundo", fundo);
|
|
59
|
+
} else no.style.background = fundo;
|
|
52
60
|
return;
|
|
53
61
|
}
|
|
54
62
|
if (fundo.tipo === "gradiente") {
|
|
@@ -70,13 +78,23 @@ function aplicarFundo(no, fundo) {
|
|
|
70
78
|
/* --------------------------------------------------------------- gráficos */
|
|
71
79
|
|
|
72
80
|
function svgGrafico(e, l, a) {
|
|
73
|
-
|
|
81
|
+
/* Os dados podem chegar como "dados" ou como "itens", e o tipo como
|
|
82
|
+
"grafico", "estilo" ou "tipo".
|
|
83
|
+
|
|
84
|
+
O nome certo é `dados` + `grafico`, e é o que a spec ensina. Mas quem
|
|
85
|
+
escreve um gráfico — a IA ou uma pessoa — chuta `itens`, porque é o
|
|
86
|
+
campo de TODOS os outros elementos da mesma spec (lista, cartoes,
|
|
87
|
+
numeros usam `itens`). Com o nome trocado esta função devolvia string
|
|
88
|
+
vazia: o gráfico simplesmente não existia na tela, sem erro nenhum.
|
|
89
|
+
Aceitar as duas grafias custa uma linha e evita a falha muda. */
|
|
90
|
+
var bruto = e.dados || e.itens || [];
|
|
91
|
+
var dados = bruto.map(function (d) {
|
|
74
92
|
return Array.isArray(d) ? { r: d[0], v: +d[1] || 0 } : { r: d.rotulo, v: +d.valor || 0 };
|
|
75
93
|
});
|
|
76
94
|
if (!dados.length) return "";
|
|
77
95
|
var cores = e.cores || ["var(--c-tom)", "var(--c-tom2)", "#7c9cff", "#2fbf7f", "#f472b6", "#facc15"];
|
|
78
96
|
var max = Math.max.apply(null, dados.map(function (d) { return d.v; })) || 1;
|
|
79
|
-
var tipo = e.grafico || "barras";
|
|
97
|
+
var tipo = e.grafico || e.estilo || e.tipoGrafico || "barras";
|
|
80
98
|
var pad = { e: 8, d: 8, t: 14, b: 42 };
|
|
81
99
|
var iw = l - pad.e - pad.d, ih = a - pad.t - pad.b;
|
|
82
100
|
var p = ['<svg viewBox="0 0 ' + l + " " + a + '" width="' + l + '" height="' + a + '">'];
|
|
@@ -138,11 +156,233 @@ function svgGrafico(e, l, a) {
|
|
|
138
156
|
/* -------------------------------------------------------------- elementos */
|
|
139
157
|
|
|
140
158
|
var TIPOS_CONHECIDOS = { texto: 1, lista: 1, cartoes: 1, numeros: 1, tabela: 1, codigo: 1,
|
|
141
|
-
citacao: 1, forma: 1, imagem: 1, icone: 1, grafico: 1, no: 1, conector: 1
|
|
159
|
+
citacao: 1, forma: 1, imagem: 1, icone: 1, grafico: 1, no: 1, conector: 1,
|
|
160
|
+
objeto3d: 1 };
|
|
161
|
+
|
|
162
|
+
/* ── chaves de animação ──────────────────────────────────────────────────
|
|
163
|
+
Transforma a lista de instantes de um elemento numa regra @keyframes de
|
|
164
|
+
verdade e prende ela no elemento.
|
|
165
|
+
|
|
166
|
+
Os nomes precisam ser únicos por página: o mesmo documento aparece várias
|
|
167
|
+
vezes ao mesmo tempo (palco, miniaturas do painel, clipes da tira), e
|
|
168
|
+
dois @keyframes de mesmo nome com conteúdo diferente se atropelariam —
|
|
169
|
+
a última regra injetada venceria em TODAS as cópias. */
|
|
170
|
+
var _nChave = 0;
|
|
171
|
+
|
|
172
|
+
function _numero(v, padrao) {
|
|
173
|
+
return typeof v === "number" && isFinite(v) ? v : padrao;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function aplicarChaves(no, e, ctx) {
|
|
177
|
+
var chaves = e.chaves.slice().sort(function (a, b) {
|
|
178
|
+
return _numero(a.t, 0) - _numero(b.t, 0);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
var nome = "ch" + (++_nChave);
|
|
182
|
+
var passos = chaves.map(function (k) {
|
|
183
|
+
var pct = Math.max(0, Math.min(1, _numero(k.t, 0))) * 100;
|
|
184
|
+
var mov = [];
|
|
185
|
+
// x e y de uma chave são DESLOCAMENTO a partir de onde o elemento está.
|
|
186
|
+
// Assim a chave continua valendo se a pessoa arrastar o elemento no
|
|
187
|
+
// palco depois — o contrário obrigaria a refazer todas as chaves.
|
|
188
|
+
var dx = _numero(k.x, 0), dy = _numero(k.y, 0);
|
|
189
|
+
if (dx || dy) mov.push("translate(" + dx + "px," + dy + "px)");
|
|
190
|
+
var esc = _numero(k.escala, 1);
|
|
191
|
+
if (esc !== 1) mov.push("scale(" + esc + ")");
|
|
192
|
+
var giro = _numero(k.giro, 0);
|
|
193
|
+
if (giro) mov.push("rotate(" + giro + "deg)");
|
|
194
|
+
|
|
195
|
+
var linhas = ["transform:" + (mov.length ? mov.join(" ") : "none")];
|
|
196
|
+
var op = _numero(k.opacidade, 1);
|
|
197
|
+
linhas.push("opacity:" + op);
|
|
198
|
+
if (typeof k.desfoque === "number" && k.desfoque > 0) {
|
|
199
|
+
linhas.push("filter:blur(" + k.desfoque + "px)");
|
|
200
|
+
}
|
|
201
|
+
return pct.toFixed(3) + "%{" + linhas.join(";") + "}";
|
|
202
|
+
}).join("");
|
|
203
|
+
|
|
204
|
+
// A regra vai na RAIZ da cena, não dentro do elemento.
|
|
205
|
+
//
|
|
206
|
+
// Presa no próprio elemento ela era apagada segundos depois: quem desenha
|
|
207
|
+
// o conteúdo (texto, lista, número) escreve o innerHTML e leva junto tudo
|
|
208
|
+
// que estivesse ali. O elemento continuava com `animation-name: ch1`
|
|
209
|
+
// apontando para uma regra que não existia mais — a animação simplesmente
|
|
210
|
+
// não acontecia, sem erro nenhum no console. A cena só recebe filhos, e
|
|
211
|
+
// por isso a regra sobrevive lá.
|
|
212
|
+
var folha = document.createElement("style");
|
|
213
|
+
folha.textContent = "@keyframes " + nome + "{" + passos + "}";
|
|
214
|
+
(ctx.raiz || no).appendChild(folha);
|
|
215
|
+
|
|
216
|
+
var dur = _numero(ctx.duracao, 4);
|
|
217
|
+
no.style.animationName = nome;
|
|
218
|
+
no.style.animationDuration = dur + "s";
|
|
219
|
+
no.style.animationTimingFunction = e.suavizar === false ? "linear" : "cubic-bezier(.4,0,.2,1)";
|
|
220
|
+
no.style.animationFillMode = "both";
|
|
221
|
+
no.style.animationIterationCount = e.repetir ? "infinite" : "1";
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/* ── identidade da marca ─────────────────────────────────────────────────
|
|
225
|
+
"Ele não escolhe as cores dos textos, não escolhe tema, não escolhe uma
|
|
226
|
+
imagem de fundo, não coloca uma logo. Ficou horrível."
|
|
227
|
+
|
|
228
|
+
O logo era o mais gritante: não havia NENHUM jeito de pôr um. Dava para
|
|
229
|
+
subir a imagem e posicionar à mão, cena por cena — o que ninguém faz num
|
|
230
|
+
vídeo de dez cenas, e a IA nunca fazia.
|
|
231
|
+
|
|
232
|
+
`doc.marca` resolve de uma vez: o logo aparece em todas as cenas, no
|
|
233
|
+
canto, no tamanho certo, sem entrar no fluxo do layout. E `marca.cor`
|
|
234
|
+
define a cor de destaque do documento inteiro, que é o que dá identidade
|
|
235
|
+
aos números, às barras do gráfico e aos detalhes.
|
|
236
|
+
|
|
237
|
+
"marca": { "logo": "/midia/logo.png", "cor": "#ff5a36",
|
|
238
|
+
"canto": "inferior-direita", "opacidade": 0.85 }
|
|
239
|
+
|
|
240
|
+
`cena.semMarca: true` tira de uma cena específica — a capa costuma querer
|
|
241
|
+
o logo grande no meio, não pequeno no canto. */
|
|
242
|
+
var CANTOS = {
|
|
243
|
+
"superior-esquerda": "top:5.2%;left:5%",
|
|
244
|
+
"superior-direita": "top:5.2%;right:5%",
|
|
245
|
+
"inferior-esquerda": "bottom:5.2%;left:5%",
|
|
246
|
+
"inferior-direita": "bottom:5.2%;right:5%",
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
function aplicarMarca(no, cena, doc, H) {
|
|
250
|
+
var m = doc && doc.marca;
|
|
251
|
+
if (!m || typeof m !== "object") return;
|
|
252
|
+
// A cor da marca vira a cor de destaque de tudo, a menos que a cena diga
|
|
253
|
+
// outra — é ela que pinta número, barra de gráfico e detalhe.
|
|
254
|
+
if (m.cor && !cena.tom) no.style.setProperty("--c-tom", m.cor);
|
|
255
|
+
if (!m.logo || cena.semMarca) return;
|
|
256
|
+
|
|
257
|
+
var caixa = document.createElement("div");
|
|
258
|
+
caixa.className = "marca-dagua";
|
|
259
|
+
caixa.style.cssText = "position:absolute;z-index:6;pointer-events:none;"
|
|
260
|
+
+ (CANTOS[m.canto] || CANTOS["inferior-direita"])
|
|
261
|
+
+ ";opacity:" + (m.opacidade != null ? m.opacidade : 0.85);
|
|
262
|
+
var img = document.createElement("img");
|
|
263
|
+
img.src = m.logo;
|
|
264
|
+
img.alt = "";
|
|
265
|
+
// Altura proporcional à tela, resolvida em PIXELS: a caixa é absoluta e
|
|
266
|
+
// sem altura própria, e porcentagem dentro dela não resolveria — o logo
|
|
267
|
+
// sairia no tamanho original, gigante num caso e sumido no outro.
|
|
268
|
+
var alturaPx = Math.round((H || 1080) * ((m.altura || 4.6) / 100));
|
|
269
|
+
img.style.cssText = "height:" + alturaPx + "px;width:auto;display:block";
|
|
270
|
+
caixa.appendChild(img);
|
|
271
|
+
no.appendChild(caixa);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/* ── objetos 3D ──────────────────────────────────────────────────────────
|
|
275
|
+
"Quero que tu coloque a função ali no vídeo, na apresentação de slides,
|
|
276
|
+
pra fazer objeto 3D com animação, tudo assim, bem top."
|
|
277
|
+
|
|
278
|
+
Isto é 3D de verdade — perspectiva, faces, profundidade — feito com
|
|
279
|
+
transform 3D do próprio navegador. Sem WebGL e sem biblioteca: o Studio
|
|
280
|
+
não tem dependência nenhuma e não vai passar a ter por causa disto. O que
|
|
281
|
+
dá para fazer assim cobre o que um vídeo institucional pede: um cubo
|
|
282
|
+
girando com o logo nas faces, um cartão com espessura, uma pilha de
|
|
283
|
+
camadas em profundidade.
|
|
284
|
+
|
|
285
|
+
{ "tipo": "objeto3d", "forma": "cubo", "tamanho": 320,
|
|
286
|
+
"faces": ["Produto", "Dados", "IA"], "girar": 14 }
|
|
287
|
+
|
|
288
|
+
`girar` é a volta completa em segundos; 0 deixa parado no ângulo dado.
|
|
289
|
+
`faces` aceita texto ou URL de imagem. E como o elemento é um nó normal,
|
|
290
|
+
as chaves de animação valem aqui também: dá para o cubo atravessar a cena
|
|
291
|
+
girando. */
|
|
292
|
+
var FORMAS_3D = { cubo: 1, caixa: 1, cartao: 1, prisma: 1, painel: 1 };
|
|
293
|
+
|
|
294
|
+
function _face3d(conteudo, transform, cor) {
|
|
295
|
+
var f = document.createElement("div");
|
|
296
|
+
f.className = "face3d";
|
|
297
|
+
f.style.transform = transform;
|
|
298
|
+
if (cor) f.style.background = cor;
|
|
299
|
+
if (/^(https?:|\/)/.test(String(conteudo || ""))) {
|
|
300
|
+
var im = document.createElement("img");
|
|
301
|
+
im.src = conteudo;
|
|
302
|
+
im.alt = "";
|
|
303
|
+
f.appendChild(im);
|
|
304
|
+
} else if (conteudo) {
|
|
305
|
+
f.textContent = conteudo;
|
|
306
|
+
}
|
|
307
|
+
return f;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function montar3d(no, e) {
|
|
311
|
+
var forma = FORMAS_3D[e.forma] ? e.forma : "cubo";
|
|
312
|
+
var t = +e.tamanho || 320;
|
|
313
|
+
// A caixa e o prisma são o cubo achatado num eixo: uma medida só muda a
|
|
314
|
+
// silhueta inteira, e o autor não precisa pensar em seis faces.
|
|
315
|
+
var prof = forma === "cartao" || forma === "painel" ? Math.max(6, Math.round(t * 0.045))
|
|
316
|
+
: (forma === "caixa" ? Math.round(t * 0.62) : t);
|
|
317
|
+
var faces = Array.isArray(e.faces) ? e.faces : [];
|
|
318
|
+
var cores = Array.isArray(e.cores) ? e.cores : [];
|
|
319
|
+
|
|
320
|
+
no.classList.add("obj3d", "f-" + forma);
|
|
321
|
+
no.style.width = _px(t);
|
|
322
|
+
no.style.height = _px(forma === "cartao" ? Math.round(t * 0.62) : t);
|
|
323
|
+
no.style.perspective = _px(e.perspectiva || t * 3.4);
|
|
324
|
+
|
|
325
|
+
var corpo = document.createElement("div");
|
|
326
|
+
corpo.className = "corpo3d";
|
|
327
|
+
corpo.style.width = _px(t);
|
|
328
|
+
corpo.style.height = _px(forma === "cartao" ? Math.round(t * 0.62) : t);
|
|
329
|
+
|
|
330
|
+
var meia = prof / 2;
|
|
331
|
+
var h = forma === "cartao" ? Math.round(t * 0.62) : t;
|
|
332
|
+
var lados = [
|
|
333
|
+
["translateZ(" + meia + "px)", 0],
|
|
334
|
+
["rotateY(180deg) translateZ(" + meia + "px)", 1],
|
|
335
|
+
["rotateY(90deg) translateZ(" + (t / 2) + "px)", 2],
|
|
336
|
+
["rotateY(-90deg) translateZ(" + (t / 2) + "px)", 3],
|
|
337
|
+
["rotateX(90deg) translateZ(" + (h / 2) + "px)", 4],
|
|
338
|
+
["rotateX(-90deg) translateZ(" + (h / 2) + "px)", 5],
|
|
339
|
+
];
|
|
340
|
+
// Cartão e painel têm só frente e verso: as bordas viram espessura, e
|
|
341
|
+
// desenhar seis faces num objeto de 6px de profundidade só faria sujeira.
|
|
342
|
+
var quantas = (forma === "cartao" || forma === "painel") ? 2 : 6;
|
|
343
|
+
for (var i = 0; i < quantas; i++) {
|
|
344
|
+
var lado = lados[i];
|
|
345
|
+
var f = _face3d(faces[lado[1]] != null ? faces[lado[1]] : (faces[0] || ""),
|
|
346
|
+
lado[0], cores[lado[1] % (cores.length || 1)]);
|
|
347
|
+
// As laterais são estreitas (a profundidade) e o topo/base são baixos.
|
|
348
|
+
//
|
|
349
|
+
// Só mudar a medida não bastava: a face é `inset: 0`, então encostava na
|
|
350
|
+
// borda esquerda em vez de ficar no meio — e a rotação, que gira em
|
|
351
|
+
// torno do centro, abria o objeto. O cubo saía com as faces soltas, sem
|
|
352
|
+
// fechar. Reposicionar a face no centro antes de girar fecha a caixa.
|
|
353
|
+
if (i >= 2 && i < 4) {
|
|
354
|
+
f.style.width = _px(prof);
|
|
355
|
+
f.style.left = _px(Math.round((t - prof) / 2));
|
|
356
|
+
}
|
|
357
|
+
if (i >= 4) {
|
|
358
|
+
f.style.height = _px(prof);
|
|
359
|
+
f.style.top = _px(Math.round((h - prof) / 2));
|
|
360
|
+
}
|
|
361
|
+
corpo.appendChild(f);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
var giro = e.girar == null ? 14 : +e.girar;
|
|
365
|
+
var inclina = e.inclinar == null ? -14 : +e.inclinar;
|
|
366
|
+
if (giro > 0) {
|
|
367
|
+
corpo.style.animation = "giro3d " + giro + "s linear infinite";
|
|
368
|
+
corpo.style.setProperty("--inc", inclina + "deg");
|
|
369
|
+
} else {
|
|
370
|
+
corpo.style.transform = "rotateX(" + inclina + "deg) rotateY(" + (e.angulo || -28) + "deg)";
|
|
371
|
+
}
|
|
372
|
+
no.appendChild(corpo);
|
|
373
|
+
}
|
|
142
374
|
|
|
143
375
|
function renderElemento(e, ctx) {
|
|
144
376
|
var no = document.createElement("div");
|
|
145
377
|
var tipo = e.tipo || "texto";
|
|
378
|
+
/* Qual elemento do documento é este nó.
|
|
379
|
+
|
|
380
|
+
O editor precisava saber, e antes deduzia pela ordem dos nós na tela.
|
|
381
|
+
A dedução estava errada: os layouts de fluxo separam cabeçalho de corpo
|
|
382
|
+
e desenham o cabeçalho primeiro, então a ordem na tela NÃO é a ordem do
|
|
383
|
+
documento. O efeito era clicar num texto e o editor selecionar outro —
|
|
384
|
+
e mexer no elemento errado. O índice vem carimbado daqui. */
|
|
385
|
+
if (ctx && ctx.ordem && ctx.ordem.has(e)) no.dataset.i = ctx.ordem.get(e);
|
|
146
386
|
/* Tipo inventado ("titulo", "subtitulo"...) caía num default sem estilo:
|
|
147
387
|
texto de 16px num canvas de 1920 — invisível — ou div vazio quando o
|
|
148
388
|
conteúdo estava noutro campo. O servidor novo recusa; documentos velhos
|
|
@@ -178,11 +418,27 @@ function renderElemento(e, ctx) {
|
|
|
178
418
|
|
|
179
419
|
/* animação */
|
|
180
420
|
var an = e.anim || ctx.animPadrao;
|
|
181
|
-
if (an && an !== "nenhuma") {
|
|
421
|
+
if (an && an !== "nenhuma" && !(e.chaves && e.chaves.length > 1)) {
|
|
182
422
|
no.setAttribute("data-an", typeof an === "string" ? an : (an.entrada || "subir"));
|
|
183
423
|
no.style.setProperty("--atraso", (typeof an === "object" && an.atraso != null ? an.atraso : ctx.i * 0.09 + 0.1));
|
|
184
424
|
}
|
|
185
425
|
|
|
426
|
+
/* ── chaves de animação ────────────────────────────────────────────────
|
|
427
|
+
Antes um elemento só sabia ENTRAR: escolhia-se um efeito de entrada e
|
|
428
|
+
acabava ali. Não dava para fazer nada percorrer a cena, crescer devagar
|
|
429
|
+
ou sumir no fim — que é o que separa animação de slide com efeito.
|
|
430
|
+
|
|
431
|
+
Uma chave é um instante: `{t: 0.5, x: 200, escala: 1.2}` diz onde o
|
|
432
|
+
elemento está na metade da cena. Duas ou mais viram uma animação CSS de
|
|
433
|
+
verdade, gerada aqui, com a duração da cena. O navegador interpola;
|
|
434
|
+
nós só descrevemos os pontos.
|
|
435
|
+
|
|
436
|
+
Quem tem chaves não recebe animação de entrada: as duas brigariam pelo
|
|
437
|
+
mesmo transform, e a chave é a mais específica das duas. */
|
|
438
|
+
if (ctx.animar && e.chaves && e.chaves.length > 1) {
|
|
439
|
+
aplicarChaves(no, e, ctx);
|
|
440
|
+
}
|
|
441
|
+
|
|
186
442
|
switch (tipo) {
|
|
187
443
|
case "texto": {
|
|
188
444
|
var papel = PAPEIS[e.papel] || PAPEIS.corpo;
|
|
@@ -355,6 +611,11 @@ function renderElemento(e, ctx) {
|
|
|
355
611
|
break;
|
|
356
612
|
}
|
|
357
613
|
|
|
614
|
+
case "objeto3d": {
|
|
615
|
+
montar3d(no, e);
|
|
616
|
+
break;
|
|
617
|
+
}
|
|
618
|
+
|
|
358
619
|
case "no": {
|
|
359
620
|
if (e.destaque) no.classList.add("destaque");
|
|
360
621
|
if (e.formato === "circulo") no.classList.add("circulo");
|
|
@@ -546,6 +807,7 @@ function renderCena(cena, doc, op) {
|
|
|
546
807
|
if (cena.tom) no.style.setProperty("--c-tom", cena.tom);
|
|
547
808
|
if (cena.tom2) no.style.setProperty("--c-tom2", cena.tom2);
|
|
548
809
|
aplicarFundo(no, cena.fundo);
|
|
810
|
+
aplicarMarca(no, cena, doc, H);
|
|
549
811
|
|
|
550
812
|
var layout = cena.layout || (op.layoutPadrao || "titulo");
|
|
551
813
|
var elementos = (cena.elementos || []).slice();
|
|
@@ -560,7 +822,21 @@ function renderCena(cena, doc, op) {
|
|
|
560
822
|
// tela vertical pede tipografia maior: os padrões são calibrados em 1920x1080
|
|
561
823
|
var escalaTipo = H > W ? 1.35 : 1;
|
|
562
824
|
no.style.setProperty("--esc", escalaTipo);
|
|
563
|
-
var ctx = {
|
|
825
|
+
var ctx = {
|
|
826
|
+
i: 0, escalaTipo: escalaTipo,
|
|
827
|
+
animPadrao: op.animar ? (cena.anim || "subir") : null,
|
|
828
|
+
// as chaves precisam saber quanto dura a cena para virar segundos
|
|
829
|
+
animar: !!op.animar,
|
|
830
|
+
duracao: cena.duracao || 4,
|
|
831
|
+
raiz: no,
|
|
832
|
+
// objeto do elemento -> posição dele em cena.elementos, tirada ANTES de
|
|
833
|
+
// qualquer separação por layout
|
|
834
|
+
ordem: (function () {
|
|
835
|
+
var m = new Map();
|
|
836
|
+
(cena.elementos || []).forEach(function (e, i) { m.set(e, i); });
|
|
837
|
+
return m;
|
|
838
|
+
})(),
|
|
839
|
+
};
|
|
564
840
|
|
|
565
841
|
if (layout === "livre") {
|
|
566
842
|
fluxo.style.padding = "0";
|
package/studio/web/render.css
CHANGED
|
@@ -582,3 +582,50 @@
|
|
|
582
582
|
.cena .el { animation: none !important; opacity: 1 !important; }
|
|
583
583
|
.folha-doc { padding: 0; width: auto; min-height: 0; }
|
|
584
584
|
}
|
|
585
|
+
|
|
586
|
+
/* ══════════════════════════════════════════════════════════════════════════
|
|
587
|
+
Objetos 3D
|
|
588
|
+
|
|
589
|
+
Perspectiva de verdade, com transform 3D do navegador. Sem WebGL e sem
|
|
590
|
+
biblioteca — o Studio não tem dependência e não vai passar a ter por isto.
|
|
591
|
+
═════════════════════════════════════════════════════════════════════════ */
|
|
592
|
+
.el.obj3d { display: grid; place-items: center; }
|
|
593
|
+
.corpo3d {
|
|
594
|
+
position: relative;
|
|
595
|
+
transform-style: preserve-3d;
|
|
596
|
+
transform: rotateX(-14deg) rotateY(-28deg);
|
|
597
|
+
}
|
|
598
|
+
.face3d {
|
|
599
|
+
/* Sem isto a borda e o padding SOMAM ao tamanho pedido: cada face fica
|
|
600
|
+
maior do que a medida usada no translateZ, e o cubo não fecha — as
|
|
601
|
+
faces ficam soltas, com fresta. O render.css é usado sozinho no HTML
|
|
602
|
+
exportado, então não dá para contar com o reset do core.css. */
|
|
603
|
+
box-sizing: border-box;
|
|
604
|
+
position: absolute; inset: 0;
|
|
605
|
+
display: grid; place-items: center;
|
|
606
|
+
background: var(--c-tom, #ff5a36);
|
|
607
|
+
color: #fff;
|
|
608
|
+
font: 700 clamp(14px, 2.2vw, 40px)/1.15 var(--f-display, inherit);
|
|
609
|
+
text-align: center;
|
|
610
|
+
padding: 6%;
|
|
611
|
+
border: 1px solid rgba(255, 255, 255, .16);
|
|
612
|
+
border-radius: 6px;
|
|
613
|
+
box-shadow: inset 0 0 90px rgba(0, 0, 0, .34);
|
|
614
|
+
backface-visibility: visible;
|
|
615
|
+
overflow: hidden;
|
|
616
|
+
}
|
|
617
|
+
.face3d img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
|
618
|
+
/* cada face pega uma luz diferente, senão o cubo vira uma mancha só */
|
|
619
|
+
.face3d:nth-child(2) { filter: brightness(.78); }
|
|
620
|
+
.face3d:nth-child(3) { filter: brightness(.88); }
|
|
621
|
+
.face3d:nth-child(4) { filter: brightness(.7); }
|
|
622
|
+
.face3d:nth-child(5) { filter: brightness(1.1); }
|
|
623
|
+
.face3d:nth-child(6) { filter: brightness(.6); }
|
|
624
|
+
|
|
625
|
+
@keyframes giro3d {
|
|
626
|
+
from { transform: rotateX(var(--inc, -14deg)) rotateY(0deg); }
|
|
627
|
+
to { transform: rotateX(var(--inc, -14deg)) rotateY(360deg); }
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/* painel: sem moldura, é uma superfície flutuando em profundidade */
|
|
631
|
+
.el.obj3d.f-painel .face3d { border: 0; box-shadow: 0 40px 90px -30px rgba(0,0,0,.7); }
|