index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>蘋果風格的計算器</title>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" media="screen and (max-width: 500px)" href="index-mobile.css">
<link rel="stylesheet" media="screen and (min-width: 501px)" href="index-pc.css">
</head>
<body>
<div class="calculator">
<div class="operation">
<div class="screen">
<div style="display: none;" class="calc-history"></div>
<div class="calc-in"></div>
<div class="calc-out active"></div>
</div>
<div class="line">
<div data-ac="cls" class="button symbol">C</div>
<div data-ac="per" class="button symbol">%</div>
<div data-ac="sq" class="button symbol">x
<span>2</span>
</div>
<div data-ac="mul" class="button operator">×</div>
</div>
<div class="line">
<div data-val="7" class="button number">7</div>
<div data-val="8" class="button number">8</div>
<div data-val="9" class="button number">9</div>
<div data-ac="div" class="button operator">÷</div>
</div>
<div class="line">
<div data-val="4" class="button number">4</div>
<div data-val="5" class="button number">5</div>
<div data-val="6" class="button number">6</div>
<div data-ac="plus" class="button operator">+</div>
</div>
<div class="line">
<div data-val="1" class="button number">1</div>
<div data-val="2" class="button number">2</div>
<div data-val="3" class="button number">3</div>
<div data-ac="minus" class="button operator">-</div>
</div>
<div class="line">
<div data-val="0" data-val="0" class="button grow number">0</div>
<div data-ac="dot" class="button operator">.</div>
<div data-ac="eq" class="button operator">=</div>
</div>
</div>
</div>
<p class="description">一個簡易計算器<br>可以進行四則運算</p>
<script src="index.js"></script>
</body>
</html>
index.js
$(function () {
class Calculator {
constructor($dom) {
this.$dom = $($dom)
this.$history = this.$dom.find('.calc-history')
this.$in = this.$dom.find('.calc-in')
this.$out = this.$dom.find('.calc-out')
this.$operation = this.$dom.find('.operation')
this.op = {
'plus': '+',
'minus': '-',
'mul': '*',
'div': '/'
}
this.opArr = ['+', '-', '*', '/']
this.infix = []
this.suffix = []
this.result = []
this.lastVal = 0
this.calcDone = false
this.curDot = false
this.init()
}
init() {
this.bindEvents()
}
bindEvents() {
this.$operation.on('click', (e) => {
e = e || window.event
let elem = e.target || e.srcElement
let val = elem.getAttribute('data-val') || elem.getAttribute('data-ac')
let action
if (val) {
if (!isNaN(parseInt(val, 10))) {
let num = parseInt(val, 10)
let infixRe = this.buildInfix(num,'add')
console.log('中綴表示式--',infixRe)
this.$in.text(infixRe.join('')).addClass('active')
this.calculate()
return
}
action = val
if (['cls','del','eq'].indexOf(action) !== -1) {
console.log('點選了運算子')
console.log(action)
if (!this.infix.length) {
console.log('中綴表示式不存在就點了運算子')
return
}
if (action === 'cls' || (action === 'del' && this.calcDone)) {
console.log('清空運算區域')
this.$in.text('')
this.$out.text('')
this.resetData()
}
else if (action === 'del') {
return
console.log('清除鍵')
console.log(this.op[action])
let infixRe = this.buildInfix(this.op[action],'del')
this.$in.text(infixRe.join('')).addClass('active')
this.calculate()
}
else if (action === 'eq') {
this.calculate('eq')
}
}
else if (['per','dot','sq'].indexOf(action) !== -1) {
if (!this.infix.length || this.isOp(this.lastVal)) {
return
}
if (action === 'per') {
this.lastVal /= 100
} else if (action === 'dot') {
this.curDot = true
} else if (action === 'sq') {
this.lastVal *= this.lastVal
}
let infixRe = this.buildInfix(this.lastVal,'change')
this.$in.text(infixRe.join('')).addClass('active')
this.calculate()
}
else if (this.isOp(this.op[action])) {
if (!this.infix.length && (this.op[action] === '+' || this.op[action] === '/')) {
return
}
console.log(action)
let infixRe = this.buildInfix(this.op[action],'add')
console.log(infixRe)
this.$in.text(infixRe.join('')).addClass('active')
}
}
})
}
buildInfix(val, type) {
console.log('構建中綴表示式')
console.log(val)
console.log(type)
if (this.calcDone) {
console.log('直接點選運算之後')
this.calcDone = false
if (!this.isOp(val)) {
this.resetData()
} else {
let re = this.result[0]
this.resetData()
this.infix.push(re)
}
}
let newVal
if (type === 'del') {
console.log('刪除操作')
newVal = this.infix.pop()
console.log('新值為--',newVal)
newVal = Math.floor(newVal / 10)
if (newVal) {
this.infix.push(newVal)
}
this.lastVal = this.infix[this.infix.length - 1]
return this.infix
}
else if (type === 'add') {
console.log('新增操作')
console.log(val)
console.log(this.lastVal)
console.log(this.isOp(val))
console.log(this.isOp(this.lastVal))
if (this.isOp(val) && this.isOp(this.lastVal)) {
console.log('運算子')
console.log(this.infix)
return this.infix
}
else if (!this.isOp(val) && !this.isOp(this.lastVal)) {
console.log('兩個數字')
newVal = this.lastVal * 10 + val
this.infix.pop()
this.infix.push(this.lastVal = newVal)
return this.infix
}
if (!this.isOp(val) && this.infix.length === 1 && (this.lastVal === '+' || this.lastVal === '-')) {
console.log('首個數字正負數')
newVal = this.lastVal === '+' ? val : 0 - val
this.infix.pop()
this.infix.push(this.lastVal = newVal)
return this.infix
}
this.infix.push(this.lastVal = val)
return this.infix
}
else if (type === 'change') {
console.log('更改操作')
this.infix.pop()
this.infix.push(this.lastVal = val)
return this.infix
}
}
infix2suffix() {
let temp = []
this.suffix = []
for (let i =0;i < this.infix.length;i++) {
if (!this.isOp(this.infix[i])) {
this.suffix.push(this.infix[i])
} else {
if (!temp.length) {
temp.push(this.infix[i])
} else {
let opTop = temp[temp.length-1]
if (!this.priorHigher(opTop,this.infix[i])) {
while (temp.length && !this.priorHigher(opTop,this.infix[i])) {
this.suffix.push(temp.pop())
opTop = temp[temp.length-1]
}
}
temp.push(this.infix[i])
}
}
}
while (temp.length) {
this.suffix.push(temp.pop())
}
}
calcSuffix() {
this.result = []
for (let i =0;i < this.suffix.length;i++) {
if (!this.isOp(this.suffix[i])) {
this.result.push(this.suffix[i])
} else {
this.result.push(this.opCalc(this.result.pop(),this.suffix[i],this.result.pop()))
}
}
return this.result[0]
}
isOp(val) {
return val && this.opArr.indexOf(val) !== -1
}
priorHigher(a, b) {
return (a === '+' || a === '-') && (b === '*' || b === '/')
}
opCalc(b, op, a) {
return op === '+' ?
a + b :
op === '-' ?
a - b :
op === '*' ?
a * b :
op === '/' ?
a / b :
0
}
calculate(type) {
this.infix2suffix()
let suffixRe = this.calcSuffix()
if (suffixRe) {
this.$out.text('=' + suffixRe)
.attr('title',suffixRe)
.removeClass('active')
if (type === 'eq') {
this.$in.removeClass('active')
this.$out.addClass('active')
this.calcDone = true
this.lastVal = suffixRe
let history = this.infix.join('') + ' = ' + suffixRe
this.$history.text(history)
.attr('title',history)
}
}
}
resetData() {
this.infix = []
this.suffix = []
this.result = []
this.lastVal = 0
this.curDot = false
}
}
let calculator = new Calculator('.calculator')
console.log(calculator)
})
index-mobile.css
body {
margin: 0;
padding: 0;
}
div {
box-sizing: border-box;
}
.description {
display: none;
}
.calculator {
width: 100vw;
height: 100vh;
background: black;
position: relative;
}
.screen {
width: 100%;
margin-bottom: 24px;
}
.screen>div {
display: flex;
justify-content: flex-end;
align-items: center;
padding: 4px 4vw;
font-size: 28px;
color: #aaa;
margin-bottom: 4px;
}
.screen>div.active {
color: red;
}
.operation {
width: 100%;
position: absolute;
bottom: 0;
}
.line {
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
margin: 4px 0;
}
.button {
background: #999;
width: 20vw;
height: 20vw;
position: relative;
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
font-size: 36px;
color: white;
}
.button.grow {
width: 46vw;
height: 20vw;
border-radius: 18vw;
}
.button:hover {
color: red;
}
.button.symbol {
color: black;
}
.button.number {
background: #333;
}
.button.operator {
background: orange;
}
.button>span {
font-size: 18px;
position: absolute;
left: 62%;
top: 16%;
}
index-pc.css
body {
margin: 0;
padding: 0;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
background: skyblue;
}
div {
box-sizing: border-box;
}
.description {
font-size: 36px;
margin-left: 12px;
}
.calculator {
width: 375px;
height: 580px;
background: black;
position: relative;
border-radius: 12px;
}
.screen {
width: 100%;
margin-bottom: 24px;
}
.screen>div {
display: flex;
justify-content: flex-end;
align-items: center;
padding: 4px 4vw;
font-size: 28px;
color: #aaa;
margin-bottom: 4px;
}
.screen>div.active {
color: red;
}
.operation {
width: 100%;
position: absolute;
bottom: 0;
}
.line {
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
margin: 4px 0;
}
.button {
background: #999;
width: 75px;
height: 75px;
position: relative;
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
font-size: 36px;
color: white;
}
.button.grow {
width: 172.5px;
height: 75px;
border-radius: 18vw;
}
.button:hover {
color: red;
cursor: pointer;
}
.button.symbol {
color: black;
}
.button.number {
background: #333;
}
.button.operator {
background: orange;
}
.button>span {
font-size: 18px;
position: absolute;
left: 62%;
top: 16%;
}
截圖