基于AngularJS实现iOS8自带的计算器

前言

首先创建angularjs的基本项目就不说了,最好是利用yeoman这个脚手架工具直接生成,如果没有该环境的,当然也可以通过自行下载angularjs的文件引入项目。

实例详解

main.js是项目的主要js文件,所有的js都写在这个文件中,初始化之后,该文件的js代码如下

angular
 .module('calculatorApp', [
 'ngAnimate',
 'ngCookies',
 'ngResource',
 'ngRoute',
 'ngSanitize',
 'ngTouch'
 ])
 .controller('MainCtrl', function ($scope) {
 $scope.result="";
 $scope.data={
  "1":["AC","+/-","%","÷"],
  "2":["7","8","9","×"],
  "3":["4","5","6","-"],
  "4":["1","2","3","+"],
  "5":["0",".","="]
 };
 });

这里的result是用来双向绑定显示运算结果的,data为计算器键盘上的数字和符号。

该项目相关的所有css代码如下:

*{
 margin:0;
 padding:0;
}

body {
 padding-top: 20px;
 padding-bottom: 20px;
}
h1{
 text-align:center;
 color:#3385ff;
}
.main{
 margin:20px auto;
 border:1px solid #202020;
 border-bottom: none;
 width:60%;
 height:600px;
}

.result{
 display: block;
 width: 100%;
 height: 30%;
 background:#202020;
 box-sizing: border-box;
 border:none;
 padding: 0;
 margin: 0;
 resize: none;
 color: #fff;
 font-size: 80px;
 text-align: right;
 line-height: 270px;
 overflow: hidden;
 background-clip: border-box;

}
.row{
 height: 14%;
 background: #d7d8da;
 box-sizing: border-box;
 border-bottom: 1px solid #202020;
 overflow: hidden;
}

.col{
 height: 100%;
 box-sizing: border-box;
 border-right:1px solid #202020;
 float: left;
 color: #202020;
 font-size: 28px;
 text-align: center;
 line-height: 83px;
}
.normal{
 width: 25%;
}
.end-no{
 width: 25%;
 border-right: none;
 background: #f78e11;
 color: #fff;

}
.zero{
width: 50%;
}
.history{
 background:#3385ff ;
 color:#fff;
 font-size: 22px;
 text-align: center;
}

然后是html的布局如下:

<body ng-app="calculatorApp" >
<h1>calculator for ios8</h1>
  <hr/>
  <p class="history">{{ history.join(" ") }}</p>
<div class="main">
  <textarea ng-model="result" class="result" ></textarea>
 <div ng-repeat="item in data" class="row">
  <div class="col" ng-repeat="a in item" ng-class="showClass($index,a)" ng-click="showResult(a)">{{ a }}</div>
 </div>
</div>
</body>

这里class为history的p标签是用来显示输入记录的,就是说你按下的所有键都会显示在上面,便于查看结果,history为当前scope下面的一个数组,后面会讲解。这里使用一个textarea来作为计算结果的显示屏幕,主要是为了使用双向绑定的特性。同时生成计算器各个按键和界面元素都是通过对data对象进行 循环遍历来生成的,showClass方法是scope下面的一个方法,用来获取不规则界面显示元素的class属性,后面会讲解,showResult方法就是对按键响应的主方法,我们所有对按键的按下响应都是通过这个方法来的,后面会详细讲解。

showClass方法代码如下:

  //显示计算器样式
 $scope.showClass=function(index,a){
  if(a==0){
   return "zero";
  }
  return index==3||a=="="?"end-no":"normal";
 };

这个方法主要是针对每行的最后一列要显示为橘黄色和对于显示0的按键要占用两个单元格来进行特殊处理。

到目前为止,已经完全实现了计算器的界面

效果图如下:

下面需要实现对按键的响应,按键包括数字键,运算符键,AC键,每种按键按下都会有不同相应并且按键之间是存在联系的

为了使代码容易讲解,采用分段性给出showResult方法的代码然后进行详细解释的方法。

首先,这里要添加几个变量进行控制和存储之用。

 //计算时用的数字的栈
 $scope.num=[];
 $scope.history=[];
  //接受输入用的运算符栈
 $scope.opt=[];
  //计算器计算结果
 $scope.result="";
 //表示是否要重新开始显示,为true表示不重新显示,false表示要清空当前输出重新显示数字
 $scope.flag=true;
 //表示当前是否可以再输入运算符,如果可以为true,否则为false
 $scope.isOpt=true;

num数组实际上是一个栈,用来接收用户输入的数字,具体用法后面会讲解,history数组为用户输入的所有按键,每次按下就让该按键上的符号或数字进栈,然后使用绑定实时显示在界面上。opt数组是另外一个栈,用来接收用户输入的运算符。具体用法后面会讲解,flag是一个标志,为true的时候表示在按下数字的过程中被按下的数字是当前显示数字的一部分,需要跟在其后面显示,比如当前界面显示的是12,再按下3的时候会判断该标志,如果为true,就显示123,否则就清空界面,直接显示3.isOpt是另外一个标志,主要是为了防止用户在输入过程中对运算符的非法输入,比如说用户接连输入了1+2+,当输到这里是,下面输入的应该是一个数字,但是用户却输入了一个运算符,通过判断这个标志,会让计算器忽略这个非法的运算符,让输入依然保持1+2+。

下面的代码分段给出,完整的代码就是将它们连接起来。

$scope.init=function(){
  $scope.num=[];
  $scope.opt=[];
  $scope.history=[];
  $scope.flag = true;
  $scope.isOpt=true;

 } ;
 $scope.showResult=function(a){
  $scope.history.push(a);
  var reg=/\d/ig,regDot=/\./ig,regAbs=/\//ig;
  //如果点击的是个数字
  if(reg.test(a)) {
   //消除冻结
   if($scope.isOpt==false){
    $scope.isOpt=true;
   }

   if ($scope.result != 0 && $scope.flag && $scope.result != "error") {
    $scope.result += a;
   }
   else {
    $scope.result = a;
    $scope.flag = true;
   }

  }

init方法是用来初始化一些变量和标志,让它们回到原始状态。showResult方法是显示界面响应用户操作的主方法,上面的代码是该方法中的一个if分支,表示如果输入的是一个数字,那么如果对运算符的输入已经被冻结(当前不允许输入运算符了,输入后会被忽略),那么输入数字的时候,就解开冻结状态,以便下次输入运算符的时候会进入运算符栈。如果当前显示的结果不为空并且现在按下的数字是当前显示的数字的一部分并且没有发生错误,那么显示的结果就是当前按下的数字接在当前显示数字的末尾,否则就代表重新显示,重新显示的时候需要让下次再输入的数字接在这个数字后面显示。

js代码(接上)

 //如果点击的是AC
  else if(a=="AC"){
   $scope.result=0;
   $scope.init();
  }

如果点击的是AC,那么代表初始化,让显示结果为0,清空所有状态。

js代码(接上)

 //如果点击的是个小数点
  else if(a=="."){
   if($scope.result!=""&&!regDot.test($scope.result)){
    $scope.result+=a;
   }
  }

如果点击的是个小数点,则在当前显示不为空并且当前显示的结果里面不存在小数点的情况下让这个小数点接在当前显示的末尾。

js代码(接上)

 //如果点击的是个取反操作符
  else if(regAbs.test(a)){
   if($scope.result>0){
    $scope.result="-"+$scope.result;
   }
   else{
    $scope.result=Math.abs($scope.result);
   }
  }

如果点击的是个取反操作,则将当前显示结果取反

js代码(接上)

 //如果点击的是个百分号
  else if(a=="%"){
   $scope.result=$scope.format(Number($scope.result)/100);

  }

如果点击的是个百分号,则将当前显示结果除以100之后再显示,这里有个format函数

其代码如下:

 //格式化result输出
  $scope.format=function(num){
  var regNum=/.{10,}/ig;
   if(regNum.test(num)){
    if(/\./.test(num)){
     return num.toExponential(3);
    }
    else{
     return num.toExponential();
    }
   }
   else{
    return num;
   }
  }

它的作用主要是ios8自带的计算器不会无限显示很多位的数字,如果超过10位(包括小数点),则采用科学计算法来显示,这里为了简便,对于含有小数点且超过10位的显示结果采用科学计算法计算的时候,让它保留小数点之后3位显示。

js代码(showResult部分接上)

 //如果点击的是个运算符且当前显示结果不为空和error
  else if($scope.checkOperator(a)&&$scope.result!=""&&$scope.result!="error"&&$scope.isOpt){
   $scope.flag=false;
   $scope.num.push($scope.result);
   $scope.operation(a);
   //点击一次运算符之后需要将再次点击运算符的情况忽略掉
   $scope.isOpt=false;
  }

这个分支是最复杂的一个分支,它代表如果输入的是一个运算符,那么就要进行运算了。进入到这个分支,需要首先将flag置为false,作用是下次再输入数字就是重新输入数字而不是接着当前显示结果输入了。

然后要让当前显示的数字作为被运算的数字首先进入到数字栈中,operation方法就是运算方法,因为这次已经点击了一个运算符,所以下次再点击就要忽略这个运算符,将isOpt置为false。

operation代码如下

  //比较当前输入的运算符和运算符栈栈顶运算符的优先级
  //如果栈顶运算符优先级小,则将当前运算符进栈,并且不计算,
  //否则栈顶运算符出栈,且数字栈连续出栈两个元素,进行计算
  //然后将当前运算符进栈。
  $scope.operation=function(current){
   //如果运算符栈为空,直接将当前运算符入栈
   if(!$scope.opt.length){
    $scope.opt.push(current);
    return;
   }
   var operator,right,left;
   var lastOpt=$scope.opt[$scope.opt.length-1];
   //如果当前运算符优先级大于last运算符,仅进栈
   if($scope.isPri(current,lastOpt)){
    $scope.opt.push(current);
   }
   else{
     operator=$scope.opt.pop();
     right=$scope.num.pop();
     left=$scope.num.pop();
     $scope.calculate(left,operator,right);
     $scope.operation(current);
    }
  };

该方法接受当前输入的运算符作为参数,其核心思想为,当前接收到了一个运算符,如果运算符栈为空,则将当前运算符入栈,然后这种情况就不用再做什么了。如果当前运算符栈不为空,那么弹出当前运算符栈的栈顶元素,让当前接收的运算符和栈顶运算符比较优先级(乘除优先级大于加减,同一优先级的情况下栈顶运算符优先级较高,因为先入栈的)。isPri方法用来判断优先级的,接收两个参数,第一个为当前接收的运算符,第二个为出栈的栈顶运算符,如果按照前面所说的规则,当前运算符的优先级较高,那么就直接将这个运算符入栈。如果当前运算符优先级小于栈顶运算符,那么就需要进行计算并更改计算器的显示了,将运算数字栈栈顶两个元素依次弹出,分别作为一次运算的两个运算数字,然后弹出运算符栈的栈顶元素,作为本次运算的运算符,调用calculate方法进行运算

该方法代码如下

 //负责计算结果函数
  $scope.calculate=function(left,operator,right) {
   switch (operator) {
    case "+":
     $scope.result = $scope.format(Number(left) + Number(right));
     $scope.num.push($scope.result);
     break;
    case "-":
     $scope.result = $scope.format(Number(left) - Number(right));
     $scope.num.push($scope.result);
     break;
    case "×":
     $scope.result = $scope.format(Number(left) * Number(right));
     $scope.num.push($scope.result);
     break;
    case "÷":
     if(right==0){
      $scope.result="error";
      $scope.init();
     }
     else{
      $scope.result = $scope.format(Number(left) / Number(right));
      $scope.num.push($scope.result);
     }
     break;
    default:break;
   }
  };

该方法接受三个参数,左运算数字,中间的运算符和右边的运算数字,按照加减乘除法运算后更改result显示结果并将计算结果入栈到运算数字栈中,这里需要注意如果运算的是除法并且除数是0,则发生了错误,显示错误,清空所有状态,否则正常运算。

一次运算完成之后,运算符栈和数字栈中的状态都会被更改,而目前的按键current值还没有入栈,所以又要重复上述过程进行优先级比较后在运算,实际上是一个递归的过程,直到运算符栈为空或者当前运算符的优先级高于运算符栈的栈顶运算符。isPri方法是用来判断运算符优先级的

代码如下:

 //判断当前运算符是否优先级高于last,如果是返回true
  //否则返回false
  $scope.isPri=function(current,last){
   if(current==last){
    return false;
   }
   else {
    if(current=="×"||current=="÷"){
     if(last=="×"||last=="÷"){
      return false;
     }
     else{
      return true;
     }
    }
    else{
     return false;
    }
   }
  };

判断规则前面已经讲述。

此外还有一个checkOperator方法,是判断输入的符号是不是加减乘除四则运算符号

代码如下:

 //判断当前符号是否是可运算符号
  $scope.checkOperator=function(opt){
   if(opt=="+"||opt=="-"||opt=="×"||opt=="÷"){
    return true;
   }
   return false;
  }

如果是就返回true,否则返回false。

到目前为止,还有一个输入等于号的分支没有

其代码如下(接showResult方法)

 //如果点击的是等于号
  else if(a=="="&&$scope.result!=""&&$scope.result!="error"){
   $scope.flag=false;
   $scope.num.push($scope.result);
   while($scope.opt.length!=0){
    var operator=$scope.opt.pop();
    var right=$scope.num.pop();
    var left=$scope.num.pop();
    $scope.calculate(left,operator,right);
   }
  }
 };

如果输入的是等于号,则首先将flag置为false,允许下次输入数字的时候界面重新显示,并且要将当前显示的数字作为运算数字入栈到数字栈。然后就要进行不断的出栈运算直到运算符栈为空才能够停止。

总结

上面就是实现的主要代码和过程,由于分支代码较多而一次全部给出所有分支又不能够详细讲述,所以将showResult方法分开了,可能看着不太适应。由于写的比较仓促且没有花太多时间去测试,可能存在一些bug,欢迎指出。同时由于水平有限,可能该方法不是最好,欢迎给出更好的方案一起交流学习~~以上就是这篇文章的全部内容了,希望对的大家的学习或者工作带来一定的帮助。

(0)

相关推荐

  • AngularJs+Bootstrap实现漂亮的计算器

    之前看到一个试题,要求用angularJs和Bootstrap写一个简单的计算器,通过百度,发现没有什么好的例子,所以呢,我就把自己写的一个例子发出来给大家.(大牛勿喷) HTML代码: <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <me

  • 基于AngularJS实现的工资计算器实例

    本文实例讲述了基于AngularJS实现的工资计算器.分享给大家供大家参考,具体如下: 先看界面: 其实在ng中最让人印象深刻的就是数据的双向绑定,在html中就完成了很多操作.大概用到的就是控制器视图服务等,没有分模块写控制器,代码如下: <html ng-app = "myApp"> <head> <title>工资计算器ng</title> <script src= "angular.js"><

  • 基于AngularJS实现iOS8自带的计算器

    前言 首先创建angularjs的基本项目就不说了,最好是利用yeoman这个脚手架工具直接生成,如果没有该环境的,当然也可以通过自行下载angularjs的文件引入项目. 实例详解 main.js是项目的主要js文件,所有的js都写在这个文件中,初始化之后,该文件的js代码如下 angular .module('calculatorApp', [ 'ngAnimate', 'ngCookies', 'ngResource', 'ngRoute', 'ngSanitize', 'ngTouch'

  • 基于AngularJs + Bootstrap + AngularStrap相结合实现省市区联动代码

    Angular JS (Angular.JS) 是一组用来开发Web页面的框架.模板以及数据绑定和丰富UI组件.它支持整个开发进程,提供web应用的架构,无需进行手工DOM操作. AngularJs 就是一个函数库,算不上一个框架,源码2万2千多行,提供了前端MVC的开发方式,有双向绑定,指令等特性,这是具有革命性的.我是多么反感jQuery 用选择器 选择元素 ,绑定事件,进行一大堆DOM操作,一旦代码过多,非常不好维护,html结构改变,又要重写js代码,不过 jQuery  对 ajax的

  • 基于angularJS的表单验证指令介绍

    做开发这么久一直被表单验证这个看似简单又不简单的东西搞得非常烦躁,于是写了个表单验证的框架,再也不用担心这个心烦的验证了. 这里使用AngularJS的指令进行处理代码及其简洁明了 下面是指令JS代码 app.directive('ccForm',['$parse',function ($parse) { return { restrict:'A', link:function (scope,element,attrs) { var first=true; var errors=0; var c

  • 基于AngularJS的简单使用详解

    Angular Js 的初步认识和使用 一: 1.模块化 定义模块和控制器 ng-app="myapp" controller="myctrl" 指定模型 ng-model="" 获取的属性值: ng-bind="属性名"或者{{属性名}} 2.初始化模块(在Script中进行) var myapp1 =angular.module("myapp",[]); 3.定义模块的控制器,并依赖注入, $scope

  • 基于AngularJS+HTML+Groovy实现登录功能

    AngularJS是开发基于浏览器的响应式RWD应用程序的一个前端MVC框架,由谷歌最初开发的 开源项目,干净的架构吸引了大量粉丝,适合建立CRUD类型的业务应用程序,并不适合开发游戏等应用, 使用声明性编程的用户界面和命令式编程的逻辑, 支持现代桌面和移动浏览器 Internet Explorer版本8.0及以上. AngularJS是一款客户端MVC的javascript框架,而客户端MVC代表未来架构(为什么要使用MVC+REST+CQRS 架构),如果你有Struts或SpringMVC

  • 基于AngularJS的拖拽文件上传的实例代码

    随着HTML5的普及,现在大部分浏览器都支持拖拽功能,今天我们要说的就是实现一套拖拽上传的功能(Angularjs+nodejs). 一.首先前端这款插件是基于AngularJS的,下面我们来看主要代码. 引入js: <script src="js/angular.1.3.15.min.js"></script> <script src="js/ng-file-upload-shim.min.js"></script>

  • 基于Angularjs实现分页功能

    前言 学习任何一门语言前肯定是有业务需求来驱动你去学习它,当然ng也不例外,在学习ng前我第一个想做的demo就是基于ng实现分页,除去基本的计算思路外就是使用指令封装成一个插件,在需要分页的列表页面内直接引用. 插件 在封装分页插件时我实现了几种方式总体都比较零散,最后找到了一个朋友(http://www.miaoyueyue.com/archives/813.html)封装的插件,觉还不错,读了下他的源码就直接在项目中使用了. 原理和使用说明 1.插件源码主要基于angular direct

  • 基于BootStrap的前端分页带省略号和上下页效果

    bootstrap前端分页 自带效果. 首先是百度下获得资源 http://blog.csdn.net/u013025627/article/details/50485327 其实15年的时候我师兄给过我一个文档不知道在哪儿搞得,我靠那是示例之多.现在嘛只有找代码片段自己写 好了好了这个东西也就是没有上一页 下一页的.于是我加了而且修改了下源代码.扯淡的是我不能用bootstrap的效果,为什么?因为我们有自己的样式,so我得有个下过自己写 首先看看源代码我修改注释的部分 这个now是UI做的效

  • 基于fileUpload文件上传带进度条效果的实例(必看)

    文件上传过程中,如果我们能看到进度条会更好,实现思路是服务器端用监听器实时监听进度并存入session,客户端异步请求服务器端获得上传进度,并进行效果渲染. 效果图: 服务器端servlet: public class UploadServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException

随机推荐