今天继续玩一些微信小程序的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源码下载地址 有什么问题欢饮大家留言指出。