Сейчас узнаем, какой подарок выпадет именно Вам
Крутите колесо
Как создать калькулятор рассрочки в Тильда
Рассрочка
Оформление рассрочки
Рассчитайте ежемесячный платёж за 30 секунд
Сумма, ₽
Первоначальный взнос, ₽
0%
Срок, мес
Общая стоимость
0 ₽
Наценка
0 ₽
Ежемесячный платеж
0 ₽
Оставьте заявку и мы поможем оформить рассрочку в ближайшее время на выгодных для вас условиях

Как создать калькулятор рассрочки в Тильда

Библиотека для примера
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.8.0"></script>

<script>
document.addEventListener('DOMContentLoaded', function() {
    
    const donutColor1 = '#D6D6D6';
    const donutColor2 = '#FF6B01';
    
    const forms = document.querySelectorAll('.form-section div[data-elem-type="form"]');
    let renderedForms = 0;
    const totalForms = forms.length;
    let chart = null;

    function initCalculator() {
      
        if (window._calculatorInitialized) {
            return;
        }
        window._calculatorInitialized = true;

        let control = true;


        const diagramm = document.querySelector('.diagramm');
        if (diagramm) {
            diagramm.innerHTML = '<div class="diag-wrapper"><canvas id="doughnutChartID"></canvas></div>';
        }


        const posPriceRange = document.querySelector('input[name="pos-price-range"]');
        const prepaidRange = document.querySelector('input[name="prepaid-range"]');
        const termRange = document.querySelector('input[name="term-range"]');
        const posPriceInp = document.querySelector('input[name="pos-price-inp"]');
        const prepaidInp = document.querySelector('input[name="prepaid-inp"]');
        const termInp = document.querySelector('input[name="term-inp"]');

        const monthPayEl = document.querySelector('.month-pay .tn-atom');
        const creditEl = document.querySelector('.credit .tn-atom');
        const finalPercentEl = document.querySelector('.final-percent .tn-atom');
        const percentNumEl = document.querySelector('.percent-num .tn-atom');

        if (!posPriceRange || !prepaidRange || !termRange) {
            console.warn('Не все элементы формы найдены');
            return;
        }

        function formatNumber(num) {
            return Math.round(num).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' ');
        }

        
        function cleanNumber(str) {
            if (!str) return 0;
            return parseInt(str.toString().replace(/[^0-9]/g, '')) || 0;
        }

        function updateRangeLabels() {
            const formSection = document.querySelector('.form-section');
            if (!formSection) return;

            const rangeGroups = formSection.querySelectorAll('.t-input-group_rg');
            
            rangeGroups.forEach(function(group) {
                const rangeInput = group.querySelector('input[type="range"]');
                if (!rangeInput) return;

                const minVal = parseInt(rangeInput.min) || 0;
                const maxVal = parseInt(rangeInput.max) || 0;

                const minLabel = group.querySelector('.t-range__interval-txt_min');
                const maxLabel = group.querySelector('.t-range__interval-txt_max');

                if (minLabel) {
                    minLabel.setAttribute('data-num', formatNumber(minVal));
                }
                if (maxLabel) {
                    maxLabel.setAttribute('data-num', formatNumber(maxVal));
                }
            });
        }

        function countCalc() {
            let summa = cleanNumber(posPriceRange.value);
            let vznos = cleanNumber(prepaidRange.value);
            let srok = cleanNumber(termRange.value);
            let percent = 105;

            let creditSumm = summa - vznos;
            if (creditSumm <= 0) creditSumm = 0;
            let monthPercent = percent / 12 / 100;
            let degree = Math.pow(1 + monthPercent, -srok);
            let monthCredit = creditSumm * (monthPercent / (1 - degree));

            let finalSumm = monthCredit * srok;
            let percentSumm = finalSumm - creditSumm;
            let percentSumm2 = percentSumm;
            let income = monthCredit * 1.6667;

            let itSumm = creditSumm + vznos + percentSumm;
            let creditSumm2 = itSumm;

            monthCredit = formatNumber(monthCredit);
            creditSumm = formatNumber(creditSumm);
            itSumm = formatNumber(itSumm);
            finalSumm = formatNumber(finalSumm);
            percentSumm = formatNumber(percentSumm);
            income = formatNumber(income);

            if (monthPayEl) monthPayEl.textContent = monthCredit + ' ₽';
            if (creditEl) creditEl.textContent = itSumm + ' ₽';
            if (finalPercentEl) finalPercentEl.textContent = percentSumm + ' ₽';

            let allSumm = creditSumm2 + percentSumm2;
            let prcDiag = Math.round((creditSumm2 * 100) / allSumm);

            if (chart) {
                chart.data.datasets[0].data[0] = 100 - prcDiag;
                chart.data.datasets[0].data[1] = prcDiag;
                chart.update();
            }

            setTimeout(function() {
                const hiddenCost = document.querySelector('input[type="hidden"][name="Сумма"]');
                const hiddenPrepaid = document.querySelector('input[type="hidden"][name="Первоначальный взнос"]');
                const hiddenTerm = document.querySelector('input[type="hidden"][name="Срок"]');
                const hiddenRate = document.querySelector('input[type="hidden"][name="Ставка"]');
                const hiddenTotal = document.querySelector('input[type="hidden"][name="Итоговая стоимость"]');
                const hiddenMarkup = document.querySelector('input[type="hidden"][name="Торговая наценка"]');
                const hiddenPayment = document.querySelector('input[type="hidden"][name="Ежемесячный платёж"]');

                if (hiddenCost) hiddenCost.value = posPriceInp ? posPriceInp.value : '';
                if (hiddenPrepaid) hiddenPrepaid.value = prepaidInp ? prepaidInp.value : '';
                if (hiddenTerm) hiddenTerm.value = termInp ? termInp.value : '';
                if (hiddenRate) hiddenRate.value = percent + '%';
                if (hiddenTotal) hiddenTotal.value = itSumm + ' ₽';
                if (hiddenMarkup) hiddenMarkup.value = percentSumm + ' ₽';
                if (hiddenPayment) hiddenPayment.value = monthCredit + ' ₽';

                updateRangeLabels();
            }, 500);
        }

  
        if (typeof Chart !== 'undefined') {
            const canvas = document.getElementById('doughnutChartID');
            if (canvas) {
                try {
                    chart = new Chart(canvas, {
                        type: 'doughnut',
                        data: {
                            datasets: [{
                                data: [100, 0],
                                backgroundColor: [donutColor1, donutColor2],
                                spacing: 1,
                                rotation: 90,
                                borderRadius: 5,
                            }],
                        },
                        options: {
                            cutout: 28,
                        },
                    });
                } catch(e) {
                    console.error('Ошибка создания графика:', e);
                }
            }
        }

        function setInpVal() {
            if (posPriceInp && posPriceRange) {
                posPriceInp.value = formatNumber(cleanNumber(posPriceRange.value)) + ' ₽';
            }
            
            if (prepaidInp && prepaidRange) {
                let prc = cleanNumber(prepaidRange.value);
                prepaidInp.value = formatNumber(prc) + ' ₽';
            }

            if (percentNumEl && posPriceRange && prepaidRange) {
                let summa = cleanNumber(posPriceRange.value);
                let prc = cleanNumber(prepaidRange.value);
                let percent = summa > 0 ? ((prc * 100) / summa).toFixed(1) : '0';
                percentNumEl.textContent = percent + '%';
            }

            if (termInp && termRange) {
                let term = cleanNumber(termRange.value);
                let pdps = ' мес';
                if (term == 1 || term == 21 || term == 31) pdps = ' мес';
                if (term == 2 || term == 3 || term == 4 || term == 22 || term == 23 || term == 24 || term == 32 || term == 33 || term == 34) pdps = ' мес';
                termInp.value = term + pdps;
            }
        }

    
        function updateRangeFromInput(input, range, format = true, suffix = ' ₽') {
            if (!input || !range) return;
            
            let val = cleanNumber(input.value);
            let min = parseInt(range.min) || 0;
            let max = parseInt(range.max) || 0;
            
            val = Math.max(min, Math.min(max, val));
            
            range.value = val;
            
            if (format) {
                input.value = formatNumber(val) + suffix;
            }
            
            setInpVal();
            countCalc();
            
            range.dispatchEvent(new Event('input', { bubbles: true }));
            range.dispatchEvent(new Event('change', { bubbles: true }));
        }

        function maxCorrect() {
            setTimeout(function() {
                if (posPriceRange && prepaidRange) {
                    let costt = cleanNumber(posPriceRange.value);
                    let newMax = Math.round(+costt * 0.50);
                    let newMin = Math.round(+costt * 0.25);
                    prepaidRange.max = newMax;
                    prepaidRange.min = newMin;
                    
                    let prepaidVal = cleanNumber(prepaidRange.value);
                    if (prepaidVal > newMax) {
                        prepaidRange.value = newMax;
                        if (prepaidInp) {
                            prepaidInp.value = formatNumber(newMax) + ' ₽';
                        }
                    } else if (prepaidVal < newMin) {
                        prepaidRange.value = newMin;
                        if (prepaidInp) {
                            prepaidInp.value = formatNumber(newMin) + ' ₽';
                        }
                    }
                    
                    prepaidRange.dispatchEvent(new Event('input', { bubbles: true }));
                    updateRangeLabels();
                    
                    setInpVal();
                    countCalc();
                }
            }, 500);
        }

        function digits_int(target) {
            let val = target.value.replace(/[^0-9.]/g, '');
            if (val) {
                let num = parseInt(val);
                target.value = formatNumber(num);
            }
        }

        setTimeout(function() {
            maxCorrect();
            setInpVal();
            countCalc();
            setTimeout(updateRangeLabels, 100);
        }, 1500);

 
        if (posPriceInp) {
            posPriceInp.addEventListener('blur', function(e) {
                updateRangeFromInput(this, posPriceRange, true, ' ₽');
                maxCorrect();
            });
            
            posPriceInp.addEventListener('input', function(e) {
                let val = this.value.replace(/[^0-9]/g, '');
                let num = parseInt(val) || 0;
                this.value = formatNumber(num) + ' ₽';
            });
        }

        if (prepaidInp) {
            prepaidInp.addEventListener('blur', function(e) {
                updateRangeFromInput(this, prepaidRange, true, ' ₽');
            });
            
            prepaidInp.addEventListener('input', function(e) {
                let val = this.value.replace(/[^0-9]/g, '');
                let num = parseInt(val) || 0;
                this.value = formatNumber(num) + ' ₽';
            });
        }

        if (termInp) {
            termInp.addEventListener('blur', function(e) {
                let term = cleanNumber(this.value);
                let min = parseInt(termRange.min) || 0;
                let max = parseInt(termRange.max) || 0;
                
                term = Math.max(min, Math.min(max, term));
                termRange.value = term;
                
                let pdps = ' мес';
                if (term == 1 || term == 21 || term == 31) pdps = ' мес';
                if (term == 2 || term == 3 || term == 4 || term == 22 || term == 23 || term == 24 || term == 32 || term == 33 || term == 34) pdps = ' мес';
                this.value = term + pdps;
                
                setInpVal();
                countCalc();
                termRange.dispatchEvent(new Event('input', { bubbles: true }));
            });
            
            termInp.addEventListener('input', function(e) {
                let term = cleanNumber(this.value);
                let pdps = ' мес';
                if (term == 1 || term == 21 || term == 31) pdps = ' мес';
                if (term == 2 || term == 3 || term == 4 || term == 22 || term == 23 || term == 24 || term == 32 || term == 33 || term == 34) pdps = ' мес';
                this.value = term + pdps;
            });
        }


        if (posPriceRange) {
            posPriceRange.addEventListener('input', function() {
                setInpVal();
                countCalc();
            });
            
            posPriceRange.addEventListener('change', function() {
                maxCorrect();
            });
        }

        if (prepaidRange) {
            prepaidRange.addEventListener('input', function() {
                setInpVal();
                countCalc();
            });
        }

        if (termRange) {
            termRange.addEventListener('input', function() {
                setInpVal();
                countCalc();
            });
        }
    }

    function checkFormsRendered() {
        let allRendered = true;
        forms.forEach(function(form) {
            if (!form.querySelector('input, select, textarea')) {
                allRendered = false;
            }
        });
        return allRendered;
    }

    if (totalForms === 0) {
        initCalculator();
        return;
    }

    if (checkFormsRendered()) {
        initCalculator();
        return;
    }

    forms.forEach(function(form) {
        form.addEventListener('render', function onRender() {
            renderedForms++;
            if (renderedForms === totalForms) {
                initCalculator();
            }
            form.removeEventListener('render', onRender);
        });
    });

    setTimeout(function() {
        if (renderedForms < totalForms) {
            console.warn('Не все формы отрендерились за отведенное время, принудительная инициализация');
            initCalculator();
        }
    }, 5000);
});
</script>



<style>

.diag-wrapper {
    height: inherit;
    width: inherit;
}

#allrecords .form-section [data-elem-type="form"]:has(.t-input-group_rg) {
    z-index: 1
}

.form-section .t-range__value-txt {
    display: none !important;
}    


.form-section input.t-range {
    filter: brightness(0.94);
}

.form-section .t-range__interval-txt {
    opacity: 0.7;
    font-size: 0px;
}


.form-section .t-range__interval-txt:before {
    content: attr(data-num);
    font-size: 12px;
}

.form-section .t-range__interval-txt:after {
    content: " ₽";
    font-size: 12px;
}

.form-section [data-field-name="term-range"] .t-range__interval-txt:after {
    content: " мес";
    font-size: 12px;
}


.form-section .t-range::-webkit-slider-runnable-track {
  height: 4px;
  border-radius: 2px;    
}

.form-section .t-range::-webkit-slider-thumb {
  -webkit-appearance: none; 
  appearance: none;
  width: 14px;            
  height: 14px;          
  margin-top: -6px;  
  box-shadow: 0 0 0 2px #ffffff, 0 0 0 3px #e37d1c;
  transition: all 0.1s ease-in-out;
}

.form-section .t-range::-webkit-slider-thumb:hover {
    transform: scale(1.1);
}


.form-section .t-range::-moz-range-track {
  height: 4px;
  border-radius: 2px;
}

.form-section .t-range::-moz-range-thumb {
  width: 14px;           
  height: 14px;           
  border: none;           
  border-radius: 50%;    
  box-shadow: 0 0 0 2px #ffffff, 0 0 0 3px #e37d1c;
  transition: all 0.1s ease-in-out;
}

.form-section .t-range::-moz-range-thumb:hover {
    transform: scale(1.1);
}
</style>
Made on
Tilda