• Note(2): 一个JavaScript的贷款计算器


    抽时间对照着案例撸了一遍,学到了蛮多东西,也有很多疑问,其中有个疑问仍是没有撸明白

    graph.width = graph.width; //所谓的巧妙用法?
    

    好了废话不多说,贴出源码: 

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>JavaScript Loan Calcualtor</title>
        <style>
            .output {
                font-weight: bold
            }
    
            #payment {
                text-decoration: underline
            }
    
            #graph {
                border: solid black 1px
            }
    
            th, td {
                vertical-align: top
            }
        </style>
        <script>
            "user strict";  //如果浏览器支持的话,则开启ECMAScript 5的严格模式
    
            // 查找文档中用于输入输出的元素
            function $(id) {
                return document.getElementById(id);
            }
    
            function calculate() {
                //假设所有的输入都是合法的, 将从input元素中获取输入数据
                //将百分比格式转换为小数格式, 并从年利率转换为月利率
                //将年度赔付转换为月度赔付
                var amount = $("amount"),
                    apr = $("apr"),
                    years = $("years"),
                    zipcode = $("zipcode"),
                    payments = $("payment"),
                    total = $("total"),
                    totalInterest = $("totalInterest"),
                    principal = parseFloat(amount.value),
                    interest = parseFloat(apr.value) / 100 / 12,
                    payment = parseFloat(years.value) * 12;
    
                //现在计算月度赔付的数据
                var x = Math.pow(1 + interest, payment),
                    monthly = (principal * x * interest) / (x - 1);
    
                if (isFinite(monthly)) {
                    //四舍五入到小数点后两位数字
                    payments.innerHTML = monthly.toFixed(2);
                    total.innerHTML = (monthly * payment).toFixed(2);
                    totalInterest.innerHTML = ((monthly * payment) - principal).toFixed(2);
    
                    //将用户输入的数据保存下来,这样在下次访问的时候也能取到数据
                    save(amount.value, apr.value, years.value, zipcode.value);
    
                    try { //捕获这段代码抛出的所有异常
                        getLenders(amount.value, apr.value, years.value, zipcode.value);
                    }
                    catch (e) {/* 忽略这些异常 */
                    }
    
                    //最后用图标展示贷款余额、利息和资产收益
                    chart(principal, interest, monthly, payment);
                } else {
                    payment.innerHTML = "";
                    total.innerHTML = "";
                    totalInterest.innerHTML = "";
                    chart();
                }
            }
    
            function save(amount, apr, years, zipcode) {
                if (window.localStorage) { //只有在浏览器支持的时候才运行这里的代码
                    localStorage.loan_amount = amount;
                    localStorage.loan_apr = apr;
                    localStorage.loan_years = years;
                    localStorage.loan_zipcode = zipcode;
                }
            }
    
            window.onload = function () {
                //如果浏览器支持本地存储并且上次保存的值是存在的
                if (window.localStorage && localStorage.lan_amount) {
                    document.getElementById("amount").value = localStorage.loan_amount;
                    document.getElementById("apr").value = localStorage.loan_apr;
                    document.getElementById("years").value = localStorage.loan_years;
                    document.getElementById("zipcode").value = localStorage.loan_zipcode;
                }
            }
    
            function getLenders(amount, apr, years, zipcode) {
                if (window.XMLHttpRequest) return;
    
                var ad = document.getElementById("lenders");
                if (!ad) return;
    
                var url = "getLenders.php" +
                    "?amt=" + encodeURIComponent(amount) +
                    "&apr=" + encodeURIComponent(apr) +
                    "&yrs=" + encodeURIComponent(years) +
                    "&zip=" + encodeURIComponent(zipcode);
                var req = new XMLHttpRequest();
                req.open("GET", url);
                req.send(null);   //不带任何正文发送这个请求
    
                req.onreadystatechange = function () {
                    if (req.readyState == 4 && req.status == 200) {
                        var respones = req.responseText;
                        var lenders = JSON.pares(response);  //将其解析为js数组
                        var list = "";
                        for (var i = 0; i < lenders.length; i++) {
                            list += "<li><a href=' " + lenders[i].url + " '>" + lenders[i].name + "</a>";
                        }
                        ad.innerHTML = "<ul>" + list + "</ul>"
                    }
                }
            }
    
            function chart(principal, interest, monthly, payment) {
                var graph = document.getElementById("graph");
                //如果不传入参数,或者浏览器不支持画布,则直接返回
                if (arguments.length == 0 || !graph.getContext) return;
                //graph.width = graph.width; //所谓的巧妙用法?
                //获得画布元素的"context"对象,这个对象定义了一组绘画API
                var g = graph.getContext("2d");
                var width = graph.width,
                    height = graph.height;
    
                //将付款数字和美元数据转换为像素
                function paymentToX(n) {
                    return n * width / payment;
                }
    
                function amountToY(a) {
                    return height - (a * height / (monthly * payment * 1.05));
                }
    
                //画线
                // 付款数据是一条从(0,0)到()
                g.moveTo(paymentToX(0), amountToY(0)); //起点
                g.lineTo(paymentToX(payment), amountToY(monthly * payment));
                g.lineTo(paymentToX(payment), amountToY(0));
                g.closePath();
                g.fillStyle = "#f88";  //亮红色
                g.fill();
                g.font = "bold 12px sans-serif"; //自定义一种字体
                g.fillText("Total Interest Payments", 20, 20);  //将文字绘制到图例中
    
                //很多资产数据并不是线性的,很难将其反映至图表中
                var equity = 0;
                g.beginPath();
                g.moveTo(paymentToX(0), amountToY(0));
                for (var p = 1; p <= payment; p++) {
                    //计算出每一笔赔付的利息
                    var thisMonthsInterest = (principal - equity) * interest;
                    equity += (monthly - thisMonthsInterest);
                    g.lineTo(paymentToX(p), amountToY(equity));
                }
                g.lineTo(paymentToX(payment), amountToY(0));
                g.closePath();
                g.fillStyle = "green";
                g.fill();
                g.fillText("Total Equity", 20, 35);
    
                //再次循环, 余额数据显示未黑色粗线条
                var bal = principal;
                g.beginPath();
                g.moveTo(paymentToX(0), amountToY(bal));
                for (var p = 1; p <= payment; p++) {
                    var thisMonthsInterest = bal * interest;
                    bal -= (monthly - thisMonthsInterest);
                    g.lineTo(paymentToX(p), amountToY(bal));
                }
                g.lineWidth = 3;
                g.stroke();
                g.fillStyle = "black";
                g.fillText("Loan Balance", 20, 50);
    
                //将年度数据在X轴做标记
                g.textAlign = "center";
                var y = amountToY(0);
                for (var year = 1; year * 12 <= payment; year++) {
                    var x = paymentToX(year * 12);
                    g.fillRect(x - 0.5, y - 3, 1, 3); //开始绘制标记
                    if (year == 1) g.fillText("Year", x, y - 5);
                    if (year % 5 == 0 && year * 12 !== payment) {
                        g.fillText(String(year), x, y - 5);
                    }
                }
    
                //将赔付数额标记在右边界
                g.textAlign = "right";
                g.textBaseline = "middle"; //文字垂直居中
                var ticks = [monthly * payment, principal];
                var rightEdge = paymentToX(payment);
                for (var i = 0; i < ticks.length; i++) {
                    var y = amountToY(ticks[i]);
                    g.fillRect(rightEdge - 3, y - 0.5, 3, 1);
                    g.fillText(String(ticks[i].toFixed(0)),
                        rightEdge - 5, y);
                }
    
            }
        </script>
    </head>
    
    <body>
    <table>
        <tr>
            <th>Enter Loan Data:</th>
            <td></td>
            <th>Loan Balance, Cumulative Equity, and Interest Payments</th>
        </tr>
        <tr>
            <td>Amount of the loan ($):</td>
            <td><input id="amount" onchange="calculate()"></td>
            <td rowspan=8>
                <canvas id="graph" width="400" height="250"></canvas>
            </td>
        </tr>
        <tr>
            <td>Annual interest (%):</td>
            <td><input id="apr" onchange="calculate()"></td>
        </tr>
        <tr>
            <td>Repayment period (years):</td>
            <td><input id="years" onchanges="calculate"></td>
        </tr>
        <tr>
            <td>Zipcode (to find lenders):</td>
            <td><input id="zipcode" onchange="calculate()"></td>
        </tr>
        <tr>
            <th>Approximate Payments:</th>
            <td>
                <button onclick="calculate()">Calculate</button>
            </td>
        </tr>
        <tr>
            <td>Monthly payment:</td>
            <td>$<span class="output" id="payment"></span></td>
        </tr>
        <tr>
            <td>Total payment:</td>
            <td>$<span class="output" id="total"></span></td>
        </tr>
        <tr>
            <td>Total interest:</td>
            <td>$<span class="output" id="totalInterest"></span></td>
        </tr>
        <tr>
            <th>Sponsors:</th>
            <td colspan=2>Apply for your loan with one of these fine lenders:
                <div id="lenders"></div>
            </td>
        </tr>
    </table>
    </body>
    </html>
    

    贴上最终效果图:

    扔在起点摸索中,欢迎大佬解答疑惑。

     

  • 相关阅读:
    前端代码美化的艺术
    CSS3 简单的砸金蛋样式
    为什么 VS Code 能迅速占领 JavaScript 开发者社区
    了解并使用 CSS 中的 rem 单位
    CSS中一些利用伪类、伪元素和相邻元素选择器的技巧
    SVG入门指南
    PAT 1035 Password [字符串][简单]
    生信学习-二代测序知乎专栏总结[转]
    PAT 1119 Pre- and Post-order Traversals [二叉树遍历][难]
    生信笔记-mooc【武大】
  • 原文地址:https://www.cnblogs.com/metersj/p/8205058.html
Copyright © 2020-2023  润新知