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">&times;</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">&divide;</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 () {
    // console.log('文档加载成功')
    // $('.operation').on('click', (e) => {
    //     e = e || window.event
    //     let elem = e.target || e.srcElement
    //     let val = elem.getAttribute('data-val') || elem.getAttribute('data-ac')
    //     console.log(val)
    //     if (val) {
    //         console.log(val)
    //     }
    // })
    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) {
                    // 数字 0-9
                    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
                }
                // // TODO:小数点运算
                // else if (this.isOp(val)) {
                //     this.curDot = false
                // }
                // // 小数点
                // if (this.curDot) {
                //     let dotLen = 0
                //     newVal = this.infix.pop()
                //     dotLen = newVal.toString().split('.')
                //     dotLen = dotLen[1] ? dotLen[1].length : 0
                //     newVal += val / Math.pow(10, dotLen + 1)
                //     // 修正小数点运算精确值
                //     newVal = parseFloat(newVal.toFixed(dotLen + 1))
                //     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 {
                    // temp数组为空遇到操作符时直接压入
                    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 {
    /* flex-grow: 1; */
    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 {
    /* flex-grow: 2; */
    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;
    /* background: red; */
    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 {
    /* flex-grow: 1; */
    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 {
    /* flex-grow: 2; */
    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;
    /* background: red; */
    position: absolute;
    left: 62%;
    top: 16%;
}

截图

在这里插入图片描述


本文转载:CSDN博客