MiniStar 1 year ago
commit
e56a8f9cde
10 changed files with 642 additions and 0 deletions
  1. 50 0
      README.md
  2. 12 0
      app.js
  3. 12 0
      app.json
  4. 1 0
      app.wxss
  5. 38 0
      pages/index/index.js
  6. 44 0
      pages/index/index.wxml
  7. 90 0
      pages/index/index.wxss
  8. 72 0
      project.config.json
  9. 7 0
      sitemap.json
  10. 316 0
      utils/calc.js

+ 50 - 0
README.md

@@ -0,0 +1,50 @@
+一个微信小程序--简易计算器
+============================
+
+这是一个简单的仿小米计算器的微信小程序, 目前比较简单, 仅供参考, 欢迎star。
+
+计算器的逻辑是基于一个简单的状态机实现的, 下面给出了状态机图, 仅供参考。
+
+效果图:
+--------
+![效果图](http://7xqe6t.com1.z0.glb.clouddn.com/imgs/2016-11-09/calc/demo.gif)
+
+
+状态机图:
+--------
+![状态机图](http://7xqe6t.com1.z0.glb.clouddn.com/imgs/2016-11-09/calc/states.svg)
+
+* init: 初始状态
+* first_undot: 第一个操作数录入中, 无小数点
+* first_dot: 第一个操作数录入中, 有小数点
+* second_undot: 第二个操作数录入中, 无小数点
+* second_dot: 第二个操作数录入中, 有小数点
+* result: 结果状态
+
+
+## 主要特性
+
+* 支持简单的加减乘除和取余数
+* 支持连续操作, 比如做了加法以后, 结果会直接作为第一个操作数进入下一轮操作
+* 支持删除单个数字和一次性全部清空
+## LICENSE
+
+Copyright 2016 boyce.ywr@gmail.com
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 12 - 0
app.js

@@ -0,0 +1,12 @@
+//app.js
+App({
+  onLaunch: function () {
+    //调用API从本地缓存中获取数据
+    var logs = wx.getStorageSync('logs') || []
+    logs.unshift(Date.now())
+    wx.setStorageSync('logs', logs)
+  },
+  globalData:{
+    userInfo:null
+  }
+})

+ 12 - 0
app.json

@@ -0,0 +1,12 @@
+{
+  "pages": [
+    "pages/index/index"
+  ],
+  "window": {
+    "backgroundTextStyle": "light",
+    "navigationBarBackgroundColor": "#fff",
+    "navigationBarTitleText": "简易计算器",
+    "navigationBarTextStyle": "black"
+  },
+  "sitemapLocation": "sitemap.json"
+}

+ 1 - 0
app.wxss

@@ -0,0 +1 @@
+/**app.wxss**/

+ 38 - 0
pages/index/index.js

@@ -0,0 +1,38 @@
+//index.js
+//获取应用实例
+var app = getApp()
+var calc = require("../../utils/calc")
+Page({
+  data: {
+    calc: {},
+    tapped: {}
+  },
+  showAbout: function(e){
+    wx.showModal({
+      title: '关于',
+      content: '一个简单的计算器 @V1.0',
+      showCancel: false  
+    })
+  },
+  btnClicked: function(e){
+    var code = e.target.dataset.op
+    calc.addOp(code)
+    console.log(calc.getVars())
+    this.setData({calc: calc.getVars()})
+  },
+  btnTouchStart: function(e){
+    var code = e.target.dataset.op
+    var tapped = {[code]: 'active'}
+    this.setData({tapped: tapped})
+  },
+  btnTouchEnd: function(e){
+    var code = e.target.dataset.op
+    var tapped = {}
+    this.setData({tapped: tapped})
+  },
+  onLoad: function () {
+    console.log('onLoad')
+    calc.reset()
+    var that = this
+  }
+})

+ 44 - 0
pages/index/index.wxml

@@ -0,0 +1,44 @@
+<!--weixin.wxml-->
+<view class="container">
+  <view class="panel-display" style="position: relative;">
+  <view>
+    <icon id="icon-about" type="info" size="28" color="#aaa" bindtap="showAbout"/></view>
+    <view id="display-num">{{calc.displayNum}}</view>
+    <view id="display-op">{{calc.displayOp}}</view>
+  </view>
+  <view class="panel-btns">
+    <view class="btns-rows">
+      <view id="btn-c" class="btn {{tapped['c']}}" bindtap="btnClicked" bindtouchstart="btnTouchStart"  bindtouchend="btnTouchEnd" data-op="c">AC</view>
+      <view class="btn {{tapped['d']}}" bindtap="btnClicked" bindtouchstart="btnTouchStart"  bindtouchend="btnTouchEnd"  data-op="d">DEL</view>
+      <view class="btn {{tapped['/']}}" bindtap="btnClicked" bindtouchstart="btnTouchStart"  bindtouchend="btnTouchEnd"  data-op="/" style="font-size: 24px;">÷</view>
+      <view class="btn {{tapped['x']}}" bindtap="btnClicked" bindtouchstart="btnTouchStart"  bindtouchend="btnTouchEnd"  data-op="x">×</view>
+    </view>
+    <view class="btns-rows">
+      <view class="btn {{tapped['7']}}" bindtap="btnClicked" bindtouchstart="btnTouchStart"  bindtouchend="btnTouchEnd"  data-op="7">7</view>
+      <view class="btn {{tapped['8']}}" bindtap="btnClicked" bindtouchstart="btnTouchStart"  bindtouchend="btnTouchEnd"  data-op="8">8</view>
+      <view class="btn {{tapped['9']}}" bindtap="btnClicked" bindtouchstart="btnTouchStart"  bindtouchend="btnTouchEnd"  data-op="9">9</view>
+      <view class="btn {{tapped['-']}}" bindtap="btnClicked" bindtouchstart="btnTouchStart"  bindtouchend="btnTouchEnd"  data-op="-">-</view>
+    </view>
+    <view class="btns-rows">
+      <view class="btn {{tapped['4']}}" bindtap="btnClicked" bindtouchstart="btnTouchStart"  bindtouchend="btnTouchEnd"  data-op="4">4</view>
+      <view class="btn {{tapped['5']}}" bindtap="btnClicked" bindtouchstart="btnTouchStart"  bindtouchend="btnTouchEnd"  data-op="5">5</view>
+      <view class="btn {{tapped['6']}}" bindtap="btnClicked" bindtouchstart="btnTouchStart"  bindtouchend="btnTouchEnd"  data-op="6">6</view>
+      <view class="btn {{tapped['+']}}" bindtap="btnClicked" bindtouchstart="btnTouchStart"  bindtouchend="btnTouchEnd"  data-op="+">+</view>
+    </view>
+    <view id="btns2" class="btns-rows">
+      <view id="btns2-left">
+        <view class="btns2-left-part">
+          <view class="btn {{tapped['1']}}" bindtap="btnClicked" bindtouchstart="btnTouchStart"  bindtouchend="btnTouchEnd"  data-op="1">1</view>
+          <view class="btn {{tapped['2']}}" bindtap="btnClicked" bindtouchstart="btnTouchStart"  bindtouchend="btnTouchEnd"  data-op="2">2</view>
+          <view class="btn {{tapped['3']}}" bindtap="btnClicked" bindtouchstart="btnTouchStart"  bindtouchend="btnTouchEnd"  data-op="3">3</view>
+        </view>
+        <view class="btns2-left-part">
+          <view class="btn {{tapped['%']}}" bindtap="btnClicked" bindtouchstart="btnTouchStart"  bindtouchend="btnTouchEnd"  data-op="%">%</view>
+          <view class="btn {{tapped['0']}}" bindtap="btnClicked" bindtouchstart="btnTouchStart"  bindtouchend="btnTouchEnd"  data-op="0">0</view>
+          <view class="btn {{tapped['.']}}" bindtap="btnClicked" bindtouchstart="btnTouchStart"  bindtouchend="btnTouchEnd"  data-op=".">.</view>
+        </view>
+      </view>
+      <view id="btns2-right" class="btn {{tapped['=']}}" bindtap="btnClicked" bindtouchstart="btnTouchStart"  bindtouchend="btnTouchEnd" data-op="=">=</view>
+    </view>
+  </view>
+</view>

+ 90 - 0
pages/index/index.wxss

@@ -0,0 +1,90 @@
+/**index.wxss**/
+
+.container{
+  display: flex;
+  flex-direction: column;
+  height: 100vh;
+  align-items: center;
+  justify-content: space-between;
+  background-color: #f1f3f3;
+}
+
+.panel-display{
+  width: 100%;
+  flex: 1;
+}
+
+.panel-btns{
+  width: 100%;
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+}
+
+.btns-rows{
+  width: 100%;
+  flex: 1;
+  display: flex;
+  flex-direction: row;
+  background-color: #f7f8f9;
+}
+
+.btn{
+  text-align: center;
+  box-sizing: border-box;
+  flex: 1;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  border-top: 1px solid #c3c6c7;
+}
+
+.btn.active{
+  background-color: #e3e4e5;
+}
+
+.btn:not(:last-child){
+  border-right: 1px solid #c3c6c7;
+}
+
+#btns2-right{
+  border-left: 1px solid #c3c6c7;
+}
+
+#display-num{
+  display: inline-block;
+  font-size: 36px;
+  position: absolute; 
+  bottom: 5vh; 
+  right: 3vw; 
+}
+
+#display-op{
+  display: inline-block;
+  font-size: 16px;
+  position: absolute; 
+  bottom: 1vh; 
+  right: 3vw; 
+}
+
+#btns2{
+  flex: 2;display: flex;flex-direction: row;
+}
+
+#btns2-left{
+  flex: 3;display: flex;flex-direction: column;
+}
+
+.btns2-left-part{
+  display: flex; flex-direction: row;flex-grow: 1;
+}
+
+#btn-c{
+  color: #f00;
+}
+
+#icon-about{
+  position: absolute; 
+  right: 3vw;
+  top: 3vw;
+}

+ 72 - 0
project.config.json

@@ -0,0 +1,72 @@
+{
+  "description": "Project configuration file",
+  "packOptions": {
+    "ignore": []
+  },
+  "setting": {
+    "urlCheck": true,
+    "es6": true,
+    "enhance": false,
+    "postcss": true,
+    "preloadBackgroundData": false,
+    "minified": true,
+    "newFeature": false,
+    "coverView": true,
+    "nodeModules": false,
+    "autoAudits": false,
+    "showShadowRootInWxmlPanel": true,
+    "scopeDataCheck": false,
+    "uglifyFileName": false,
+    "checkInvalidKey": true,
+    "checkSiteMap": true,
+    "uploadWithSourceMap": true,
+    "compileHotReLoad": false,
+    "useMultiFrameRuntime": false,
+    "useApiHook": true,
+    "babelSetting": {
+      "ignore": [],
+      "disablePlugins": [],
+      "outputPath": ""
+    },
+    "enableEngineNative": false,
+    "bundle": false,
+    "useIsolateContext": true,
+    "useCompilerModule": true,
+    "userConfirmedUseCompilerModuleSwitch": false,
+    "userConfirmedBundleSwitch": false,
+    "packNpmManually": false,
+    "packNpmRelationList": [],
+    "minifyWXSS": true
+  },
+  "compileType": "miniprogram",
+  "libVersion": "2.12.2",
+  "appid": "wx8551d91392a2ea86",
+  "projectname": "miniprogram-1",
+  "debugOptions": {
+    "hidedInDevtools": []
+  },
+  "scripts": {},
+  "isGameTourist": false,
+  "simulatorType": "wechat",
+  "simulatorPluginLibVersion": {},
+  "condition": {
+    "search": {
+      "list": []
+    },
+    "conversation": {
+      "list": []
+    },
+    "game": {
+      "list": []
+    },
+    "plugin": {
+      "list": []
+    },
+    "gamePlugin": {
+      "list": []
+    },
+    "miniprogram": {
+      "list": []
+    }
+  }
+}

+ 7 - 0
sitemap.json

@@ -0,0 +1,7 @@
+{
+  "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
+  "rules": [{
+  "action": "allow",
+  "page": "*"
+  }]
+}

+ 316 - 0
utils/calc.js

@@ -0,0 +1,316 @@
+/**!
+ *
+ * Copyright(c) boyce and other contributors.
+ * MIT Licensed
+ *
+ * Authors:
+ *  boyce <boyce.ywr@gmail.com> (http://www.jianshu.com/users/9b5b907d9bce)
+ */
+
+'use strict'
+
+const STATE = {
+    INIT: 0,  //初始状态
+    RESULT: 1,  //结果状态
+    FIRST_UNDOT: 2,  //记录第一个操作数,且该操作数没有小数点
+    FIRST_DOT: 3,   //记录第一个操作数,且该操作数有小数点
+    SECOND_UNDOT: 4, //记录第二个操作数,且该操作数没有小数点
+    SECOND_DOT: 5 //记录第二个操作数,且该操作数有小数点
+}
+
+let curState = STATE.INIT  //状态机所在状态
+let curResult = 0   //计算结果
+let opNum1 = '0'   //操作数1
+let opNum2 = ''  //操作数2
+let op = ''   //操作符
+
+let displayNum = opNum1 //界面上应当显示的数值
+let displayOp = ""  //界面上应当显示的操作符
+
+/**
+ * 重置程序状态
+ */
+function reset() {
+    curState = STATE.INIT
+    curResult = 0
+    opNum1 = '0'
+    opNum2 = ''
+    op = ''
+}
+
+/**
+ * 是否为零
+ */
+function isZero(code) {
+    return code == '0'
+}
+
+/**
+ * 是否数字
+ */
+function isNumber(code) {
+    return code >= '0' && code <= '9'
+}
+
+/**
+ * 是否操作符
+ */
+function isOperator(code) {
+    return code == '+' || code == '-'
+        || code == 'x' || code == '/' || code == '%'
+}
+
+/**
+ * 是否小数点
+ */
+function isDot(code) {
+    return code == '.'
+}
+
+/**
+ * 是否是等号
+ */
+function isEquel(code) {
+    return code == '='
+}
+
+/**
+ * 是否清楚
+ */
+function isClear(code) {
+    return code == 'c'
+}
+
+/**
+ * 是否删除
+ */
+function isDelete(code) {
+    return code == 'd'
+}
+
+/**
+ * 转换为可现实的操作符
+ */
+function op2Show(code) {
+    return code == '/' ? '÷' : (code == 'x' ? '×' : code)
+}
+
+/**
+ *
+ */
+function tryAppend(num, code) {
+    if (num.length < 15) {
+        num += code
+    }
+    return num
+}
+
+function tryTrunc(num) {
+    let str = '' + num
+    if (str.length > 15) {
+        str = str.substr(0, 15)
+    }
+    return str
+}
+
+/**
+ *
+ */
+function tryDelete() {
+    if (curState == STATE.SECOND_DOT
+        || curState == STATE.SECOND_UNDOT) {
+        if (opNum2.length > 0) {
+            opNum2 = opNum2.substr(0, opNum2.length - 1)
+        }
+        if (opNum2 == '') {
+            opNum2 = '0'
+        }
+        return
+    } else {
+        if (opNum1.length > 0 && opNum1 != '0') {
+            opNum1 = opNum1.substr(0, opNum1.length - 1)
+        }
+        if (opNum1 == '') {
+            opNum1 = '0'
+        }
+        return
+    }
+}
+
+function tryCalc() {
+    let n1 = parseFloat(opNum1)
+    let n2 = parseFloat(opNum2)
+    switch (op) {
+        case '+':
+            curResult = n1 + n2
+            break
+        case '-':
+            curResult = n1 - n2
+            break
+        case 'x':
+            curResult = n1 * n2
+            break
+        case '/':
+            if (n2 == 0) {
+                reset()
+                curResult = 'NaN'
+                displayOp = ''
+            } else {
+                curResult = n1 / n2
+            }
+            break
+        case '%':
+            if (n2 == 0) {
+                reset()
+                curResult = 'NaN'
+                displayOp = ''
+            } else {
+                curResult = n1 % n2
+            }
+            break
+    }
+    curResult = tryTrunc(curResult)
+}
+
+function addOp(code) {
+    switch (curState) {
+        case STATE.RESULT:
+        case STATE.INIT:
+            if (isNumber(code) && !isZero(code)) {
+                curState = STATE.FIRST_UNDOT
+                opNum1 = code
+            } else if (isDot(code)) {
+                curState = STATE.FIRST_DOT
+                opNum1 = '0.'
+            } else if (isOperator(code)) {
+                curState = STATE.SECOND_UNDOT
+                opNum1 = '0'
+                opNum2 = ''
+                op = code
+            }
+            displayNum = opNum1
+            displayOp = ''
+            break
+        case STATE.FIRST_UNDOT:
+            displayOp = ''
+            if (isNumber(code)) {
+                if (!isZero(opNum1)) {
+                    opNum1 = tryAppend(opNum1, code)
+                } else {
+                    opNum1 = code
+                }
+            } else if (isDot(code)) {
+                curState = STATE.FIRST_DOT
+                opNum1 = opNum1 == '' ? '0' : tryAppend(opNum1, '.')
+            } else if (isDelete(code)) {
+                tryDelete()
+            } else if (isOperator(code)) {
+                curState = STATE.SECOND_UNDOT
+                op = code
+                opNum2 = ''
+                displayOp = op
+            }
+            displayNum = opNum1
+            break
+        case STATE.FIRST_DOT:
+            displayOp = ''
+            if (isNumber(code)) {
+                opNum1 = tryAppend(opNum1, code)
+            } else if (isDelete(code)) {
+                tryDelete()
+                if (opNum1.indexOf('.') < 0)
+                    curState = STATE.FIRST_UNDOT
+            } else if (isOperator(code)) {
+                curState = STATE.SECOND_UNDOT
+                op = code
+                opNum2 = ''
+                displayOp = op
+            }
+            displayNum = opNum1
+            break
+        case STATE.SECOND_UNDOT:
+            if (isNumber(code)) {
+                if (!isZero(opNum2)) {
+                    opNum2 = tryAppend(opNum2, code)
+                } else {
+                    opNum2 = code
+                }
+                displayNum = opNum2
+            } else if (isDot(code)) {
+                curState = STATE.SECOND_DOT
+                opNum2 = opNum2 == '' ? '0' : tryAppend(opNum2, '.')
+                displayNum = opNum2
+            } else if (isDelete(code)) {
+                tryDelete()
+                displayNum = opNum2
+            } else if (isOperator(code)) {
+                if (opNum2 != '') {
+                    //直接计算
+                    tryCalc()
+                    curState = STATE.SECOND_UNDOT
+                    opNum1 = curResult
+                    opNum2 = ''
+                    displayNum = curResult
+                }
+                op = code
+                displayOp = op
+            } else if (isEquel(code)) {
+                if (opNum2 != '') {
+                    tryCalc()
+                    curState = STATE.RESULT
+                    opNum1 = '0'
+                    opNum2 = ''
+                    displayNum = curResult
+                }
+                op = code
+                displayOp = op
+            }
+            break
+        case STATE.SECOND_DOT:
+            if (isNumber(code)) {
+                opNum2 = tryAppend(opNum2, code)
+                displayNum = opNum2
+            } else if (isDelete(code)) {
+                tryDelete()
+                if (opNum2.indexOf('.') < 0)
+                    curState = STATE.SECOND_UNDOT
+                displayNum = opNum2
+            } else if (isOperator(code)) {
+                if (opNum2 != '') {
+                    //直接计算
+                    tryCalc()
+                    curState = STATE.SECOND_UNDOT
+                    opNum1 = curResult
+                    opNum2 = ''
+                    displayNum = curResult
+                }
+                op = code
+                displayOp = op
+            } else if (isEquel(code)) {
+                if (opNum2 != '') {
+                    tryCalc()
+                    curState = STATE.RESULT
+                    opNum1 = '0'
+                    opNum2 = ''
+                    displayNum = curResult
+                }
+                op = code
+                displayOp = op
+            }
+            break
+    }
+    if (isClear(code)) {
+        reset()
+        displayNum = opNum1
+        displayOp = ''
+    }
+    displayOp = op2Show(displayOp)
+}
+
+reset()
+
+module.exports = {
+    reset, addOp, getVars(){
+        return {curState, curResult, opNum1, opNum2, op, displayNum, displayOp}
+    }
+}