Simulador de Consórcio

Simulador de Consórcio
:root{ –bg:#f8fafc; –card:#ffffff; –ink:#0f172a; –muted:#475569; –primary:#16a34a; –line:#e2e8f0; –warn:#dc2626; } *{box-sizing:border-box} html,body{margin:0;background:var(–bg);color:var(–ink);font-family:system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,”Helvetica Neue”,Arial,”Noto Sans”,sans-serif} h1{font-size:clamp(1.3rem,2.5vw,1.8rem);margin:0 0 12px} p.lead{color:var(–muted);margin:0 0 24px} .grid{gap:16px;grid-template-columns:1fr} @media (min-width: 880px){.grid{grid-template-columns: 1fr 1.2fr}} .card{background:var(–card);border:1px solid var(–line);border-radius:14px;box-shadow:0 1px 0 rgba(0,0,0,.03);padding:18px} label{display:block;font-weight:600;margin:8px 0 6px} .row{display:grid;grid-template-columns:1fr;gap:12px} @media(min-width:580px){.row.two{grid-template-columns:1fr 1fr}} .field{position:relative} .suffix{ position:absolute; right:12px; top:50%; translate:0 -50%; color:var(–muted); pointer-events:none; font-weight:600; } input{ width:100%;padding:12px 14px;border:1px solid var(–line);border-radius:10px;background:#fff;color:var(–ink); font:inherit;outline:none; } input:focus{border-color:#94a3b8;box-shadow:0 0 0 3px rgba(148,163,184,.25)} .actions{display:flex;flex-wrap:wrap;gap:10px;margin-top:14px} .btn{ display:inline-block !important;text-decoration:none !important;border:1px solid transparent !important;border-radius:999px !important;padding:12px 18px !important;font-weight:800 !important; transition:transform .05s ease, box-shadow .2s ease, background .2s ease !important; } .btn[role=”button”]{cursor:pointer !important} .btn-primary{background:var(–primary);color:#fff !important;box-shadow:0 2px 0 rgba(0,0,0,.05) !important} .btn-primary:hover,.btn-primary:focus-visible{background:#22c55e !important} .btn-primary:active{transform:translateY(1px) !important} .btn-secondary{background:#fff !important;color:var(–ink);border-color:var(–line) !important} .btn-secondary:hover,.btn-secondary:focus-visible{background:#f1f5f9 !important} .btn-ghost{background:transparent !important;color:var(–muted) !important} .hint{font-size:.9rem;color:var(–muted);margin-top:6px} .error{color:var(–warn);font-weight:600;margin:8px 0} .summary{display:grid;grid-template-columns:1fr;gap:12px} @media(min-width:700px){.summary{grid-template-columns:repeat(3,1fr)}} .kpi{background:#fff;border:1px dashed var(–line);border-radius:12px;padding:12px} .kpi .k{font-size:.85rem;color:var(–muted)} .kpi .v{font-size:1.15rem;font-weight:800;margin-top:6px} table{width:100%;border-collapse:collapse} thead th{position:sticky;top:0;background:#fff;border-bottom:2px solid var(–line);text-align:left;font-size:.9rem;padding:10px} tbody td{border-bottom:1px solid var(–line);padding:10px;font-size:.95rem} .table-wrap{max-height:420px;overflow:auto;border:1px solid var(–line);border-radius:12px;background:#fff} .muted{color:var(–muted)} .badge{display:inline-block;background:#ecfeff;border:1px solid #a5f3fc;color:#0e7490;padding:2px 8px;border-radius:999px;font-size:.75rem;font-weight:700} .sr-only{position:absolute;left:-10000px;width:1px;height:1px;overflow:hidden} .footnote{margin-top:12px;color:var(–muted);font-size:.9rem}

Descubra o valor das parcelas no consórcio imobiliário ou consórcio de veículos e planeje sua conquista com segurança.
Parcelas = Cota do bem (sem juros) + Taxa de adm. + Fundo de reserva + Seguro (sobre a parcela total). Reajuste anual do crédito aplicado no mês 13, 25, 37…

Formulário de Simulação

%
Percentual sobre o crédito total, diluído nas parcelas.
%
%
Aplicado ao saldo do crédito no mês 13, 25, 37…
%
Total = Base ÷ (1 − %Seguro); Seguro = Total − Base.
Abatido do crédito para recalcular a cota do bem.

Resultado

Resumo da primeira parcela

Cota do Bem
Base sem Seguro
Parcela Total

Reajuste anual aplica-se apenas à cota do bem. Taxa de administração e fundo de reserva permanecem constantes.

Mês Cota do Bem Taxa Adm. Fundo Res. Seguro Parcela Total Saldo Crédito Reajuste?

Seguro calculado sobre a parcela total: Total = Base ÷ (1 − p), p em %/100.

document.getElementById(id); const inputs = { credito: $(‘credito’), prazo: $(‘prazo’), taxaAdm: $(‘taxaAdm’), fundoReserva: $(‘fundoReserva’), reajuste: $(‘reajuste’), seguro: $(‘seguro’), lance: $(‘lance’) }; const out = { erro: $(‘erro’), kParcelaBem: $(‘kParcelaBem’), kBase: $(‘kBase’), kTotal: $(‘kTotal’), tb: $(‘tb’), tabelaWrap: $(‘tabelaWrap’) }; // ========== // Máscaras // ========== function onlyDigits(str){ return (str||”).replace(/\D+/g,”); } // Moeda – máscara simples enquanto digita function formatCurrencyBR(v){ const digits = onlyDigits(v); const n = (Number(digits)||0)/100; return n.toLocaleString(‘pt-BR’,{style:’currency’,currency:’BRL’}); } function parseCurrencyBR(str){ if (!str) return NaN; const cleaned = String(str).replace(/\s/g,”).replace(/[R$\u00A0]/g,”).replace(/\./g,”).replace(‘,’, ‘.’); return Number(cleaned); } document.querySelectorAll(‘input[data-mask=”currency”]’).forEach(inp=>{ inp.addEventListener(‘input’, ()=>{ const selEnd = inp.selectionEnd; inp.value = formatCurrencyBR(inp.value); inp.setSelectionRange(inp.value.length, inp.value.length); }); }); // Percentual “easy”: digita só número/virgula/ponto; sem % durante a edição function parsePercentToNumber(str){ if (!str) return NaN; // aceita “6”, “6,5”, “6.5”, “16,25” const s = String(str).trim().replace(‘%’,”).replace(/\s/g,”).replace(‘,’, ‘.’); return Number(s); } function formatPercentBR(num){ if (!isFinite(num)) return ”; return num.toLocaleString(‘pt-BR’,{minimumFractionDigits:2, maximumFractionDigits:4}) + ‘%’; } function attachPercentEasy(inp){ // ao focar: tira % e deixa só número (com vírgula opcional) inp.addEventListener(‘focus’, ()=>{ const raw = String(inp.value||”).replace(‘%’,”).trim(); // Converte “6,00%” -> “6,00” const s = raw.replace(/\s/g,”); inp.value = s; // posiciona no fim requestAnimationFrame(()=> inp.setSelectionRange(inp.value.length, inp.value.length)); }); // ao sair: formata e aplica % inp.addEventListener(‘blur’, ()=>{ const n = parsePercentToNumber(inp.value); if (isFinite(n)) inp.value = formatPercentBR(n); else inp.value = ”; }); // durante a digitação: bloqueia caracteres não numéricos (exceto , .) inp.addEventListener(‘input’, ()=>{ let v = inp.value; v = v.replace(/[^\d,\.]/g,”); // impede mais de um separador decimal const parts = v.split(/[,\.]/); if (parts.length > 2){ v = parts[0] + ‘,’ + parts.slice(1).join(”); } inp.value = v; }); } document.querySelectorAll(‘input[data-mask=”percent-easy”]’).forEach(attachPercentEasy); // ========== // Utilidades // ========== const fmtBRL = n => isFinite(n) ? n.toLocaleString(‘pt-BR’,{style:’currency’,currency:’BRL’}) : ‘—’; function safeNumber(v, def=0){ return isFinite(v) ? v : def; } function validate(){ out.erro.textContent = ”; const credito = parseCurrencyBR(inputs.credito.value); const prazo = Math.floor(Number(inputs.prazo.value.replace(/\D+/g,”))); const taxaAdm = parsePercentToNumber(inputs.taxaAdm.value); if (!isFinite(credito) || credito <= 0) return 'Informe um valor de crédito válido.'; if (!isFinite(prazo) || prazo <= 0) return 'Informe um prazo (meses) válido.'; if (!isFinite(taxaAdm) || taxaAdm = 100) return ‘O seguro não pode ser igual ou superior a 100%.’; return null; } function simular(){ const err = validate(); if (err){ out.erro.textContent = err; out.tabelaWrap.scrollTop = 0; return; } const credito = parseCurrencyBR(inputs.credito.value); const prazo = Math.floor(Number(inputs.prazo.value.replace(/\D+/g,”))); const taxaAdmTotalPct = safeNumber(parsePercentToNumber(inputs.taxaAdm.value),0); const fundoReservaPct = safeNumber(parsePercentToNumber(inputs.fundoReserva.value),0); const reajusteAnualPct = safeNumber(parsePercentToNumber(inputs.reajuste.value),0); const seguroPct = safeNumber(parsePercentToNumber(inputs.seguro.value),0); const lance = safeNumber(parseCurrencyBR(inputs.lance.value),0); const creditoBaseInicial = Math.max(credito – Math.max(0, Math.min(lance, credito)), 0); // Componentes fixos mensais (adm/FR) sobre crédito original const admMensal = (taxaAdmTotalPct/100 * credito) / prazo; const frMensal = (fundoReservaPct/100 * credito) / prazo; let saldo = creditoBaseInicial; let parcelasRestantes = prazo; let cotaBemAtual = parcelasRestantes > 0 ? (saldo / parcelasRestantes) : 0; out.tb.innerHTML = ”; let primeiraLinha = null; for (let mes = 1; mes 1 && ((mes – 1) % 12 === 0) && reajusteAnualPct > 0){ const fator = 1 + (reajusteAnualPct/100); saldo = saldo * fator; cotaBemAtual = saldo / parcelasRestantes; houveReajuste = true; } const cotaBem = cotaBemAtual; const base = cotaBem + admMensal + frMensal; const p = (seguroPct/100); const parcelaTotal = (p >= 1) ? Infinity : base / (1 – p); const seguro = parcelaTotal – base; if (mes === 1){ primeiraLinha = { cota: cotaBem, base, total: parcelaTotal }; } saldo = Math.max(0, saldo – cotaBem); parcelasRestantes = Math.max(0, parcelasRestantes – 1); cotaBemAtual = parcelasRestantes > 0 ? (saldo / parcelasRestantes) : 0; const tr = document.createElement(‘tr’); const cols = [ mes, fmtBRL(cotaBem), fmtBRL(admMensal), fmtBRL(frMensal), fmtBRL(seguro), fmtBRL(parcelaTotal), fmtBRL(saldo), houveReajuste ? ‘Sim’ : ‘—’ ]; cols.forEach(txt=>{ const td = document.createElement(‘td’); td.textContent = txt; tr.appendChild(td); }); out.tb.appendChild(tr); } if (primeiraLinha){ out.kParcelaBem.textContent = fmtBRL(primeiraLinha.cota); out.kBase.textContent = fmtBRL(primeiraLinha.base); out.kTotal.textContent = fmtBRL(primeiraLinha.total); } document.getElementById(‘resultado’).scrollIntoView({behavior:’smooth’, block:’start’}); out.tabelaWrap.focus(); } function limpar(){ inputs.credito.value = ”; inputs.prazo.value = ”; inputs.taxaAdm.value = ”; inputs.fundoReserva.value = ”; inputs.reajuste.value = ”; inputs.seguro.value = ”; inputs.lance.value = ”; out.erro.textContent = ”; out.kParcelaBem.textContent = ‘—’; out.kBase.textContent = ‘—’; out.kTotal.textContent = ‘—’; out.tb.innerHTML = ”; document.body.scrollIntoView({behavior:’smooth’, block:’start’}); } function baixarCSV(){ if (!out.tb.children.length){ simular(); if (!out.tb.children.length) return; } let csv = ‘Mes,Cota do Bem,Taxa Adm.,Fundo Res.,Seguro,Parcela Total,Saldo Credito,Reajuste\n’; […out.tb.children].forEach(tr=>{ const cols = […tr.children].map(td=> td.textContent .replace(/\./g,”).replace(‘R$’,”).replace(/\s/g,”).replace(‘,’, ‘.’)); csv += cols.join(‘,’) + ‘\n’; }); const blob = new Blob([csv],{type:’text/csv;charset=utf-8;’}); const url = URL.createObjectURL(blob); const a = document.createElement(‘a’); a.href = url; a.download = ‘simulacao-consorcio.csv’; document.body.appendChild(a); a.click(); setTimeout(()=>{ URL.revokeObjectURL(url); a.remove(); }, 100); } // Ações $(‘btnCalcular’).addEventListener(‘click’, e=>{ e.preventDefault(); simular(); }); $(‘btnLimpar’).addEventListener(‘click’, e=>{ e.preventDefault(); limpar(); }); $(‘btnCSV’).addEventListener(‘click’, e=>{ e.preventDefault(); baixarCSV(); }); // Enter dispara calcular Object.values(inputs).forEach(inp=>{ inp.addEventListener(‘keydown’, ev=>{ if (ev.key === ‘Enter’){ ev.preventDefault(); simular(); } }); }); // Exemplos (edite à vontade) inputs.credito.value = ‘R$ 120.000,00’; inputs.prazo.value = ‘120’; inputs.taxaAdm.value = ‘16,00’; inputs.fundoReserva.value = ‘3,00’; inputs.reajuste.value = ‘6,00’; inputs.seguro.value = ‘0,50’; inputs.lance.value = ”; })(); //–>