制作一款好看的计算器
序
微信小程序总是学习知识点也是很枯燥的,那么就做个小栗子来缓解一下学习的疲倦吧(更新累了)
像我学习小程序视频教程中也有说过做计算器,不过教程中的计算器太粗糙了,只能计算一下就结束了。 这可不行,男人要持久:dog:
所以我有感而发,准备做一个完整度高一些的简单计算器,计算器的基本功能就模仿win10自带的计算器吧,有加、减、乘、除、取余、正负这几块的功能,界面自己进行一下调整。
实现效果(图居然没录全,右侧少了一点):
正文开始
微信开发文档:https://developers.weixin.qq.com/miniprogram/dev/component/
创建页面
在微信小程序中,page页面是一个非常重要的概念,计算器页面可以在资源管理器中自己右键创建文件夹生成,也可以直接在app.json的page对象中输入页面地址和名称来生成。
app.json
中例子如下:
"pages": [
"pages/Calculator/Calculator",
"pages/index/index",
"pages/logs/logs",
],
ctrl + s保存后,资源管理器中就出现了Calculator的页面
页面一开始创建出来,在wxml中只有一行字,直接删掉即可。
关于navigationBarTitleText顶部标题栏的文字和背景颜色,可以在app.json中设置,也可以在页面的JS(Calculator.js)中进行设置
- 在app.json中设置
- Calculator.js中设置
onLoad() {
wx.setNavigationBarTitle({
title: '空城机の计算器',
})
wx.setNavigationBarColor({
frontColor: '#ffffff',
backgroundColor: '#C37D58',
})
},
页面布局
在本次制作当中,为了在不同机型下自适应,我使用的尺寸格式是rpx,机型选择了iPhone6,这样 1px == 2rpx,将CSS编写的px像素大小乘2即可
页面总体分为三大块, 背景、屏幕、按键
背景我使用了image标签来做的,绝对定位,大小100%
在屏幕上,本来我想制作一条工具栏,暂时先放弃了
屏幕上目前分为计算栏和结果栏
按键界面上分为三块,一块是清除、数字正负、取余功能,一块是加减乘除等于这5个按键,另一块是数字按键部分
图示:
页面样式
页面的样式主要集中在wxss文件当中
屏幕和按键都是以毛玻璃效果为主,这样可以将底部的背景图模糊的展现出来
毛玻璃效果的制作以背景色background-color半透明为主,然后设置backdrop-filter blur滤镜模糊效果
backdrop-filter的MDN:https://developer.mozilla.org/zh-CN/docs/Web/CSS/backdrop-filter
这里的颜色大家可以自己自由选择搭配,比如我的屏幕CSS:
background-color: rgba(255, 255, 255, .3); /* 背景色 */
box-shadow: 6rpx 6rpx 12px 6rpx rgba(0, 0, 0, .3); /* 阴影 */
backdrop-filter: blur(20rpx); /* 高斯滤镜 */
键盘的按键布局我使用了flex
布局,这款布局非常好用,将上面的按键设置为宽25%,让flex-wrap: wrap;
自动换行生效,按键就可以有效的排版布局了。
在设置0按键
时,使用:nth-child
子类选择即可
在点击键盘按键时,按键需要有一个动态的交互过程,在微信小程序中有一个hover-class
的概念,在view上可以给hover-class
附上一个class,这个class的样式就是按键按下时的样式,通常手指抬起后,这个样式的持续时间有400毫秒,这里可以使用hover-stay-time
设置一个时间
顺便提一句,即使是在wxss中,依旧还是有:hover
的存在,不过此时的:hover
设置的就是按下去的样式,并且手指抬起不会恢复哦
数据渲染
在计算器下方的键盘上,按键有19个,如果一个按键一个按键写入wxml当中,就未免太过繁琐了,而且写点击方法时也比较麻烦
所以我将这些按钮的信息写入JS文件中,在Page()中存在data,这是页面的初始数据
设置keysdata数组对象,里面装了按键数组,每个按键都有name、index、type这三个属性。 type属性可以用来区分到底是计算符还是数字
/**
* 页面的初始数据
*/
data: {
keysdata: [ // type:0 计算符 1 数字
{ name: 'AC', index: '001', type: 0 },
{ name: '+/-', index: '002', type: 0 },
{ name: '%', index: '003', type: 0 },
{ name: '÷', index: '004', type: 0 },
{ name: '7', index: '005', type: 1 },
{ name: '8', index: '006', type: 1 },
{ name: '9', index: '007', type: 1 },
{ name: '×', index: '008', type: 0 },
{ name: '4', index: '009', type: 1 },
{ name: '5', index: '0010', type: 1 },
{ name: '6', index: '0011', type: 1 },
{ name: '-', index: '0012', type: 0 },
{ name: '1', index: '0013', type: 1 },
{ name: '2', index: '0014', type: 1 },
{ name: '3', index: '0015', type: 1 },
{ name: '+', index: '0016', type: 0 },
{ name: '0', index: '0017', type: 1 },
{ name: '.', index: '0018', type: 1 },
{ name: '=', index: '0019', type: 0 },
],
resultNum: '0', //结果
calNum1: '', // 计算前位
calNum2: '', // 计算后位
calIcon: '', // 计算符
calIconEQ: '', // = 计算符
isCompute: false, // 是否计算
lastbtn: 0, //上一个按钮 0:数字 1:计算符号 2:等于 3:清空 4:正负
},
在wxml的按键渲染中使用wx:for
语句遍历渲染出按键
<view class="keyboards" wx:for="{{ keysdata }}" wx:key="index">
<view class="keys" data-num="{{ item }}" hover-class="hoverkeys"
hover-stay-time="100" bindtap="keyClick" >
{{ item.name }}
</view>
</view>
计算逻辑
这是整个过程中最复杂最困难的部分
一开始我并没有添加记录上一个按键的属性,这导致了我一开始的判断做的很累,而且还不完善
然后又把逻辑给重新写了一下
在按键按下去的功能中,最重要的是计算和等于两种
计算中按键判断大致相同,首先先准备一个计算方法compute,根据传入参数将加减乘除取余这五种操作写入进去
compute(num1, num2, way)
参数有三个,前两个代表计算的数值,way表示方式
并且num1和num2因为判断的不同,可能传入的num1,num2数值会是空字符串。所以需要将数值根据方法不同来进行计算 (本来不想放代码的)
compute(num1, num2, way) {
let res = 0;
switch(way) {
case '%':
if (!num1 && num1 != 0) { num1 = '1' }
if (!num2) { num2 = '1' }
res = parseFloat(num1) % parseFloat(num2); break;
case '÷':
if (!num1) { num1 = '1' }
if (!num2) { num2 = '1' }
res = parseFloat(num1) / parseFloat(num2); break;
case '×':
if (!num1) { num1 = '1' }
if (!num2) { num2 = '1' }
res = parseFloat(num1) * parseFloat(num2); break;
case '-':
if (!num1) { num1 = '0' }
if (!num2) { num2 = '0' }
res = parseInt(num1) - parseInt(num2); break;
case '+':
if (!num1) { num1 = '0' }
if (!num2) { num2 = '0' }
res = parseFloat(num1) + parseFloat(num2); break;
}
return res;
}
在计算过程栏和结果栏中需要用到上面数据渲染中data定义的calNum1、calNum2、calIcon等对象
<!-- 计算过程 -->
<view class="calcuProcess">
{{ calNum1 }} {{ calIcon }} {{ calNum2 }} {{ calIconEQ }}
</view>
<!-- 结果和输入 -->
<view class="inputNum">
{{ resultNum }}
</view>
calNum1 | calIcon | calNum2 | calInconEQ | resultNum |
---|---|---|---|---|
7 | + | 8 | = | 15 |
因为键盘按键是使用wx:for进行渲染的,data-num="{{ item }}"
利用data-将按键数据绑定到元素上
所以可以使用bindtap="keyClick"
来绑定keyClick点击方法
根据event的target来判断当前按下的是哪一个按键
let n = event.target.dataset.num;
先判断按键是否为数字按键或者是计算符按键
如果是数字按键,如果上一次按下的按键是计算符,那么结果栏的输入显示的resultNum数值先置为空字符串,之后再判断首字母是否是0,如果是08、012这类数字,将首字母的0去掉
如果按下的是计算符按键,那么判断就更加复杂了
1、每一种按键都有自身的逻辑方法,根据按键值进入不同的方法
// 判断执行哪个方法
switch (n.name) {
case 'AC': this.clearScreen(); break;
case '+/-': this.isNegative(); break;
case '%': this.remainder(); break;
case '÷': this.division(); break;
case '×': this.multiplication(); break;
case '-': this.reduce(); break;
case '+': this.add(); break;
case '=': this.equalRes(); break;
}
2、当前按键值是加减乘除这类计算值
上一次的按键lastbtn为数字,那么将calNum1和结果栏上当前显示的数值进行计算
比如:let res = this.compute(data.calNum1, data.resultNum, '+')
上一次按键为等号 = ,将数据计算出来,然后去除calNum2和最后的等号
如果计算过程栏为空,直接按计算符,那么就是
最后不要忘记改变上一次的按键lastbtn记录和calIcon计算符
微信小程序和vue的区别在于这不是双向绑定的,虽然使用this.callcon = '某个值'
可以改变当前的calIncon,但是页面wxml上并不会改变,所以需要使用setData
方法来进行改变页面
3、当前按键值是等于号
判断在计算过程栏中是否有过等于符,如果有,那么继续那calNum1的值和结果栏值进行计算
如下示例:
如果接下来没有calNum2,判断上一次按键是否是数字或者等于符
如果上一次按键是计算符
4、清除符,将resultNum置为0,其他的calNum1等符号置为空即可
注意:这里像按键这些逻辑没有具体写,所以各位可以发散性的编写,并且我的逻辑并不一定是最优解,只能说给各位一个大致的方向
完整代码
Calculator.wxml
<!-- 背景图 -->
<image class="img" src="../../source/wei2.png"></image>
<!-- 屏幕部分 -->
<view class="calculateScreen">
<!-- 功能栏 -->
<view class="funBar"></view>
<!-- 计算过程 -->
<view class="calcuProcess">
{{ calNum1 }} {{ calIcon }} {{ calNum2 }} {{ calIconEQ }}
</view>
<!-- 结果和输入 -->
<view class="inputNum">
{{ resultNum }}
</view>
</view>
<!-- 键盘界面 -->
<view class="keyboard">
<view class="keyboards" wx:for="{{ keysdata }}" wx:key="index">
<view class="keys" data-num="{{ item }}" hover-class="hoverkeys"
hover-stay-time="100" bindtap="keyClick" >
{{ item.name }}
</view>
</view>
</view>
Calculator.wxss
/* pages/Calculator/Calculator.wxss */
.img{
position: absolute;
top: 0rpx;
width: 100%;
height: 100vh;
z-index: 0;
}
.calculateScreen {
position: relative;
width: 100%;
height: 30vh;
background-color: rgba(255, 255, 255, .3);
z-index: 100;
box-shadow: 6rpx 6rpx 12px 6rpx rgba(0, 0, 0, .3);
overflow: hidden;
/* 小程序的高斯模糊效果要加backdrop */
backdrop-filter: blur(20rpx);
}
.funBar {
width: 100%;
height: 90rpx;
}
.calcuProcess {
width: 100%;
height: 90rpx;
line-height: 90rpx;
font-size: 32rpx;
text-align: right;
color: #1d1a1a;
}
.inputNum {
width: calc(100vw - 40rpx);
height: calc(100% - 200rpx);
line-height: 115rpx;
font-size: 80rpx;
text-align: right;
padding: 0rpx 20rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-shadow: 5px 5px 5px #464443;
}
.keyboard {
width: 100%;
height: calc(70vh - 32rpx);
padding-top: 12rpx;
display: flex;
flex-wrap: wrap;
position: relative;
}
.keyboards {
width: 25%;
height: 20%;
display: flex;
justify-content:center;
align-items: center;
}
.keys {
width: 130rpx;
height: 130rpx;
border-radius: 50%;
line-height: 130rpx;
text-align: center;
font-size: 60rpx;
backdrop-filter: blur(10px);
/* background-color: rgba(246,245,245, .6); */
background: linear-gradient(145deg, rgba(235,228,228, .3), rgba(221,214,214, .2));
box-shadow: 10px 10px 20px #8d8888,
-10px -10px 20px #e2dfdf;
color: #fff;
text-shadow: 5px 5px 5px #992002;
}
.hoverkeys {
background: linear-gradient(145deg, rgba(221,214,214, .6), rgba(235,228,228, .7));
box-shadow: 10px 10px 30px #302a2a,
-10px -10px 30px #726060;
}
.keyboards:nth-child(17) {
width: 50%;
}
.keyboards:nth-child(17) .keys {
width: 300rpx;
border-radius: 130rpx;
}
Calculator.js
// pages/Calculator/Calculator.js
Page({
onLoad() {
wx.setNavigationBarTitle({
title: '空城机の计算器',
})
wx.setNavigationBarColor({
frontColor: '#ffffff',
backgroundColor: '#C37D58',
})
},
/**
* 页面的初始数据
*/
data: {
keysdata: [ // type:0 计算符 1 数字
{ name: 'AC', index: '001', type: 0 },
{ name: '+/-', index: '002', type: 0 },
{ name: '%', index: '003', type: 0 },
{ name: '÷', index: '004', type: 0 },
{ name: '7', index: '005', type: 1 },
{ name: '8', index: '006', type: 1 },
{ name: '9', index: '007', type: 1 },
{ name: '×', index: '008', type: 0 },
{ name: '4', index: '009', type: 1 },
{ name: '5', index: '0010', type: 1 },
{ name: '6', index: '0011', type: 1 },
{ name: '-', index: '0012', type: 0 },
{ name: '1', index: '0013', type: 1 },
{ name: '2', index: '0014', type: 1 },
{ name: '3', index: '0015', type: 1 },
{ name: '+', index: '0016', type: 0 },
{ name: '0', index: '0017', type: 1 },
{ name: '.', index: '0018', type: 1 },
{ name: '=', index: '0019', type: 0 },
],
resultNum: '0', //结果
calNum1: '', // 计算前位
calNum2: '', // 计算后位
calIcon: '', // 计算符
calIconEQ: '', // = 计算符
isCompute: false, // 是否计算
lastbtn: 0, //上一个按钮 0:数字 1:计算符号 2:等于 3:清空 4:正负
},
// 键盘点击事件
keyClick(event) {
let n = event.target.dataset.num;
if (n.type == 1) {
if (this.data.lastbtn == 1) {
this.data.resultNum = ''
}
if (this.data.resultNum == '0') {
if(n.name != '.')
this.data.resultNum = n.name;
else
this.data.resultNum += n.name;
} else {
if( this.data.lastbtn == '2' ) {
// 是否等于过
if (this.data.calIconEQ) {
this.clearScreen();
}
this.data.resultNum = '';
}
this.data.resultNum += n.name;
}
this.setData({ resultNum: this.data.resultNum })
this.data.lastbtn = 0; //更新最新按下按钮
} else if(n.type == 0){
// 判断执行哪个方法
switch (n.name) {
case 'AC': this.clearScreen(); break;
case '+/-': this.isNegative(); break;
case '%': this.remainder(); break;
case '÷': this.division(); break;
case '×': this.multiplication(); break;
case '-': this.reduce(); break;
case '+': this.add(); break;
case '=': this.equalRes(); break;
}
}
},
// 相加
add () {
let data = this.data;
if (data.lastbtn == 1 && data.calIcon == '+') return ;
// 上一次数字
if (data.lastbtn == 0) {
let res = this.compute(data.calNum1, data.resultNum, '+')
this.setData({ calNum1: res })
this.setData({ resultNum: res })
}
// 上一次等于
if (data.lastbtn == 2 && data.calIconEQ) {
this.setData({ calNum1: data.resultNum })
this.setData({ calNum2: '' })
this.setData({ calIconEQ: '' })
}
// 如果界面为空
if (!data.calNum1 && !data.calIcon) {
this.setData({ calNum1: data.resultNum });
}
this.setData({ calIcon: '+' });
data.lastbtn = 1; //更新最新按下按钮
},
// 清除
clearScreen () {
this.setData({ resultNum: '0' });
this.setData({ calNum1: '' });
this.setData({ calNum2: '' });
this.setData({ calIcon: '' });
this.setData({ calIconEQ: '' });
this.data.lastbtn = 3; //更新最新按下按钮
console.clear()
},
// 正负
isNegative (){
let data = this.data;
data.resultNum = parseFloat(data.resultNum); //数值变字符串
if (data.resultNum > 0){
this.setData({ resultNum: -data.resultNum })
} else {
this.setData({ resultNum: Math.abs(data.resultNum) })
}
data.lastbtn = 4; //更新最新按下按钮
},
// 余数
remainder () {
let data = this.data;
if (data.lastbtn == 1 && data.calIcon == '%') return ;
// 上一次数字
if (data.lastbtn == 0) {
if (!data.calNum1 && !data.calIcon) {
this.setData({ calNum1: data.resultNum })
} else {
let res = this.compute(data.calNum1, data.resultNum, '%')
this.setData({ calNum1: res })
this.setData({ resultNum: res })
}
}
// 上一次等于
if (data.lastbtn == 2 && data.calIconEQ) {
this.setData({ calNum1: data.resultNum })
this.setData({ calNum2: '' })
this.setData({ calIconEQ: '' })
}
// 如果界面为空
if (!data.calNum1 && !data.calIcon) {
this.setData({ calNum1: data.resultNum });
}
this.setData({ calIcon: '%' });
data.lastbtn = 1; //更新最新按下按钮
},
// 除法
division () {
let data = this.data;
if (data.lastbtn == 1 && data.calIcon == '÷') return ;
if (data.calIconEQ) {
}
// 上一次数字
if (data.lastbtn == 0) {
if (!data.calNum1 && !data.calIcon) {
this.setData({ calNum1: data.resultNum })
} else {
let res = this.compute(data.calNum1, data.resultNum, '÷')
this.setData({ calNum1: res })
this.setData({ resultNum: res })
}
}
// 上一次等于
if (data.lastbtn == 2 && data.calIconEQ) {
this.setData({ calNum1: data.resultNum })
this.setData({ calNum2: '' })
this.setData({ calIconEQ: '' })
}
// 如果界面为空
if (!data.calNum1 && !data.calIcon) {
this.setData({ calNum1: data.resultNum });
}
this.setData({ calIcon: '÷' });
data.lastbtn = 1; //更新最新按下按钮
},
// 乘法
multiplication () {
let data = this.data;
if (data.lastbtn == 1 && data.calIcon == '×') return ;
// 上一次数字
if (data.lastbtn == 0) {
let res = this.compute(data.calNum1, data.resultNum, '×')
this.setData({ calNum1: res })
this.setData({ resultNum: res })
}
// 上一次等于
if (data.lastbtn == 2 && data.calIconEQ) {
this.setData({ calNum1: data.resultNum })
this.setData({ calNum2: '' })
this.setData({ calIconEQ: '' })
}
// 如果界面为空
if (!data.calNum1 && !data.calIcon) {
this.setData({ calNum1: data.resultNum });
}
this.setData({ calIcon: '×' });
data.lastbtn = 1; //更新最新按下按钮
},
// 减法
reduce() {
let data = this.data;
if (data.lastbtn == 1 && data.calIcon == '-') return ;
// 上一次数字
if (data.lastbtn == 0) {
if (!data.calNum1 && !data.calIcon) {
this.setData({ calNum1: data.resultNum })
} else {
let res = this.compute(data.calNum1, data.resultNum, '-')
this.setData({ calNum1: res })
this.setData({ resultNum: res })
}
}
// 上一次等于
if (data.lastbtn == 2 && data.calIconEQ) {
this.setData({ calNum1: data.resultNum })
this.setData({ calNum2: '' })
this.setData({ calIconEQ: '' })
}
// 如果界面为空
if (!data.calNum1 && !data.calIcon) {
this.setData({ calNum1: data.resultNum });
}
this.setData({ calIcon: '-' });
data.lastbtn = 1; //更新最新按下按钮
},
// 结果
equalRes() {
let data = this.data;
let res;
// 判断是否等于过
if (data.calIconEQ) {
res = this.compute(data.calNum1, data.calNum2, data.calIcon);
this.setData({ calNum1: res })
this.setData({ resultNum: this.compute(res, data.calNum2, data.calIcon) })
} else {
if (data.lastbtn == 2) return ;
// 判断有没有后一个数
if (this.data.calNum2) {} else {
if (data.lastbtn == 3) {
this.setData({ calNum1: data.resultNum })
this.setData({ calIcon: '=' });
}
if (data.lastbtn == 0 || data.lastbtn == 4) {
if (data.calIcon && data.calIcon != '=') {
res = this.compute(data.calNum1, data.resultNum, data.calIcon);
this.setData({ calNum2: data.resultNum });
this.setData({ calIconEQ: '=' });
this.setData({ resultNum: res });
} else {
this.setData({ calNum1: data.resultNum })
this.setData({ calIcon: '=' });
}
}
if (data.lastbtn == 1) {
// console.log(data.resultNum)
this.setData({ calNum1: data.resultNum })
res = this.compute(data.calNum1, data.resultNum, data.calIcon);
this.setData({ calNum2: data.resultNum })
this.setData({ resultNum: res })
this.setData({ calIconEQ: '=' });
}
}
}
data.lastbtn = 2; //更新最新按下按钮
},
compute(num1, num2, way) {
let res = 0;
switch(way) {
case '%':
if (!num1 && num1 != 0) { num1 = '1' }
if (!num2) { num2 = '1' }
res = parseFloat(num1) % parseFloat(num2); break;
case '÷':
if (!num1) { num1 = '1' }
if (!num2) { num2 = '1' }
res = parseFloat(num1) / parseFloat(num2); break;
case '×':
if (!num1) { num1 = '1' }
if (!num2) { num2 = '1' }
res = parseFloat(num1) * parseFloat(num2); break;
case '-':
if (!num1) { num1 = '0' }
if (!num2) { num2 = '0' }
res = parseInt(num1) - parseInt(num2); break;
case '+':
if (!num1) { num1 = '0' }
if (!num2) { num2 = '0' }
res = parseFloat(num1) + parseFloat(num2); break;
}
return res;
}
})
- 点赞
- 收藏
- 关注作者
评论(0)