在简易计算器基础上扩展科学计算功能,首先要明确需要添加哪些数学运算。常见的科学计算功能包括三角函数(sin/cos/tan)、对数函数(log/ln)、指数运算(x^y)、开方运算(√)、阶乘(n!)等。这些功能可以满足学生、工程师等用户群体的复杂计算需求。
我建议将科学计算功能分为三类布局:基础运算区、科学函数区和高级运算区。基础运算区保留原有加减乘除功能;科学函数区放置三角函数、对数等按钮;高级运算区则安排指数、开方等操作。这样的分区设计既保持了界面整洁,又方便用户快速定位所需功能。
实际开发中,我发现科学计算器最需要关注的是运算优先级问题。比如sin(30)+cos(60)这样的表达式,需要确保先计算三角函数再进行加法运算。这比简易计算器单纯处理四则运算要复杂得多,需要设计更完善的表达式解析算法。
科学计算器的按钮数量明显增多,传统的4×5网格布局可能不够用。我尝试过几种方案后,发现折叠式面板最适合移动端:默认显示基础计算器,点击"科学"按钮展开第二排功能按钮。这样既节省空间,又保持了操作便捷性。
WXML代码可以这样调整:
xml复制<view class='mode-switch'>
<button bindtap="toggleScientific">科学模式</button>
</view>
<view class='scientific-panel' wx:if="{{isScientific}}">
<!-- 科学函数按钮 -->
<view class='btnGroup'>
<view class='item purple' bindtap='clickButton' id="sin">sin</view>
<view class='item purple' bindtap='clickButton' id="cos">cos</view>
<view class='item purple' bindtap='clickButton' id="tan">tan</view>
<view class='item purple' bindtap='clickButton' id="log">log</view>
</view>
</view>
样式方面需要新增科学函数按钮的专用样式:
css复制.purple {
background: #D8BFD8;
color: #000;
}
.scientific-panel {
transition: all 0.3s ease;
}
实测发现,过渡动画能让模式切换更自然。建议给.scientific-panel添加transition属性,让面板平滑展开收起,提升用户体验。
科学计算器的难点在于表达式解析。我采用分治法处理:先将表达式转换为逆波兰表示法(RPN),再通过栈结构计算。这种方法能完美处理运算符优先级和函数嵌套。
在JS中新增科学计算函数:
javascript复制// 计算三角函数
calculateTrig: function(func, angle) {
const rad = angle * Math.PI / 180;
switch(func) {
case 'sin': return Math.sin(rad);
case 'cos': return Math.cos(rad);
case 'tan': return Math.tan(rad);
}
},
// 处理指数运算
calculatePower: function(base, exponent) {
return Math.pow(base, exponent);
},
// 阶乘计算
factorial: function(n) {
if (n < 0) return NaN;
let result = 1;
for(let i=2; i<=n; i++) result *= i;
return result;
}
处理用户输入时需要特别注意函数参数的提取。比如当用户输入"sin(30)+1"时,需要先识别出"sin(30)"这部分,计算后再进行加法运算。我采用正则表达式来匹配函数模式:
javascript复制const funcRegex = /(sin|cos|tan|log|ln)\(([^)]+)\)/g;
let match;
while ((match = funcRegex.exec(expr)) !== null) {
const func = match[1];
const arg = match[2];
const value = this.calculateFunction(func, arg);
expr = expr.replace(match[0], value);
}
科学计算器的状态管理更复杂,需要跟踪当前模式(基础/科学)、运算中间结果等。我建议使用Redux-like的状态管理方案:
javascript复制// 在Page的data中新增
data: {
isScientific: false,
currentMode: 'basic', // 'basic'或'scientific'
memoryValue: 0, // 内存存储值
lastOperation: null, // 记录上次操作
error: null // 错误信息
},
// 模式切换
toggleScientific: function() {
this.setData({
isScientific: !this.data.isScientific,
currentMode: this.data.isScientific ? 'basic' : 'scientific'
});
}
错误处理尤为重要,科学计算中可能遇到除零、负数开方等问题。我采用try-catch包裹核心计算逻辑:
javascript复制try {
result = this.calculate(expression);
if (isNaN(result)) throw '非法计算结果';
this.setData({ result, error: null });
} catch (err) {
this.setData({
result: '0',
error: typeof err === 'string' ? err : '计算错误'
});
}
界面层需要显示错误信息,可以在WXML中添加错误提示区域:
xml复制<view class="error-message" wx:if="{{error}}">
{{error}}
</view>
随着功能增加,计算器可能出现性能问题。我总结了几点优化经验:
javascript复制// 不好的做法
this.setData({ a: 1 });
this.setData({ b: 2 });
// 推荐做法
this.setData({
a: 1,
b: 2
});
javascript复制const cache = {};
function cachedSin(x) {
if (!cache[x]) {
cache[x] = Math.sin(x);
}
return cache[x];
}
javascript复制// 计算密集型任务分片执行
function chunkedCalculate() {
// 每次处理一部分计算
if (moreWork) {
setTimeout(chunkedCalculate, 0);
}
}
javascript复制// 创建Worker
const worker = wx.createWorker('workers/calculator.js');
// 主线程发送计算任务
worker.postMessage({
type: 'calculate',
expression: 'sin(30)+log(100)'
});
// 接收计算结果
worker.onMessage(function(res) {
this.setData({ result: res.result });
});
科学计算器需要更全面的测试用例。我建议建立测试矩阵,覆盖各种边界条件:
| 测试类型 | 测试用例 | 预期结果 |
|---|---|---|
| 三角函数 | sin(90) | 1 |
| 对数运算 | log(100) | 2 |
| 组合运算 | 2^3+1 | 9 |
| 错误处理 | 1/0 | "除零错误" |
| 嵌套函数 | sin(cos(0)) | 0.8415 |
小程序开发工具提供了很好的调试支持。我常用的调试技巧包括:
对于科学计算这种精度敏感的场景,要特别注意浮点数精度问题。我通常会添加精度处理函数:
javascript复制function roundToPrecision(num, precision = 10) {
return parseFloat(num.toPrecision(precision));
}
完成基础科学计算功能后,可以考虑添加以下实用功能:
javascript复制// 在data中新增
history: [],
// 计算完成后保存
addToHistory: function(expr, result) {
const newHistory = [{expr, result}, ...this.data.history.slice(0, 9)];
this.setData({ history: newHistory });
}
javascript复制data: {
angleMode: 'deg' // 'deg'或'rad'
},
toggleAngleMode: function() {
this.setData({
angleMode: this.data.angleMode === 'deg' ? 'rad' : 'deg'
});
}
css复制/* 深色主题 */
.dark-theme {
background: #333;
color: #fff;
}
.dark-theme .item {
background: #555;
color: #fff;
}
javascript复制// 在页面配置中启用手势
onSwipe: function(e) {
if (e.direction === 'left') {
this.toggleScientific();
}
}
实现这些功能时,要注意保持代码的可维护性。我习惯将不同功能模块拆分到单独的文件中,通过require引入。比如将科学计算函数单独放在scientific.js中:
javascript复制// scientific.js
module.exports = {
calculateSin: function(angle) {
// sin计算实现
},
// 其他科学函数...
};
// 在页面JS中引入
const scientific = require('../../utils/scientific');