最近朋友买房,想计算下自己的房贷的还款情况,自己正好周末没事,从网上找来点代码修改,也算是对自己技术的巩固吧。
目前这个还只是个初级版本,暂时可以在PC上正常访问,将来会一步一步的把相继功能都加上的,将要完成的功能:
- 可以在Android手机上访问的界面对齐、美化等。
- 图表的优化。
- 等额本金法和等额本息法的实现。
以上功能暂列这么多,以后有想到的在慢慢往上加;也希望大家多多的提出你的意见。
以后会把每次都更新版本都添加到文章的后面,方便历史版本的追溯。样图:
版本一:
<!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="计 算" /> <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"> 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"> 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