用 HVML 写一个计算器
本文用一个实际的例子描写了如何使用 HVML 一步步编写一个简单的计算器。
Web 版本
我们以 CSDN 上一个 Web 版计算器示例作为开头,以方便理解需求。
这一 Web 版本的计算器,其运行原理如下:
维护一个用户输入的运算表达式字符串,如
50 * 3 - 20;当用户点击
=按钮时,使用 JavaScript 的内置eval函数来对运算表达式求值。
该示例在单个 HTML 文件中同时包含了 CSS 和 JavaScript 脚本代码,我们将其复制到本文中,以方便读者阅读,希望原作者不要告侵权。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>计算器</title>
<style type="text/css">
body{
padding: 0;margin: 0;
background-color:#49C593 ;
}
#calculator{
position: absolute;
width: 1200px;height: 620px;
left: 50%;top: 50%;
margin-left: -600px;
margin-top: -310px;
}
#calculator #c_title {
margin: auto;
/*margin-left: 300px;*/
width: 800px;
height: 80px;
}
#calculator #c_title h2{
text-align: center;
font-size: 33px;font-family: "微软雅黑";color: #666;
line-height: 30px;
}
#c_text{
width: 1000px;
margin: auto;
}
#calculator #c_text #text{
/*margin-left: 200px;*/
padding-right: 10px;
width: 1000px;
height: 50px;
font-size: 25px;font-family: "微软雅黑";color: #666666;
text-align: right;border: 1px white;
border: double 1px;
}
#calculator #c_value{
width: 1080px;height: 408px;
/*margin-left: 160px;*/
margin: 20px auto;
}
#calculator #c_value ul{
margin: 0;
}
#calculator #c_value ul li{
margin: 10px;
list-style: none;float: left;
width: 180px;height: 80px;line-height: 80px;
text-align: center;background-color: chartreuse;
border: 1px solid black;
font-size: 30px;font-family: "微软雅黑";color: #666;
box-shadow: 5px 5px 30px rgba(0,0,0,0.4);
-webkit-user-select: none;
-ms-user-select: none;
-moz-user-select: none;
}
#calculator #c_value ul li:active{
background-color: white;
}
#calculator #c_value ul li:hover{
opacity:0.8;
cursor:pointer;
}
#calculator #c_value ul .c_blue{
background-color: cornflowerblue;color: #000000;
}
#calculator #c_value ul .c_yellow{
background-color: #f9f900;color: #000000;
}
</style>
<script type="text/javascript">
var IsClear = false;
var cal = "";
function get(key){
var str = document.getElementById("text").value;
if(IsClear){
str = "0";
IsClear = false;
}
if(str.length < 20){
str = (str == "0" ? "" : str);
if(str == "" && key == '00'){
str = "0";
}else{
str += key;
}
}
document.getElementById("text").value = str;
}
function goBack(){
var str = document.getElementById("text").value;
str = str.subSTR.0,str.length-1);
if(str=="") str="0";
document.getElementById("text").value = str;
}
function clearText(){
document.getElementById("text").value = "0";
}
function eq(){
IsClear = true;
var str = document.getElementById("text").value;
var result = eval(str)
if(result == "Infinity"){
result = "输入有误";
}
document.getElementById("text").value = result;
}
</script>
</head>
<body>
<div id="calculator">
<div id="c_title"><h2>计算器</h2></div>
<div id="c_text">
<input type="text" id="text" value="0" readonly="readonly" />
</div>
<div id="c_value">
<ul>
<li onclick="get(7)">7</li>
<li onclick="get(8)">8</li>
<li onclick="get(9)">9</li>
<li onclick="goBack()" class="c_blue">←</li>
<li onclick="clearText()" class="c_blue">C</li>
<li onclick="get(4)">4</li>
<li onclick="get(5)">5</li>
<li onclick="get(6)">6</li>
<li onclick="get('*')" class="c_blue">×</li>
<li onclick="get('/')" class="c_blue">÷</li>
<li onclick="get(1)">1</li>
<li onclick="get(2)">2</li>
<li onclick="get(3)">3</li>
<li onclick="get('+')" class="c_blue">+</li>
<li onclick="get('-')" class="c_blue">-</li>
<li onclick="get(0)">0</li>
<li onclick="get('00')">00</li>
<li onclick="get('.')">.</li>
<li onclick="get('%')" class="c_blue">%</li>
<li onclick="eq()" class="c_yellow">=</li>
</ul>
</div>
</div>
</body>
</html>该页面被渲染后的效果如下图所示:

一些说明
上面的 HTML 代码,编写得算不上特别优美,比如大量使用了 id 属性,而其实使用 class 属性更为合适。
我们不打算仔细改造这些细节之处,所以最终的 HVML 程序中,会保留在头部声明的 style 标签内容,但由于内容过多,我们会省略其中的样式信息。
数据驱动生成 HTML 内容
首先,我们看到原始 HTML 中有 12 个用来输入数字的按钮,以及八个用于表示四则运算、删除字符、清零的功能按钮。我们可以使用 HVML 的 iterate 动作标签完成这些重复性的 HTML 内容的生成。为此,我们首先准备一个全局的数据:
我们在全局的 buttons 这个 JSON 对象数组上执行迭代,即可生成所有的按钮。注意,为方便其后的编程,我们为不同种类的按钮新增了一个用于功能的类名,如 number、plus 等。
对应的 HVML body 标签内容如下:
注意,在上述代码中,除了使用了 HVML iterate 动作标签之外,我们还使用了 archetype 标签,用于定义一个 HTML 片段模板。
处理表达式输入
随着用户点击按钮,解释器输出框中的内容将随之改变。为此,我们设定一个全局的字符串变量,用于保存计算器输出框中的字符串,当用户点击按钮时,该字符串的值将发生变化,而计算器输出框中显示的内容也将相应发生变化。
这一改变涉及两处:
在头部新增一个全局的
myResult变量。监听具有类名
.btn元素之click事件,更新表达式并重置输入框的文本内容。
如下所示:
处理其他按钮事件
接下来,我们使用 HVML 的 observe 标签处理其他按钮上的事件。其中清除(C)按钮是最容易处理的:
回退(backspace)按钮的处理,要稍微麻烦一些。对字符串操作,我们通常使用由 HVML 解释器实现的内置动态对象(如 $STR),通过调用该对象提供的动态方法 substr 实现:
对 = 按钮的处理,我们直接使用预定义变量 $MATH 提供的 eval 方法:
完整的计算器程序源代码
见下面的代码清单。
注:为节省篇幅,我们把 CSS 部分保存到了单独的外部文件中。
Last updated