feat(rosto): motor de palpebras, 4 rostos novos, formato do olho e tela Rosto
- rosto-motor.js: emocoes como posicao de palpebra (cima, inclinacao, baixo) com transicao suave; respiracao, sacadas e piscada que achata o olho - rostos novos Carinha Viva, Bloco, Matriz (LEDs) e Lente (iris/pupila); os classicos continuam; emoji/figurinha nos olhos seguem no SVG - formato do olho (padrao, redondo, pilula, quadrado, largo) para Viva, Bloco e Matriz; salvo na aparencia e enviado como ordem "formato X" - toque na tela do S10: cutucar = susto e risada, deslizar = carinho, tocar dormindo acorda - painel: tela Rosto (celular em destaque, cartoes de rosto, formatos, cores, testar emocoes so na previa ou no celular, lista do que ja esta pronto); Ao vivo leva a ela; paleta ganha rostos e formatos - corrige /comando rosto que nao salvava a aparencia Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+45
-2
@@ -25,6 +25,7 @@
|
||||
<path id="sobrancelha2" class="traco" style="display:none"/>
|
||||
</svg>
|
||||
<div id="fps"></div>
|
||||
<script src="rosto-motor.js"></script>
|
||||
<script>
|
||||
/* Token do servidor (o app Android abre a página com ?t=...): vai em todo pedido ao
|
||||
próprio servidor, inclusive os de reacoes-render.js. Sem ?t= nada muda. */
|
||||
@@ -103,10 +104,17 @@ const BRANCO = [245, 250, 255];
|
||||
// A "biblioteca de rostos": cada um diz se tem boca e o quanto os olhos
|
||||
// crescem. Trocar de rosto pela rede: curl "http://orangepi:8099/face/miudo"
|
||||
const ROSTOS = {
|
||||
vivo: { rotulo: "Carinha Viva", boca: true, escala: 1.00, motor: "vivo" },
|
||||
bloco: { rotulo: "Bloco", boca: false, escala: 1.00, motor: "bloco" },
|
||||
matriz: { rotulo: "Matriz", boca: false, escala: 1.00, motor: "matriz" },
|
||||
lente: { rotulo: "Lente", boca: false, escala: 1.00, motor: "lente" },
|
||||
eilik: { rotulo: "Eilik", boca: false, escala: 1.00 },
|
||||
carinha: { rotulo: "Carinha", boca: true, escala: 1.00 },
|
||||
miudo: { rotulo: "Miudo", boca: true, escala: 0.80 },
|
||||
};
|
||||
// Formato do olho nos rostos novos (rosto-motor.js): padrao, redondo, pilula, quadrado, largo.
|
||||
const FORMATOS = ["padrao", "redondo", "pilula", "quadrado", "largo"];
|
||||
let FORMATO = "padrao";
|
||||
let ROSTO_NOME = "carinha";
|
||||
let ROSTO = ROSTOS[ROSTO_NOME];
|
||||
|
||||
@@ -380,12 +388,25 @@ function desenhar() {
|
||||
const mover = seguindo ? .55 : (r.kf !== null || r.dormindo) ? 0.25 : (mirando ? 0.25 : 0.0);
|
||||
const dx = seguindo ? MOV.olharX : r.dx, dy = seguindo ? MOV.olharY : r.dy;
|
||||
const olhos = [[E1, ab, r.forma, +1], [E2, ab2, f2, -1]];
|
||||
for (let i = 0; i < 2; i++) {
|
||||
const comMotor = ROSTO.motor && window.MotorRosto && !window.carinhaOlhosIcone && !window.carinhaOlhosDesenho;
|
||||
OLHOS.forEach((g) => { g.style.display = comMotor ? "none" : ""; });
|
||||
if (comMotor) {
|
||||
window.MotorRosto.desenhar({
|
||||
estilo: ROSTO.motor, formato: FORMATO, t: tAgora, cor: r.cor, dormindo: r.dormindo,
|
||||
parado: !r.kf && !seguindo && !r.dormindo,
|
||||
olhos: olhos.map(([c, abx, fx, lado]) => ({
|
||||
cx: c[0] + dx * mover * ESC, cy: c[1] + dy * mover * ESC, ry: RY * esc,
|
||||
ab: abx, forma: fx, lado, lx: dx, ly: dy * 0.6,
|
||||
})),
|
||||
});
|
||||
} else if (window.MotorRosto) window.MotorRosto.esconder();
|
||||
for (let i = 0; i < 2 && !comMotor; i++) {
|
||||
const [c, abx, fx, lado] = olhos[i];
|
||||
olho(i, c[0] + dx * mover * ESC, c[1] + dy * mover * ESC, RX * esc, RY * esc,
|
||||
abx, fx, r.cor, lado, dx, dy * 0.6);
|
||||
}
|
||||
desenharBoca();
|
||||
if (ROSTO.motor && !ROSTO.boca) elBoca.style.display = "none";
|
||||
else desenharBoca();
|
||||
for (let i = 0; i < 2; i++) {
|
||||
const brow = document.getElementById('sobrancelha' + (i + 1));
|
||||
brow.style.display = EXTRAS.sobrancelhas && !r.dormindo ? '' : 'none';
|
||||
@@ -430,6 +451,9 @@ function trata(ordem) {
|
||||
trocarRosto(ordem.slice(6).trim());
|
||||
} else if (ROSTOS[ordem]) {
|
||||
trocarRosto(ordem); // aceita so o nome: "miudo"
|
||||
} else if (ordem.indexOf("formato ") === 0) {
|
||||
const f = ordem.slice(8).trim();
|
||||
if (FORMATOS.includes(f)) FORMATO = f;
|
||||
} else if (ordem.indexOf("cor ") === 0) {
|
||||
const v = ordem.slice(4).split(",").map(Number);
|
||||
if (v.length === 3 && v.every((n) => !isNaN(n))) {
|
||||
@@ -501,6 +525,7 @@ async function aplicarAparencia() {
|
||||
if (!resp.ok) return;
|
||||
const c = await resp.json();
|
||||
if (c.rosto && ROSTOS[c.rosto]) trocarRosto(c.rosto);
|
||||
if (FORMATOS.includes(c.formato)) FORMATO = c.formato;
|
||||
if (Array.isArray(c.cor) && c.cor.length === 3 && c.cor.every((n) => !isNaN(n)))
|
||||
{ CIANO.splice(0, 3, ...c.cor.map(Number)); r.cor = c.cor.map(Number); }
|
||||
} catch (e) { /* sem servidor — segue com o padrao */ }
|
||||
@@ -519,6 +544,24 @@ if (MODO_PAINEL) {
|
||||
});
|
||||
} else if (new URLSearchParams(location.search).get('espelho') === '1') setInterval(aplicarAparencia, 1000);
|
||||
|
||||
// toque na tela: cutucar dá susto e depois risada; deslizar o dedo é carinho.
|
||||
// O toque longo (menu do app) passa direto: só reagimos a toques curtos.
|
||||
(function () {
|
||||
let inicio = null;
|
||||
addEventListener('pointerdown', (e) => { inicio = { x: e.clientX, y: e.clientY, t: performance.now() }; });
|
||||
addEventListener('pointerup', (e) => {
|
||||
if (!inicio) return;
|
||||
const dx = e.clientX - inicio.x, dy = e.clientY - inicio.y, dur = performance.now() - inicio.t;
|
||||
inicio = null;
|
||||
if (r.dormindo) { trata('acordar'); return; }
|
||||
if (Math.abs(dx) > 60 && Math.abs(dx) > Math.abs(dy) && dur < 900) { animar('coracao'); return; }
|
||||
if (dur < 350 && Math.hypot(dx, dy) < 15) {
|
||||
animar('surpreso');
|
||||
setTimeout(() => animar('feliz'), 700);
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
||||
// teclado (util para testar no PC)
|
||||
const TECLAS = { p: "piscar", f: "feliz", c: "coracao", s: "surpreso", b: "bravo",
|
||||
t: "triste", a: "alegre", o: "olhar_esq", i: "olhar_dir",
|
||||
|
||||
Reference in New Issue
Block a user