// Base do kit isometrico: cores, fontes, curvas e medidas. Todo número visual sai daqui. // Os elementos importam só este arquivo, 'react' e 'remotion'. Nada de fora da pasta do kit. import {continueRender, delayRender} from 'remotion'; import f0Url from './fontes/archivo.woff2'; import f1Url from './fontes/jost.woff2'; // ---------- tela ---------- export const LARGURA = 1080, ALTURA = 1920, FPS = 30; // área segura (Reels/TikTok): o que se lê fica entre estas linhas export const SEGURA = {topo: 168, base: 1330, lado: 80}; export const LARGURA_UTIL = LARGURA - SEGURA.lado * 2; // 920 px // ---------- cor ---------- export const COR = { fundo: '#DDE9F0', topo: '#EBF3F8', tinta: '#1B2B4B', mute: '#6F819F', coral: '#F26B5B', mostarda: '#F2B134', verde: '#1FA6A0', marinho: '#233A66', creme: '#FFF8EC', destaque: '#F26B5B', cortina: '#F26B5B', }; // ---------- fonte ---------- // Famílias com o prefixo do kit (IS), pra não colidir com outro kit no mesmo estúdio. export const FONTE_D = '"IS Archivo", "Archivo", system-ui, sans-serif'; export const FONTE_T = '"IS Jost", "Jost", system-ui, sans-serif'; export const FONTE_S = '"IS Jost", "Jost", system-ui, sans-serif'; const LISTA = [ {fam: 'IS Archivo', url: f0Url, peso: '500 800', estilo: 'normal'}, {fam: 'IS Jost', url: f1Url, peso: '400 600', estilo: 'normal'}, ]; if (typeof document !== 'undefined' && !(globalThis as Record).__kit_IS) { (globalThis as Record).__kit_IS = true; for (const f of LISTA) { const h = delayRender('isometrico: ' + f.fam); new FontFace(f.fam, `url(${f.url}) format('woff2')`, {weight: f.peso, style: f.estilo}).load() .then((ff) => {document.fonts.add(ff); continueRender(h);}) .catch(() => continueRender(h)); } } // ---------- tempo e curva ---------- export const c01 = (t: number) => Math.max(0, Math.min(1, t)); /** progresso 0..1 de f entre a e b */ export const seg = (f: number, a: number, b: number) => c01((f - a) / (b - a)); /** saída rápida e pouso longo: cubic-bezier(.2,.8,.2,1) aproximado */ export const e5 = (t: number) => 1 - Math.pow(1 - c01(t), 5); /** entrada com overshoot de ~10% (easeOutBack, c1 = 2.2) */ export const eb = (t: number) => {const x = c01(t) - 1, c = 2.2; return 1 + (c + 1) * x * x * x + c * x * x;}; /** entra e sai suave */ export const e3 = (t: number) => {const x = c01(t); return x * x * (3 - 2 * x);}; export const lerp = (a: number, b: number, t: number) => a + (b - a) * t; /** uma batida da trilha do kit em quadros */ export const BATIDA = 15.0; // ---------- texto ---------- /** "Usar *IA* já" -> [{t:'Usar ', d:false}, {t:'IA', d:true}, {t:' já', d:false}] */ export const partes = (texto: string) => texto.split('*').map((t, i) => ({t, d: i % 2 === 1})).filter((p) => p.t.length > 0); export const textoLimpo = (t: string) => t.replace(/\*/g, ''); let canvas: HTMLCanvasElement | null = null; const cache = new Map(); export const larguraTexto = (texto: string, tamanho: number, peso = 800, trackingEm = -0.03, familia = FONTE_D): number => { const chave = `${texto}|${tamanho}|${peso}|${trackingEm}|${familia}`; const c = cache.get(chave); if (c !== undefined) return c; if (typeof document === 'undefined') return texto.length * tamanho * 0.56; canvas = canvas || document.createElement('canvas'); const ctx = canvas.getContext('2d'); if (!ctx) return texto.length * tamanho * 0.56; const fonte = `${peso} ${tamanho}px ${familia}`; ctx.font = fonte; const w = ctx.measureText(texto).width + texto.length * trackingEm * tamanho; if (document.fonts.check(fonte)) cache.set(chave, w); return w; }; /** o maior tamanho até `tamanho` em que o texto cabe em `largura`. Nunca palavra cortada. */ export const caber = (texto: string, tamanho: number, largura = LARGURA_UTIL, peso = 800, trackingEm = -0.03, familia = FONTE_D) => { const w = larguraTexto(textoLimpo(texto), tamanho, peso, trackingEm, familia); return w > largura ? Math.floor(tamanho * (largura / w)) : tamanho; }; // ---------- câmera ---------- export const CAMERA = {"z": 1.02, "zOsc": 0.01, "push": 0.0004, "dx": 10, "dy": 8, "rot": 0.004, "soco": 0.03};