基于javascript实现的贷款计算器

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>基于JavaScript实现的贷款计算器</title>
    <style>
      /*这是一个css样式表:定义看程序输出的样式*/
        .output {font-weight: bold;}
        #payment {text-decoration: underline;}
        #graph{border: solid black 1px}
        th ,td {
            vertical-align: top;
        }
    </style>
</head>
<body>
<table>
    <tr>
        <th>
            输入贷款数据:
        </th>
        <td></td>
        <th>贷款余额,累计权益和利息支付</th>
    </tr>
    <tr>
        <td>贷款额($):</td>
        <!--onchange 事件会在域的内容改变时发生。-->
        <td><input id="amount" onchange="calculate()"></td>
        <!--rowspan 属性规定单元格可横跨的行数。-->
        <td rowspan="8">
            <canvas id="graph" width="400" height="250"></canvas>
        </td>
    </tr>
    <tr>
        <td>年利(%):</td>
        <td><input id="apr" onchange="calculate()"></td>
    </tr>
    <tr>
        <td>还款期限(年):</td>
        <td><input id="years" onchange="calculate()"></td>
    </tr>
    <tr>
        <td>邮编(找到贷方):</td>
        <td><input id="zipcode" onchange="calculate()"></td>
    </tr>
    <tr>
        <th>近似付款:</th>
        <td>
            <button onclick="calculate()">计算</button>
        </td>
    </tr>
    <tr>
        <td>每月支付:</td>
        <td>$<span class="output" id="payment"></span></td>
    </tr>
    <tr>
        <td>总付款:</td>
        <td>$<samp class="output" id="total"></samp></td>
    </tr>
    <tr>
        <td>总利息:</td>
        <td>$<span class="output" id="totalinterset"></span></td>
    </tr>
    <tr>
        <td>赞助商:</td>
        <td colspan="2">
            向这些优良贷款人申请贷款:
            <div id="lenders"></div>
        </td>
    </tr>
</table>
<script>
    "use strict";       //如果浏览器支持的话,则开始ECMAScript5的严格模式

    function calculate() {
        var amount = document.getElementById("amount");
        var apr = document.getElementById("apr");
        var years = document.getElementById("years");
        var zipcode = document.getElementById("zipcode");
        var payment = document.getElementById("payment");
        var total = document.getElementById("total");
        var totalinterset = document.getElementById("totalinterset");

        var principal = parseFloat(amount.value); //将百分比格式转换成小数格式
        var interest = parseFloat(apr.value) / 100 / 12;//年利率转换成月利率
        var payments = parseFloat(years.value) * 12;//将年度赔付转换成月度赔付

        //现在计算月度赔付的数据
        var x = Math.pow(1 + interest, payments);//进行幂运算
        var monthly = (principal * x * interest) / (x - 1);
        // isFinite() 函数用于检查其参数是否是无穷大。
        if (isFinite(monthly)) {
            //将数据填充至输出字段的位置,四舍五入到小数点后两位数字
            payment.innerHTML = monthly.toFixed(2);  //toFixed() 方法可把 Number 四舍五入为指定小数位数的数字。
            total.innerHTML = (monthly * payments).toFixed(2);
            totalinterset.innerHTML = ((monthly * payments) - 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, payments)
        } else {
            //计算结果不是数组或者视无穷大,意味着输入数据是非法或者不完整的清空之前的输出数据
            payment.innerHTML = "";
            total.innerHTML = "";
            totalinterset.innerHTML = "";
            chart();
        }

    }

    //将用户的输入保存至localStorage对象的属性中
    //这些属性在再次访问时还会继续保持在原位置
    //如果你在浏览器中按照file://URL的方式直接打开本地文件
    //则无法在某些浏览器中使用存储功能比如firefox
    //而通过HTTP打开文件是可行的
    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.loan_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) {
        //如果浏览器不支持XMLHttpRequest对象,则退出
        if (!window.XMLHttpRequest) return;

        //找到要显示放贷人列表的元素
        var ad = document.getElementById("lenders");
        if (!ad) return;            //如果返回为空,则退出

        //将客户的输入数据进行URL编码,并作为查询参数附加在URL里
        var url = "getLenders.php" +            //处理数据的URL地址
            "?amt=" + encodeURIComponent(amount) +      //使用查询串中的数据
            "&apr=" + encodeURIComponent(apr) +
            "&yrs=" + encodeURIComponent(years) +
            "&zip=" + encodeURIComponent(zipcode);

        //通过XMLHttpRequest对象来提取返回数据
        var req = new XMLHttpRequest();     //发起一个新的请求
        req.open("GET", url);    //通过url发起一个HTTP GET请求
        req.send(null);         //不带任何正文发送这个请求

        //在返回数据之前,注册了一个事件处理函数,这个处理函数将会在服务器的响应
        //返回至客户端的时候调用,这种异步编程模型在客户端JavaScript中是非常常见的
        req.onreadystatechange = function () {
            if (req.readyState == 4 && req.status == 200) {
                //如果代码运行到这里,说明我们得到了一个合法且完整的HTTP响应
                var response = req.responseText;    //HTTP响应是以字符串形式呈现的
                var lenders = JSON.parse(response); //将其解析为JS数组
                //将数组中国的放贷人对象转换为HTML字符串形式
                var list = "";
                for (var i = 0; i < lenders.length; i++) {
                    list += "<li><a href='" + lenders[i].url + "'>" + lenders[i].name + "</a></li>"
                }

                //将数据在HTML元素中呈现出来
                ad.innerHTML = "<ul>" + list + "</ul>";
            }
        }
    }

    //在HTML<canvas>元素中用图表展示月度贷款余额,利息和资产收益
    //如果不传入参宿的话,则清空之前的图表数据
    function chart(principal, interest, monthly, payments) {
        var graph = document.getElementById("graph"); //得到<canvas>
        graph.width = graph.width;  //用一种巧妙的手法清楚并重置画布

        //获取画布元素的"context"对象,这个对象定义了一组会话API
        var g = graph.getContext("2d");    //所有的绘画操作都将基于这个对象
        var width = graph.width,
            height = graph.height;      //获取画布大小

        //这里讲函数左右是将付款数字和美元数据转换为像素
        function paymentToX(n) {
            return n * width / payments;
        }

        function amountToY(a) {
            return height - (a * height / (monthly * payments * 1.05));
        }

        //付款数据时一条从(0,0)到(payments,monthly * payments)的直线
        g.moveTo(paymentToX(0), amountToY(0));   //从左下方开始
        g.lineTo(paymentToX(payments),     //绘至右上方
            amountToY(monthly * payments));
        g.lineTo(paymentToX(payments), amountToY(0));    //再至右下方
        g.closePath();                              //将结尾连接至开头
        g.fillStyle = "#f88";           //亮红色
        g.fill();                       //填充矩形
        g.font = "bold 12px sans-serif";        //定义一种字体
        g.fillText("总利息支付", 20, 20);    //将文字回执到图例中

        //很多资产数据并不是线性的,很难将其反应至图表中
        var equity = 0;
        g.beginPath();      //开始绘制新的图形
        g.moveTo(paymentToX(0), amountToY(0));
        for (var p = 1; p <= payments; p++) {
            // 计算出每一笔赔付的利息
            var thisMonthsInterest = (principal - equity) * interest;
            equity += (monthly - thisMonthsInterest);       //得到资产额
            g.lineTo(paymentToX(p), amountToY(equity));//将数据绘制到画布
        }
        g.lineTo(paymentToX(payments), amountToY(0));    //将数据绘制至X轴
        g.closePath();                  //将线条结尾连接至线条开头
        g.fillStyle = "green";      //使用绿色绘制图形
        g.fill();
        g.fillText("总资产", 20, 35);
        /*文本设置为绿色*/


        //再次循环,余额数据显示为黑色粗体线条
        var bal = principal;
        g.beginPath();
        g.moveTo(paymentToX(0), amountToY(bal));
        for (var p = 1; p <= payments; p++) {
            var thisMonthsInterest = bal * interest;
            bal -= (monthly - thisMonthsInterest);      //得到资产额
            g.lineTo(paymentToX(p), amountToY(bal))
        }
        g.lineWidth = 3;        //将直线宽度加粗
        g.stroke();         //绘制余额曲线
        g.fillStyle = "black";      //使用黑色字体
        g.fillText("贷款余额", 20, 50);

        //将年度数据在X轴做标记
        g.textAlign = "center";         //文字居中对齐
        var y = amountToY(0);            //Y坐标设为0
        for (var year = 1; year * 12 <= payments; year++) {
            var x = paymentToX(year * 12);
            g.fillRect(x - 0.5, y - 3, 1, 3);
            if (year == 1)
                g.fillText("正确", x, y - 5);
            if (year % 5 == 0 && year * 12 !== payments) {
                g.fillText(String(year), x, y - 5);
            }
            g.textAlign = "right";              //文字右对齐
            g.textBaseline = "middle";          //文字垂直居中
            var ticks = [monthly * payments, payments];
            var rightEde = paymentToX(payments);
            for (var i = 0; i < ticks.length; i++) {
                var y = amountToY(ticks[i]);
                g.fillRect(rightEde - 3, y - 0.5, 3, 1);
                g.fillText(String(ticks[i].toFixed(0)), rightEde - 5, y);
            }
        }
    }


</script>
</body>
</html>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,951评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,606评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,601评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,478评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,565评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,587评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,590评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,337评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,785评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,096评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,273评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,935评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,578评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,199评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,440评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,163评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,133评论 2 352

推荐阅读更多精彩内容