【阅读】javascript实现计算器例子《javascript权威指南》(第六版)第17页至20页

以下是我找着书打出来的,没有检查,里面包含了我的一些问题,等会我会把解答也写出来

问题会在那里加上5个#号

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>JavaScript Loan Calculator</title>
<style>
.output {
	font-weight:bold;
}
#payment {
	text-decoration:underline;
}
#graph {
	border: solid black 1px;
}
th, td {
	vertical-align:top;
}
</style>
</head>

<body>
<!--
	这是一个HTML表格,其中包含了<input>元素可以用来输入数据
    程序将在<span>元素中显示计算结果,这些元素都具有类似"interest"和"years"的id
    这些id将在表格下面的javascript代码中用到。我们注意到,有一些
    input元素定义了"onchange"或"onclick"事件的处理程序,以便用户在输入数据或者点击inputs时
    执行指定的javascript代码段
-->
<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(yeaers):</td>
        <td><input id="years" onChange="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>
        <!--######问题二:像这样的id声明在它的CSS中有没有对这个id的代码,要来有什么用? -->
     </tr>
</table>
<!--
	随后是javascript代码,这些代码内嵌到一个<script>标签里
    通常情况下,这些脚本代码应该放在<head>标签中
    将javascript代码放在HTML代码之后仅仅是为了方便理解
-->
<script>
"user strict"
/*
 这里的脚本定义了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(amout.value);
	var interest = parseFloat(apr.value) / 100 / 12;
	var payments = parseFloat(years.value) * 12;
	
	//现在计算月度赔付的数据
	var x = Math.pow(i + interest, paymnets);//进行幂次运算
	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" + //使用查询串中的数据
		"?amt=" + encodeURLComponent(amount) + 
		//######问题三:这个amt前面的是问号,而其他的都是&号。。这是为什么呢?
		"&apr=" + encodeURLComponent(apr) + 
		"&yrs=" + encodeURLComponent(years) + 
		"&zip=" + encodeURLComponent(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) {
			//######问题四:4和200这两个数字是怎么得来的
			//如果代码执行到这里,说明我们得到一个合法且完整的HTTP响应
			var response = req.responseText;	//响应是以字符串的形式呈现的
			var lenders = JSON.parse(response);	//将其解析为JS数组
			
			//将数组中的放贷人对象转换成HTML字符床的形式
			var list = "";
			for(var i = 0; i < lenders.length; i++) {
				list += "<li><a href='" + lendgers[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");
	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("Total Interest Payments", 20, 20);	//将文字绘制到图例中
	
	//很多资产数据不是线性的,很难将其反应到图表中
	var equity = 0;
	g.begiPath();		//开始绘制新图形
	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("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 - thisMonthsInterest);		//得到资产值
		g.lineTo(paymentToX(p), amountToY(bal));	//将直线连接到某个点
		//问题七:这个lineTo又是什么时候定义的?
	}
	g.lineWidth = 3;								//将直线的宽度加粗
	g.stroke();										//绘制余额的曲线
	g.fillStyle = "black";							//使用黑色字体
	g.fillText("Loan Balance", 20, 50);				//图例文字	
	
	//将年度数据在C轴做标记
	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)			//开始绘制标记
		//问题八:又是没有声明这个fillRect就用了
		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";						//文字垂直居中
	//问题九:这个texBaseline是什么,CSS中的吗?
	var ticks = [monthly * payments, principal];	//我们将要用刀的两个点
	var rightEdge = paymentToX(payments);			//设置X坐标
	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>
</body>
</html>


解答:

  1. 问:canvas高度宽度为什么能能够不带单位    答:W3CSchool的链接附上点击打开链接,它默认的单位就是像素(PX),当然你也可以加上px,显示效果一样
  2. 问:HTML中的有些元素的id声明在它的CSS中有没有对这个id的代码,要来有什么用    答:不一定要所有的id都是用在CSS中的哦,像javascript也是可以使用的,这个例子中的calculate()方法中的var amount = document.getElementById("amount");就用到了它的id来获取数据
  3. 问:这个amt前面的是问号,而其他的都是&号。。这是为什么呢    答:那个问号是用来说明:哎~url,我后面的都是参数哈,你别弄错了(实际上那部分代码并没有执行)
  4. 问:直接把自己的值赋给自己,感觉结果还是他自己    答:嗯,你说的没有错,还是他自己,width的值有没有变。。。再问:那你为什么说能够清除画布呢?   再答:重新赋值就可以叫做重置了,这个是一个前辈告诉我的,虽然我也不知道他在说什么
  5. 至于那个把付款数字和美元转换成像素的东西,嗯~也许是一个公式吧,不要在意这些细节
  6. 连接至开都其实就是将当前点和子路径起始点连接起来,以达到封闭图形的效果点击打开链接,对了这个方法是早就有了的
  7. 还有问题七。。。lineTo其实也造就有了的。点击打开链接
  8. 问题八也是一样的fillRect也是早就有了的方法,用来填充一个矩形点击打开链接
  9. 至于==!它的意思实际上是不恒等(或者说严格不相等),==, 两边值类型不同的时候,要先进行类型转换,再比较。 
    ==,不做类型转换,类型不同的一定不等(摘自点击打开链接



以下是最终修改运行成功的网页

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>JavaScript Loan Calculator</title>
<style>
.output {
	font-weight:bold;
}
#payment {
	text-decoration:underline;
}
#graph {
	border: solid black 1px;
}
th, td {
	vertical-align:top;
}
</style>
</head>

<body>
<!--
	这是一个HTML表格,其中包含了<input>元素可以用来输入数据
    程序将在<span>元素中显示计算结果,这些元素都具有类似"interest"和"years"的id
    这些id将在表格下面的javascript代码中用到。我们注意到,有一些
    input元素定义了"onchange"或"onclick"事件的处理程序,以便用户在输入数据或者点击inputs时
    执行指定的javascript代码段
-->
<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="400px" height="250px"></canvas>
            <!--######问题一:这个高度宽度为什么能能够不带单位 -->
        </td>
     </tr>
     <tr>
     	<td>Annual interest(%)</td>
        <td><input id="apr" onChange="calculate();"></td>
     </tr>
     <tr>
     	<td>Repayment period(yeaers):</td>
        <td><input id="years" onChange="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>
        <!--######问题二:像这样的id声明在它的CSS中有没有对这个id的代码,要来有什么用? -->
     </tr>
</table>
<!--
	随后是javascript代码,这些代码内嵌到一个<script>标签里
    通常情况下,这些脚本代码应该放在<head>标签中
    将javascript代码放在HTML代码之后仅仅是为了方便理解
-->
<script>
"user strict"
/*
 这里的脚本定义了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);//进行幂次运算
	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" + //使用查询串中的数据
		"?amt=" + encodeURLComponent(amount) + 
		//######问题三:这个amt前面的是问号,而其他的都是&号。。这是为什么呢?
		"&apr=" + encodeURLComponent(apr) + 
		"&yrs=" + encodeURLComponent(years) + 
		"&zip=" + encodeURLComponent(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) {
			//######问题四:4和200这两个数字是怎么得来的
			//如果代码执行到这里,说明我们得到一个合法且完整的HTTP响应
			var response = req.responseText;	//响应是以字符串的形式呈现的
			var lenders = JSON.parse(response);	//将其解析为JS数组
			
			//将数组中的放贷人对象转换成HTML字符床的形式
			var list = "";
			for(var i = 0; i < lenders.length; i++) {
				list += "<li><a href='" + lendgers[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");
	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("Total Interest Payments", 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("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 - thisMonthsInterest);		//得到资产值
		g.lineTo(paymentToX(p), amountToY(bal));	//将直线连接到某个点
		//问题七:这个lineTo又是什么时候定义的?
	}
	g.lineWidth = 3;								//将直线的宽度加粗
	g.stroke();										//绘制余额的曲线
	g.fillStyle = "black";							//使用黑色字体
	g.fillText("Loan Balance", 20, 50);				//图例文字	
	
	//将年度数据在C轴做标记
	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);			//开始绘制标记
		//问题八:又是没有声明这个fillRect就用了
		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";						//文字垂直居中
	//问题九:这个texBaseline是什么,CSS中的吗?
	var ticks = [monthly * payments, principal];	//我们将要用刀的两个点
	var rightEdge = paymentToX(payments);			//设置X坐标
	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>
</body>
</html>


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值