微信小程序学习之路04-简易的计算器

今天继续玩一些微信小程序的api来做例子,感觉自己可能创造力不很足,只能模仿着别人的例子来做一个自己的计算器了。老规矩,github源码地址我会附在文章末尾,供大家参考。用微信开发者工具新建的项目,index.wxss我也不去改了。只在index.wxml界面加一个到计算器页面的入口。index.js里面加入一个参数.
下面是index.wxml

<view class="container">
  <view  bindtap="bindViewTap" class="userinfo">
    <image class="userinfo-avatar" src="{{userInfo.avatarUrl}}" background-size="cover"></image>
    <text class="userinfo-nickname">{{userInfo.nickName}}</text>
  </view>
  <view class="usermotto">
    <!--<text class="user-motto">{{motto}}</text>-->
    <button type="default" size="{{defaultSize}}" plain="{{plain}}" hover-class="button-hover" disabled="{{disabled}}" bindtap="toCalc">{{motto}}</button>
  </view>
</view>

然后是index.js

//index.js
//获取应用实例
var app = getApp()
Page({
  data: {
    motto: '简易计算器',
    userInfo: {},
    defaultSize:'default',
    disabled:false,
    iconType:'info_cycle'
  },
  //事件处理函数
  bindViewTap: function() {
    wx.navigateTo({
      url: '../logs/logs'
    })
  },
  onLoad: function () {
    console.log('onLoad')
    var that = this
    //调用应用实例的方法获取全局数据
    app.getUserInfo(function(userInfo){
      //更新数据
      that.setData({
        userInfo:userInfo
      })
    })
  },
  //到计算器界面
  toCalc:function(){
    wx.navigateTo({
      url: '../cal/cal'
    })
  }
})

这里给大家附上一张丑丑的界面图。可以看到我这里是新建了cal这个文件夹,里面放上cal.js cal.wxml cal.wxss三个文件
这里写图片描述

记得在这里你新建好了一个wxml,你就需要在app.json去做页面的配置,不然你是访问不到的。所以下面放一下app.json(由于有历史功能,待会再加一遍)

{
  "pages":[
    "pages/index/index",
    "pages/logs/logs",
    "pages/cal/cal"
  ],
  "window":{
    "backgroundTextStyle":"light",
    "navigationBarBackgroundColor": "#fff",
    "navigationBarTitleText": "WeChat",
    "navigationBarTextStyle":"black"
  }
}



cal.wxss里面的rpx也是第一次见,查了一下

rpx单位是微信小程序中css的尺寸单位,rpx可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。

下面是cal.wxss文件,里面也没什么特别的,都是用的flex弹性布局,只是flex-direction的区别,附上文件cal.wxss

.content {
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    background-color: #ccc;
    font-family: "Microsoft YaHei";
    overflow-x: hidden;
}
.layout-top{
    width: 100%;
    margin-bottom: 30rpx;
}
.layout-bottom{
    width: 100%;
}
.screen {
    text-align: right;
    width: 100%;
    line-height: 260rpx;
    padding: 0 10rpx;
    font-weight: bold;
    font-size: 60px;
    box-sizing: border-box;
    border-top: 1px solid #fff;
}
.btnGroup {
    display: flex;
    flex-direction: row;
    flex: 1;
    width: 100%;
    height: 5rem;
    background-color: #fff;
}
.item {
    width:25%;
    display: flex;
    align-items: center;
    flex-direction: column;
    justify-content: center;
    margin-top: 1px;
    margin-right: 1px;
}
.item:active {
    background-color: #ff0000;
}
.zero{
    width: 50%;
}
.orange {
    color: #fef4e9;
    background: #f78d1d;
    font-weight: bold;
}
.blue {
    color:#d9eef7;
    background-color: #0095cd;
}
.iconBtn{
    display: flex;
}
.icon{
   display: flex;
   align-items: center;
   width:100%;
   justify-content: center;
}

接下来是cal.wxml

<view class="content">
  <view class="layout-top">
    <view class="screen">
     {{screenData}}
    </view>
  </view>

  <view class="layout-bottom">
    <view class="btnGroup">
      <view class="item orange" bindtap="clickBtn" id="{{idc}}">С</view>
      <view class="item orange" bindtap="clickBtn" id="{{idb}}"></view>
      <!--<view class="item orange" bindtap="clickBtn" id="{{idt}}">+/-</view>-->
      <view class="item orange iconBtn" bindtap="history">
          <icon type="{{iconType}}" color="{{iconColor}}" class="icon" size="25"/>
      </view>
      <view class="item orange" bindtap="clickBtn" id="{{idadd}}"></view>
    </view>
    <view class="btnGroup">
      <view class="item blue" bindtap="clickBtn" id="{{id9}}">9</view>
      <view class="item blue" bindtap="clickBtn" id="{{id8}}">8</view>
      <view class="item blue" bindtap="clickBtn" id="{{id7}}">7</view>
      <view class="item orange" bindtap="clickBtn" id="{{idj}}"></view>
    </view>
    <view class="btnGroup">
      <view class="item blue" bindtap="clickBtn" id="{{id6}}">6</view>
      <view class="item blue" bindtap="clickBtn" id="{{id5}}">5</view>
      <view class="item blue" bindtap="clickBtn" id="{{id4}}">4</view>
      <view class="item orange" bindtap="clickBtn" id="{{idx}}">×</view>
    </view>
    <view class="btnGroup">
      <view class="item blue" bindtap="clickBtn" id="{{id3}}">3</view>
      <view class="item blue" bindtap="clickBtn" id="{{id2}}">2</view>
      <view class="item blue" bindtap="clickBtn" id="{{id1}}">1</view>
      <view class="item orange" bindtap="clickBtn" id="{{iddiv}}">÷</view>
    </view>
    <view class="btnGroup">
      <view class="item blue zero" bindtap="clickBtn" id="{{id0}}">0</view>
      <view class="item blue" bindtap="clickBtn" id="{{idd}}">.</view>
      <view class="item orange" bindtap="clickBtn" id="{{ide}}"></view>
    </view>
  </view>
</view>

好了重点来了,下面是index.js,这里面基本所有的逻辑我都是敲了注释的,有什么不懂的也可以在下面给我留言,csdn我还是看得很多,这里建议大家可以先复制下试试我的代码没问题的话,就自己手动敲一敲,分析下简单的js逻辑.
index.js

//获取应用实例
var rpn = require("../../utils/rpn.js");
var app = getApp()
Page({
  data:{
    idb:"back",
    idc:"clear",
    idt:"toggle",
    idadd:"+",
    id9:"9",
    id8:"8",
    id7:"7",
    idj:"-",
    id6:"6",
    id5:"5",
    id4:"4",
    idx:"*",
    id3:"3",
    id2:"2",
    id1:"1",
    iddiv:"/",
    id0:"0",
    idd:".",
    ide:"=",
    screenData:"0",
    operaSymbo:{"+":"+","-":"-","*":"*","÷":"/",".":"."},
    lastIsOperaSymbo:false,
    iconType:'waiting_circle',
    iconColor:'white',
    logs:[]
  },
  onLoad:function(options){
    // 页面初始化 options为页面跳转所带来的参数
  },
  onReady:function(){
    // 页面渲染完成
  },
  onShow:function(){
    // 页面显示
  },
  onHide:function(){
    // 页面隐藏
  },
  onUnload:function(){
    // 页面关闭
  },
  clickBtn:function(event){
      //获取触发点击事件的标签的id
    var id=event.target.id
    if(id==this.data.idb){//退格←
        var data=this.data.screenData;
        if(data==0){
            return ;
        }
        data=data.substring(0,data.length-1);
        //屏幕上不会显示东西了
        if(data==""||data=="-"){//只剩下一个负号?不合理对吧
            data=0;
        }
        this.setData({
            'screenData':data
        });
    }else if(id==this.data.idc){//清屏
        this.setData({
            'screenData':'0'
        });
    }else if(id==this.data.idt){//加正负号,这里没有在界面上用,这一段可以不用看的
        var data=this.data.screenData;
        //0不考虑正负号
        if(data=="0"){
            return ;
        }
        var firstWord=data.charAt(0);
        if(data=="-"){//第一个字母是负号,给他变成正
            data=data.substr(1);
        }else{
            data="-"+data;
            ///如果你真的看到了这里,真的没必要看这个else if,因为这个就是给第一个数取正负号的问题,这里又没有括号选择,贼鸡肋,所以界面上也就不放这个功能了。
        }
        this.setData({
            'screenData':data
        });
    }else if(id==this.data.ide){//等于=
        var data=this.data.screenData;
        if(data=="0"){
            return ;
        }
        //eval是js中window的一个方法,而微信页面的脚本逻辑在是在JsCore中运行,JsCore是一个没有窗口对象的环境,所以不能再脚本中使用window,也无法在脚本中操作组件                 
      //var result = eval(newData);

      //eval方法不能应,只能我们自己来写了
      //不过我们可以调用rpn.js这个库,他已经为我们做好了

      //判断最后一位如果是操作符,则不运算
      //isNaN() 函数用于检查其参数是否是非数字值。 isNaN(123)返回false isNaN(wqwq)返回true
      var lastWord = data.charAt(data.length-1);
      if(isNaN(lastWord)){
          return ;
      }
      var log=this.data.screenData;
      //获取rpn库运算结果
      console.log(log);
      var calData=rpn.calCommonExp(log);
      this.data.logs.push(log+"="+calData);
      var allLogs = wx.getStorageSync('callogs') || [];
      allLogs.push(log+"="+calData);
      wx.setStorageSync('callogs',allLogs);
      this.setData({
          'screenData':calData+""
      });

    }else{//运算符和数字的问题  还有点
        if(this.data.operaSymbo[id]){ //如果是符号+-*/
            if(this.data.lastIsOperaSymbo){
                //如果是最后一位是符号,就不能在收入符号
                return;
        }
      }
      var sd=this.data.screenData;
      var data;
      //这个if else逻辑很简单
      if(sd==0){
          data=id;
      }else{
          data=sd+id;
      }
      this.setData({
          'screenData':data
      });

      ///置一下最后一位是否为运算符的标志位
      if(this.data.operaSymbo[id]){
        this.setData({"lastIsOperaSymbo":true});
      }else{
        this.setData({"lastIsOperaSymbo":false});
      }
    }
  },
  history:function(){
      wx.navigateTo({
        url: '../history/history'
    })
  }
})

我们只需要去判断输入逻辑,计算的事情就交给rpn.js库来做,我们只需要传入一个像1+2-3 这种表达式给他,他就能给出结果,下面给出rpn.js,这个不需要自己看,放在utils下面就行了

function isOperator(value) {
    var operatorString = '+-*/()×÷';
    return operatorString.indexOf(value) > -1;
}

function getPrioraty(value) {
    if(value == '-' || value == '+') {
        return 1;
    } else if(value == '*' || value == '/' || value == '×' || value == '÷' ) {
        return 2;
    } else{
        return 0;
    }
}

function prioraty(v1, v2) {
    return getPrioraty(v1) <= getPrioraty(v2);
}

function outputRpn(exp) {
    var inputStack = [];
    var outputStack = [];
    var outputQueue = [];
    var firstIsOperator = false;
    exp.replace(/\s/g,'');
    if(isOperator(exp[0])){
        exp = exp.substring(1);
        firstIsOperator = true;
    }
    for(var i = 0, max = exp.length; i < max; i++) {
        if(!isOperator(exp[i]) && !isOperator(exp[i-1]) && (i != 0)) {
            inputStack[inputStack.length-1] = inputStack[inputStack.length-1] + exp[i] + '';
        } else {
            inputStack.push(exp[i]);
        }
    }
    if(firstIsOperator) {
        inputStack[0] = -inputStack[0] 
    }
    while(inputStack.length > 0) {
        var cur = inputStack.shift();
        if(isOperator(cur)) {
            if (cur == '(') {
                outputStack.push(cur);
            } else if (cur == ')') {
                var po = outputStack.pop();
                while(po != '(' && outputStack.length > 0) {
                    outputQueue.push(po);
                    po = outputStack.pop();
                }
            } else {
                while(prioraty(cur,outputStack[outputStack.length - 1]) && outputStack.length > 0) {
                    outputQueue.push(outputStack.pop());
                }
                outputStack.push(cur)
            }
        } else {
            outputQueue.push(Number(cur));
        }
    }
    if(outputStack.length > 0){
        while (outputStack.length > 0) {
            outputQueue.push(outputStack.pop());
        }
    }
    return outputQueue;
}

function calRpnExp(rpnArr) {
    var stack = [];
    for(var i = 0, max = rpnArr.length; i < max; i++) {
        if(!isOperator(rpnArr[i])) {
            stack.push(rpnArr[i]);
        } else {
            var num1 = stack.pop();
            var num2 = stack.pop();
            if(rpnArr[i] == '-') {
                var num = num2 - num1;
            } else if(rpnArr[i] == '+') {
                var num = num2 + num1;
            } else if(rpnArr[i] == '*' || rpnArr[i] == '×') {
                var num = num2 * num1;
            } else if(rpnArr[i] == '/' || rpnArr[i] == '÷') {
                var num = num2/num1;
            }
            stack.push(num);
        }
    }
    return stack[0];
}

function calCommonExp(exp) {
    var rpnArr = outputRpn(exp);
    return calRpnExp(rpnArr)
}

module.exports = {
    isOperator: isOperator,
    getPrioraty: getPrioraty,
    outputRpn: outputRpn,
    calRpnExp: calRpnExp,
    calCommonExp: calCommonExp
}

最后是查看运算历史,我们用wx.setStorageSync(‘callogs’,this.data.logs);将 data 存储在本地缓存中指定的 key 中,会覆盖掉原来该 key 对应的内容,这是一个同步接口。 下面我们就来做查看历史这个操作。

先放一张效果图
这里写图片描述

下面是history.wxss

page{
    min-height:100%;
    height:100%;
}
.content{
    height:100%;
    display:flex;
    flex-direction:column;
    align-items:center;
    box-sizing:border-box;
    position:relative;
}
.item{
    width:90%;
    line-height:100rpx;
    margin-top:3rpx;
    margin-bottom:3rpx;
    border-radius:3px;
    color:#fef4e9;
    border:1px solid #da7c0c;
    background:#f78d1d;
    display:block;
    margin-right:auto;
    margin-left:auto;
}
.main-bg{
    height:100%;
    width:100%;
    min-height:100%;
    position:absolute;
    top:0;
    left:0;
    z-index:-1;
}

然后是history.wxml,循这个有什么不懂的可以打开api看看,wx:key这个

wx:key
如果列表中项目的位置会动态改变或者有新的项目添加到列表中,并且希望列表中的项目保持自己的特征和状态(如 中的输入内容, 的选中状态),需要使用 wx:key 来指定列表中项目的唯一的标识符。

wx:key 的值以两种形式提供
字符串,代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变。
保留关键字 *this 代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字,如:
当数据改变触发渲染层重新渲染的时候,会校正带有 key 的组件,框架会确保他们被重新排序,而不是重新创建,以确保使组件保持自身的状态,并且提高列表渲染时的效率。
如不提供 wx:key,会报一个 warning, 如果明确知道该列表是静态,或者不必关注其顺序,可以选择忽略。

<view class="content">
    <image class="main-bg" src="../asserts/img/timg.jpg"></image>
    <block wx:for="{{callogs}}" wx:for-item="log" wx:key="*this">
        <view class="item">{{log}}</view>
    </block>
</view>

最后是history.js

Page({
    data:{
        callogs:[]
    },
    onLoad:function(){
        console.log(wx.getStorageSync('callogs'));
        this.setData({
            callogs:wx.getStorageSync('callogs')
        });
    }
})

说一下这个

防入坑指南
1、千万不要用background-image或者使用background设置背景图片,开发工具里可以显示,真机不能显示,替换采用image标签
2、当真机中元素没有办法横向排列时,试着改变一下display为inline-block
3、页面没有办法铺满整个手机屏幕时,添加page{height;100%;}
4、wxss文件里的样式并不会覆盖,而是先声明的有效,后声明的无效
5、不要大量使用本地图片,小程序有规定大小,超过875kb无法预览
6、永远以真机效果为准,开发者工具预览和真机偏差样式方面有可能偏差很大

最后放下github源码下载地址 有什么问题欢饮大家留言指出。

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值