<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS彈框</title>
<style type="text/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>
</tr>
<tr>
<td>輸入余額</td>
<td><input id="amount" onchange="calculate()" ></td>
<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>
<td>計算匯率:</td>
<td><button onclick="calculate()" >計算</button></td>
</tr>
<tr>
<td>月利:</td>
<td>¥<span class="output" id="payment"></span></td>
</tr>
<tr>
<td>總利:</td>
<td>¥<span class="output" id="total"></span></td>
</tr>
<tr>
<td>total interst:</td>
<td>¥<span class="output" id="totalinterest"></span></td>
</tr>
<tr>
<th>Sponsors:</th>
<td colspan="2">生成你的利率:
<div id="lenders"></div>
</td>
</tr>
</table>
<script type="text/javascript">
"use strict";//如果瀏覽器支持的話,則開啟ECMAScript5 的嚴格模式
/**
* 這里的腳本定義了caculate()函數,在HTML代碼只能怪綁定時間處理程序時會調用它
* 這個函數從<input>元素中讀取數據,計算貸款賠付信息,並將結果顯示在<span>元素中
* 同樣,這里還保存了用戶數據、展示了房貸人鏈接並繪制了圖表
*/
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)
returmn;
var ad= document.getElementById('lenders');
if(!ad)
return ; //如果返回空,則退出
//將用戶的輸入數據進行URL編碼,並作為查詢參數附加在URL中
var url = "getLenders.php"+ //處理數據的URL地址
"?amt="+encodenURIComponent(amount)+ //使用查詢串中的數據
'&apr='+encodenURIComponent(apr)+
'&yrs='+encodenURIComponent(years)+
'&zip='+encodenURIComponent(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;
var height = graph.height; //獲得畫布大小
//這里的函數作用是將付款的數字和美元數據轉換為像素
function paymentToX(n){
return n*width/payments;
}
function amountToY(a){
return height-(a*height/(monthly*payments*1.05));
}
console.log(payments);
//付款數據是一條從(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("Total interest Payments",20,20);//將文字繪制到圖例中
//很多資產數據並不是線性的,很難將其反映至圖表中
var equity = 0;
g.beginPath(); //開始繪制新圖形
g.moveTo(paymentToX(0),amountToY(0));//從左下方開始
for(var p=1; p<=payments; p++){
//計算出每一筆賠付利息
var thisMontsInterest = (principal-equity)*interest;
equity+=(monthly-thisMontsInterest); //得到資產額
g.lineTo(paymentToX(p),amountToY(equity)); //將數據繪制到畫布上
}
g.lineTo(paymentToX(payments),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<payments; p++){
var thisMonthsInterest = bal*interest;
bal -= (monthly-thisMontsInterest); //得到資產額
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); //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)
g.fillText(String(year),x,y-5); //每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>