纯JS实现房贷利率报表对比

最近朋友买房,想计算下自己的房贷的还款情况,自己正好周末没事,从网上找来点代码修改,也算是对自己技术的巩固吧。

目前这个还只是个初级版本,暂时可以在PC上正常访问,将来会一步一步的把相继功能都加上的,将要完成的功能:

  1. 可以在Android手机上访问的界面对齐、美化等。
  2. 图表的优化。
  3. 等额本金法和等额本息法的实现。

以上功能暂列这么多,以后有想到的在慢慢往上加;也希望大家多多的提出你的意见。

以后会把每次都更新版本都添加到文章的后面,方便历史版本的追溯。样图:



版本一:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>JS计算器,贷款利率计算器</title>
<!--参考出处:http://www.codefans.net/jscss/code/3613.shtml-->

<style> /* 这是一个CSS样式表:定义了程序输出的样式 */
body{ font-size:12px;}
#payment { text-decoration: underline; } /* 定义 id="payment" 的元素样式 */
#graph { border: solid black 1px; } /* 图表有一个1像素的边框 */
th, td {vertical-align: top; } /* 表格单元格对齐方式为顶端对齐 */
table{
    border-collapse: collapse;
    border: none;
}
td, th{
    border: solid #000 1px;
}
.output { font-weight: bold; } /* 计算结果定义为粗体 */
.btn{
    font-weight: 700;
    line-height: 30px;
    width: 100px;
    height: 35px;
}
.help
{
    float:right;
    cursor:pointer;
    line-height: 10px;
    margin-top: -4px;
    margin-right: -4px;
}
.nr {
    color: #666;
    font-size: 12px;
    padding: 10px 0 5px
}
.tsk {
    background: #f8f8f8;
    border: 1px solid #CCC;
    box-shadow: 0 1px 5px #DDD;
    line-height: 20px;
    padding: 10px;
    position: absolute;
    display: none;
    z-index: 10000
}

.circle {
    width: 15px;
    height: 15px;
    font-weight: 700;
    text-indent:4px;

        cursor:pointer;
    float:left;
    line-height:16px;
    background: #5A8BDC;
    -moz-border-radius: 50px;
    -webkit-border-radius: 50px;
    border-radius: 50px;
}
</style>

<script>
    function $(id){ return document.getElementById(id); }
    function helpClick(obj)
    {
        $(obj.id+"_help").style.display = "block";
    }

    function hidHelp()
    {
        $("debxhk_help").style.display = "none";
        $("debjhk_help").style.display = "none";
    }

</script>

</head>
<body>

  <table>
   <tbody>
    <tr>
     <th colspan=2>输入数据:</th>
    </tr>
    <tr>
     <td width=100px>
      <!--Amount of the loan-->贷款总额:</td>
     <td><input id="amount"  value="500000" /><span style="margin-left:-18px">元</span></td>
    </tr>
    <tr>
     <td>
      <!--Annual interest-->年利息:</td>
     <td><input id="apr"  value="5.65" /><span style="margin-left:-14px">%</span></td>
    </tr>
     <td>
      利率折扣:</td>
     <td>
         <select id=llzk style="width:156px;">
              <option value="0.7" >最新基准利率7折</option>
               <option value="0.8" >最新基准利率8折</option>
               <option value="0.83">最新基准利率8.3折</option>
               <option value="0.85">最新基准利率8.5折</option>
               <option value="0.88">最新基准利率8.8折</option>
               <option value="0.9" >最新基准利率9折</option>
               <option value="0.95">最新基准利率9.5折</option>
               <option value="1.0" selected=true>最新基准利率</option>
               <option value="1.05">最新基准利率1.05倍</option>
               <option value="1.1" >最新基准利率1.1倍</option>
               <option value="1.2" >最新基准利率1.2倍</option>
               <option value="1.3" >最新基准利率1.3倍</option>
            </select>
     </td>
    </tr>
    <tr>
     <td>
      <!--Repayment period (years)-->偿还期限:</td>
     <td><input id="years"  value="30" /><span style="margin-left:-18px">年</span></td>
    </tr>
    <tr style="display:none">
     <td>
      <!--Zipcode (to find lenders)-->邮政编码(查找放贷人):</td>
     <td><input id="zipcode"  /></td>
    </tr>
    <tr>
     <th colspan=2>
      <input class="btn" type=button onclick="calculate();" value="计 算" />&nbsp;&nbsp;&nbsp;
      <input class="btn" type=reset value="重 置" />
     </th>
    </tr>
    <tr>
         <th colspan=2>
      <!--Approximate Payments-->输出结果:</th>
    </tr>
    <tr>
     <td>
      <!--Monthly payment-->每月付款:</td>
     <td><span class="output" id="payment">0</span></td>
    </tr>
    <tr>
     <td>
      <!--Total payment-->付款总额:</td>
     <td><span class="output" id="total">0</span></td>
    </tr>
    <tr>
     <td>
      <!--Total interest-->利息总额:</td>
     <td><span class="output" id="totalinterest">0</span></td>
    </tr>
    <tr>
     <td colspan=2>图表:贷款金额,累计金额,利息支付</td>
    </tr>
    <tr>
        <td colspan="2">
            <canvas id="graph" width="500" height="250"></canvas>
        </td>
    </tr>
    <tr>
        <td colspan="2">
            <table id="result" style="width:100%">
       <tbody>
        <tr>
         <td style="position:relative;width:50%;">
          <div>
           <span style="float:left">每月等额还款</span>
           <div class="circle" onclick=helpClick(this) id="debxhk">?</div>
          </div>
          <div class="tsk" id="debxhk_help" style="width:214px;left:1px;top:16px">
              <div onclick="hidHelp()" class="help">&nbsp;x</div>
           <div class="nr">
             每月等额还款即等额本息还款法,指借款人每月按相等的金额偿还贷款本息,其中每月贷款利息按月初剩余贷款本金计算并逐月结清。
           </div>
          </div>
          <table style="clear:both" cellpadding="0" cellspacing="0" class="tbl" >
           <tbody>
            <tr>
             <td class="td1" width=100px>贷款总额</td>
             <td class="td2"><var id="_debx_dkje">0</var> 元</td>
            </tr>
            <tr>
             <td class="td1">还款月数</td>
             <td class="td2"><var id="_debx_dkqx">0</var> 月</td>
            </tr>
            <tr>
             <td class="td1">首月还款</td>
             <td class="td2"><em id="_debx_myhk">0</em> 元</td>
            </tr>
            <tr>
                <td class="td1">每月递减</td>
                <td class="td1"><em id="_debj_mydj">0</em> 元</td>
            </tr>
            <tr>
             <td class="td1">总支付利息</td>
             <td class="td2"><em id="_debx_zflx">0</em> 元</td>
            </tr>
            <tr>
             <td class="td1">本息合计</td>
             <td class="td2"><em id="_debx_hkze">1,039,024.42</em> 元</td>
            </tr>
           </tbody>
          </table>
          </td>
         <td style="position:relative">
          <div>
           <span style="float:left">逐月递减还款</span>
           <div  onclick=helpClick(this) class="circle" id="debjhk">?</div>
          </div>
          <div class="tsk" id="debjhk_help" style="width:214px;left:1px;top:16px">
              <div onclick="hidHelp()" class="help">&nbsp;X</div>
           <div class="nr">
             逐月递减还款即等额本金还款法,指本金保持相同,利息逐月递减,月还款数递减;由于每月的还款本金额固定,而利息越来越少,贷款人起初还款压力较大,但是随时间的推移每月还款数也越来越少。
           </div>
          </div>
          <table  style="clear:both" cellpadding="0" cellspacing="0" class="tbl">
           <tbody>
            <tr>
             <td class="td1" width=100px>贷款总额</td>
             <td class="td2"><var id="_debj_dkje">0</var> 元</td>
            </tr>
            <tr>
             <td class="td1">还款月数</td>
             <td class="td2"><var id="_debj_dkqx">0</var> 月</td>
            </tr>
            <tr>
             <td class="td1">首月还款</td>
             <td class="td2"><em id="_debj_syhk">0</em> 元
             </td>
            </tr>
            <tr>
                <td class="td1">每月递减</td>
                <td class="td1"><em id="_debj_mydj">0</em> 元</td>
            </tr>
            <tr>
             <td class="td1">总支付利息</td>
             <td class="td2"><em id="_debj_zflx">0</em> 元</td>
            </tr>
            <tr>
             <td class="td1">本息合计</td>
             <td class="td2"><em id="_debj_hkze">1,039,024.42</em> 元</td>
            </tr>
           </tbody>
          </table> </td>
        </tr>
        <tr>
         <td height="25" colspan="2">
             <span class="result-info">此结果仅供参考,实际应缴费以当地为准</span>
          </td>
        </tr>
       </tbody>
      </table>
        </td>
    </tr>

   </tbody>
  </table>

<script>
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 totalinterest = document.getElementById("totalinterest");

    // 假设所有的输入都是合法的,将从input元素中获取输入数据
    // 将百分比格式转换为小数格式,并从年利率转换为月利率
    // 将年度赔付转换为月度赔付
    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); // Math.pow()进行幂次运算
    var monthly = (principal*x*interest)/(x-1);

    // 如果结果没有超过JavaScript能表示的数字范围,且用户的输入也正确
    // 这里所展示的结果就是合法的
    if (isFinite(monthly)) {
        // 将数据填充至输出字段的位置,四舍五入到小数点后两位数字
        payment.innerHTML = monthly.toFixed(2);
        total.innerHTML = (monthly * payments).toFixed(2);
        totalinterest.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 = ""
        totalinterest.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>";
            }

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

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

    // 如果不传入参数,或者浏览器不支持画布,则直接返回
    if (arguments.length == 0 || !graph.getContext) return;

    // 获得画布元素的"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 = "chartreuse";                          // 使用绿色绘制图形
    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("Year", x, y-5);    // 在坐标轴做标记
        if (year % 5 == 0 && year*12 !== payments)    // 每5年的数据
        g.fillText(String(year), x, y-5);
    }

    // 将赔付数额标记在右边界
    g.textAlign = "right";                        // 文字右对齐
    g.textBaseline = "middle";                    // 文字垂直居中
    var ticks = [monthly*payments, principal];    // 我们将要用到的两个点
    var rightEdge = paymentToX(payments);         // 设置X坐标
    for(var i = 0; i < ticks.length; i++) {       // 对每两个点做循环
        var y = amountToY(ticks[i]);              // 计算每个标记的Y坐标
        g.fillRect(rightEdge-3, y-0.5, 3,1);      // 绘制标记
        g.fillText(String(ticks[i].toFixed(0)),   // 绘制文本
        rightEdge-5, y);
    }
}
</script>
</body>
</html>

版本二:

出处参考:

http://www.codefans.net/jscss/code/3613.shtml

http://www.rong360.com/calculator/fangdai.html

时间: 2024-10-13 07:30:42

纯JS实现房贷利率报表对比的相关文章

纯js实现html转pdf

项目开发中遇到了一个变态需求,需要把一整个页面导出为pdf格式,而且要保留页面上的所有的表格.svg图片和样式.简而言之,就是希望像截图一样,把整个页面截下来,然后保存成pdf.咋不上天呢--查了一下,能够实现html转pdf的方法还是挺多的,大概有以下几种:1.大部分浏览器就有这个功能.然而我们客户要的可不是这个,人家要的是能够在系统中主动触发的导出为pdf功能,所以这种方案pass.2.利用第三方工具.我找到了一种利用wkhtmltopdf这种工具来导出的方案,自己在我们的项目中试了一下,效

Highcharts纯js图表库,以后可以跟客户说,你跟阿里云ECS用的图表库是同款

Highcharts是一款纯javascript编写的图表库,能够很简便的在Web网站或Web应用中添加交互性的图表,Highcharts目前支持直线图.曲线图.面积图.柱状图.饼图.散点图等多达18种不同类型的图表,可以满足常用的Web图表需求 ! 近来维护我的阿里云服务器,进入后台偶然发现阿里云管理后台数据图表用的也是Highcharts,刚好正需要WEB端展示数据的东西,研究哈… Highcharts官网:http://www.highcharts.com Highcharts中文站:ht

纯js页面跳转整理

js方式的页面跳转1.window.location.href方式    <script language="javascript" type="text/javascript">           window.location.href="http://updn.cn";     </script>2.window.navigate方式跳转   <script language="javascript

纯js制作的弹球游戏

纯js的弹球游戏,撞壁自动返回,按钮放置暂停移动,移开开始移动 1 <!-- 2 author:zhangjie 3 date :2016-7-23 4 --> 5 <!DOCTYPE html> 6 <html> 7 <head> 8 <title></title> 9 <meta charset="UTF-8"> 10 <script type='text/javascript'> 11

[分享黑科技]纯js突破localstorage存储上线,远程抓取图片,并转码base64保存本地,最终实现整个网站所有静态资源离线到用户手机效果却不依赖浏览器的缓存机制,单页应用最新黑科技

好久没有写博客了,想到2年前答应要放出源代码的也没放出来,最近终于有空先把纯js实现无限空间大小的本地存储的功能开源了,项目地址https://github.com/xueduany/localstore,demo见http://xueduany.github.io/localstore/,下面给大家简单说说大概原理,具体细节和异常处理后面有机会在单独说 先说下突破本地localStorage的原理,官方原话是这么说的http://www.w3.org/TR/2013/PR-webstorage

Ajax,纯Js+Jquery

AJAX:Asynchronous Javascript and xml 异步,Js和Xml 交互式网页开发 不刷新页面,与服务器交互 详情请参照Jquery工具指南用在浏览器端的技术,无刷新,通过XmlHttpRequest访问页面纯js版---------- if(XmlHttpRequest){ //判断是否支持XmlHttpRequest xhr= new XmlHttpRequest(); // 创建XmlHttpRequest对象 } else{ xhr= new Activexob

【修改】纯JS省市区三级联动 支持js默认省市区

---恢复内容开始--- <!DOCTYPE html><html><head><title>修改,QQ JS省市区三级联动 -支持默认省市区</title><!-- 使用QQ的省市区数据 --><!--<script type="text/javascript" src="http://ip.qq.com/js/geo.js"></script>--><

纯js模拟 radio和checkbox控件

代码待优化,功能实现了,不兼容ie8以上, 相同name的radio可以实现切换的操作, 分享代码,共同学习进步 <!doctype html> <html> <head> <meta charset="utf-8"> <title></title> <style> .radiobox, .checkbox { width: 10px; height: 10px; padding: 2px; borde

纯JS实现省市区三级联动

<!DOCTYPE html><html><head>    <title>纯JS省市区联动</title>    <script type="text/javascript" src="jsAddress.js"></script></head><body><div> 省:<select id="cmbProvince"