资讯详情

用React做一个新拟态计算器

React Calculator

首先,先看结果(codepen):

react-calculator.png

我在项目中使用的技术:

  • HTML、CSS、JavaScript三件套
  • React
  • SCSS

本项目是freeCodeCamp建议自学的学生免费学习编程 - Python、JavaScript、Java、Git 等 (freecodecamp.org)

该计算器项目需求清晰,具体可见:前端开发库项目 - 构建一个 JavaScript 计算器 | 学习 | freeCodeCamp.org

以下是我的一般需求:

  1. 计算器要有最基本的按钮,比如AC=、10个数字按钮0-9, - * / 按钮,小数点.
  2. 其次,计算器应该有显示数据计算过程的地方,一个实时输入区和一个临时存储区。
  3. 在实时输入区域输入数字
  4. AC实时输入区和临时存储区的内容可以初始化,即0
  5. 实现加、减、乘、除操作功能,需要和=一起实现
  6. 如果在按下 = 符号继续按计算符后,应在上次计算结果的基础上进行新的计算。
  7. - 不仅是减号,还可以作为负数符号
  8. . 实现小数功能
  9. 最后需要注意的是,本计算器项目选择了

CodePen

我选择在这里使用codepen,它可以很方便地调试页面,也可以很容易地引入第三方框架,并且可以免费使用!

首先,你需要创建一个账户,然后创建一个普通的账户pen。 然后设置和引入项目所需的第三方库:

  • 打开设置,将css设置为编译器SCSS
  • 由于要使用react,所以要将js设置为编译器Bable
  • 之后在 Add External Scripts/Pens搜索并加入 reactreact-dom

到目前为止,项目的基本设置已经完成。

HTML架构

因为用的是React,所以HTML只需要给个div挂上标签

这里我将 id 设为

<div id="app"></div> 

CSS样式

按钮的宽度和长度有比例关系,非常适合使用grid目前,主流浏览器支持布局grid布局。按钮是5*每个按钮的宽度为父元素的25%,高度为70%px;

.btn-box { 
             display: grid;     grid-template-columns: repeat(4, 25%);     grid-template-rows: repeat(5, 70px); } 

其它款式可以自由发挥,这里就不细说了;

可参考新拟态风格代码:neumorphism.io,根据自己的喜好。

我的。贴在这里scss代码:

body {   background-color: #2233dd; } #app {   height: 95vh;   display:flex;   align-items: center;   justify-content: center; } .app {   user-select: none;   background: #2233dd;   box-shadow:  20px 20px 53px #1a27aa,              -20px -20px 53px #2a3fff;   width: 320px;    border-radius: 5px;   border: 5px solid #23d;   color: white;   .btn-box {     display: grid;     margin-top: 5px;     grid-template-columns: repeat(4, 25%);     grid-template-rows: repeat(5, 70px);     button {       cursor: pointer;       border: none;       outline: none;       border-radius: 0px;       background: #e0e0e0;       padding: 5px 10px;       background-color: #23d;       color: white;       font-size: 24px;       box-sizing: border-box;       &:active {         box-shadow: inset 5px 5px 16px #1e2cc0,inset -5px -5px 16px #263afa;       }       &:hover {         border: 1px solid #55f;         // border-radius: 5px;         background: linear-gradient(145deg, #23d, #1f2ec7);       }       &#clear {         grid-column-start: span 2;       }       &#zero {          grid-column-start: span 2;       }       &#equals {          grid-row-start: span 2;      }
      &#subtract, &#add, &#equals {
        border-left: 1px solid rgba(100,100,240, 1);
      }
      &#clear, &#divide {
        border-bottom: 1px solid rgba(100,100,240, 1);
      }
    }
  }
  .show {
    text-align: right;
  }
  .stag-area {
    // padding: 5px 0;
    font-size: 14px;
    color: #aaa;
  }
  .input-area {
    font-size: 22px;
    padding: 5px 0;
    border-bottom: 1px solid #66f;
  }
}

具体代码也可以去我的codepen查看。

React架构

首先,创建Calculator组件类进行计算器的渲染,包括实时输入区、暂存区以及所有的按钮。 同时,设置state保存必要的数据:

  • input保存当前输入的数字或运算符,即实时输入区的内容。
  • stag保存暂存区的数据
  • operator保存当前所使用的运算符
  • isCalculated保存是否已经计算
  • isNegative保存-是负号还是运算符

全局变量operat保存所有的运算符和00比较特殊,由于要做的计算器是,按下运算符时要判断是否已经计算过。如果是计算过的结果显示在实时输入区,那么这时按下0就不是在数字后面加个0,而是将整个实时输入区变为0)。

这里没有把button当做一个子组件来写,因为封装成一个子组件

最后使用render函数将DOM渲染进页面:ReactDOM.render(<Calculator />,document.getElementById("app"));

/* globals React, ReactDOM */
const operat = ['+','-','/','x','0'];
class Calculator extends React.Component { 
        
  constructor(props) { 
        
    super(props);
    this.state = { 
        
      input: '0',
      stag: '0',
      operator: '',
      isCalculated: false,
      isNegative: false
    }
  }
  render() { 
        
    return (
      <div className="app">
        <div className="stag-area show">{ 
         this.state.stag }</div>
        <div className="input-area show" id="display">{ 
         this.state.input }</div>
        <div className="btn-box">
          <button id="clear">AC</button>
          <button id="divide">/</button>
          <button id="multiply">x</button>
          
          <button id="seven">7</button>
          <button id="eight">8</button>
          <button id="nine">9</button>
          <button id="subtract">-</button>
          
          <button id="four">4</button>
          <button id="five">5</button>
          <button id="six">6</button>
          <button id="add">+</button>
          
          <button id="one">1</button>
          <button id="two">2</button>
          <button id="three">3</button>
          <button id="equals">=</button>
          
          <button id="zero">0</button>
          <button id="decimal">.</button>
        </div>
      </div>
    )
  }
}
// 渲染DOM
ReactDOM.render(<Calculator />,document.getElementById("app"));

按钮点击事件

接下来为每个按钮添加事件。

数字 1-9

首先,1-9这9个数字的逻辑是一样的,点击直接输入对应的数字;反映在实时输入区就是在数字后面添加对应的字符。 在Calculator类中添加函数getNumber(number) { }

getNumber(number) { 
        
  this.setState({ 
        
    input: number,
  })
}

并且为这9个数字按钮添加对应的点击事件,如:onClick={ ()=> this.getNumber('9') } 。需要注意的是,需要绑定this的指向:this.getNumber = this.getNumber.bind(this);

加、减、乘、除按钮

因为四则运算都是二元运算符,所以它们的逻辑是相通的,即按下加/减/乘/除按钮后,将之前的数字保存到暂存区,然后等待第二个数字输入之后点击=之后进行计算。

细节:点击按钮后,暂存区的文字是第一个数字+运算符,输入区也变成该运算符,并且将对应的this.operator标记为该运算符,代码如下:

add() { 
        
  this.setState(state => { 
        
    return { 
         
      input: '+',
      stag: state.input + '+',
      operator: '+'
    }
  })
}
subtract() { 
        
  this.setState(state => { 
        
    return { 
         
      input: '-',
      stag: state.input + '-',
      operator: '-'
    }
  })
}
multiply() { 
        
  this.setState(state => { 
        
    return { 
         
      input: 'x',
      stag: state.input + 'x',
      operator: '*'
    }
  })
}
divide() { 
        
  this.setState(state => { 
        
    return { 
         
      input: '/',
      stag: state.input + '/',
      operator: '/'
    }
  })
}

最后,不要忘了为这4个按钮添加对应的点击事件和绑定this的指向!

实现四则运算

加、减、乘、除按钮点击事件写好后,就需要写=的点击事件,所有的运算都在该点击事件里。

根据this.operator的值判断运算类型,分类进行运算,按下=后进行运算,运算结果显示在实时输入区和暂存区,并标记为已运算isCalculated: true

 calculator() { 
        
    const num2 = parseFloat(this.state.input);
    const num1 = parseFloat(this.state.stag);
    switch(this.state.operator) { 
        
      case '+': this.setState(state => { 
        
        const ans = num1 + num2;
        return { 
        
          input: ans,
          stag: state.stag + state.input + '=' + ans,
          isCalculated: true
        }
      });
        break;
      case '-': this.setState(state => { 
        
        const ans = num1 - num2;
        return { 
        
          input: ans,
          stag: state.stag + state.input + '=' + ans,
          isCalculated: true
        }
      });
        break;
      case '/': this.setState(state => { 
        
        const ans = num1 / num2;
        return { 
        
          input: ans,
          stag: state.stag + state.input + '=' + ans,
          isCalculated: true
        }
      });
        break;
      case '*': this.setState(state => { 
        
        const ans = num1 * num2;
        return { 
        
          input: ans,
          stag: state.stag + state.input + '=' + ans,
          isCalculated: true
        }
      });
        break;
      default: break;
  }
}

清除按钮

AC按钮会将一切重置为初始状态:

clearInput() { 
        
  this.setState({ 
        
    input: '0',
    stag: '0',
    operator: '',
    isCalculated: false,
    isNegative: false
  })
}

按钮0

getZero(){ 
        
  if(this.state.isCalculated || this.state.input !== '0' ) { 
        
    // 如果是运算过的或者是0,则按下0,实时输入区应直接变为0
    this.setState({ 
        
      input: '0',
      isCalculated: false
    })
  } else { 
        
    // 正常情况下直接在后面加0
    this.setState(state => ({ 
        
      input: state.input + '0',
      isCalculated: false
    }))
  }
}

最后,为这个按钮添加对应的点击事件和绑定this的指向!

立即执行逻辑

现在我们的计算器应用已经可以进行简单的四则运算了!但是,计算器需要支持,即类似于9/3+4的运算,现在,我们的计算器并不能支持这种运算。

为了支持,就需要重写四则运算的按钮点击事件,判断一下是否要进行计算再进行后续操作,这里我的代码写的有点粗糙(能运行就行🤪):

add() { 
        
  if((this.state.stag.indexOf('+') > -1 || this.state.stag.indexOf('-') > -1 || this.state.stag.indexOf('/') > -1 || this.state.stag.indexOf('x') > -1) && this.state.stag.indexOf('=') === -1) { 
        
    this.calculator();
    this.setState(state => { 
        
      return { 
         
        input: '+',
        stag: state.input + '+',
        operator: '+'
      }
    })
  } else { 
        
    this.setState(state => { 
        
      return { 
         
        input: '+',
        stag: state.input + '+',
        operator: '+'
      }
    })
  }
}
// ... ...

- 不仅是减号,也可以作为负数符号

需要在按下-时进行判断,如果之前已经有按下过四则运算的按钮,那么这次按下-就代表是将数字变为负数而不需要后续操作(return):

if(operat.includes(this.state.input)) { 
        
  this.setState(state => ({ 
        
    isNegative: !state.isNegative
  }))
  return;
}

除了是-这个特殊情况外,在多次按下+ / *按钮只需要取最后一次按下的运算符作为最终的运算符即可!

例如在除法运算函数前加入判断,其它同理:

if(operat.includes(this.state.input)) { 
        
  this.setState(state => ({ 
        
    input: '/',
    stag: state.stag.slice(0,-1) + '/',
    operator: '/',
    isNegative: false
  }))
  return;
}

实现小数功能

最后,考虑 . 小数功能;

首先写.按钮点击函数,当是已经计算过了,点击.就直接变成0.即可;然后,当输入框内是数字且没有.时,即可将.直接添加到末尾。

getDecimal() { 
        
  if(this.state.isCalculated) { 
        
    this.setState({ 
        
      input: '0.',
      isCalculated: false
    })
  } else if(Number.isInteger(+this.state.input) && this.state.input.indexOf('.') === -1) { 
        
    this.setState(state => ({ 
        
      input: state.input + '.'
    }))
  }
}

接着,需要重写=的点击函数,用于兼容有小数的情况,加个if判断是否为整数即可:

calculator() { 
        
  let num1,num2;
  if(this.state.stag.indexOf('.') > -1) { 
        
    num2 = parseFloat(this.state.input);
    num1 = parseFloat(this.state.stag);
  } else { 
        
    num2 = parseInt(this.state.input);
    num1 = parseInt(this.state.stag);
  }
  switch(this.state.operator) { 
        
    case '+': this.setState(state => { 
        
      return { 
        
        input: num1 + num2,
        stag: state.stag + state.input + '=' + (num1 + num2),
        isCalculated: true
      }
    });
      break;
    case '-': this.setState(state => { 
        
      return { 
        
        input: num1 - num2,
        stag: state.stag + state.input + '=' + (num1 - num2),
        isCalculated: true
      }
    });
      break;
    case '/': this.setState(state => { 
        
      const ans = num1 / num2;
      return { 
        
        input: ans,
        stag: state.stag + state.input + '=' + ans,
        isCalculated: true
      }
    });
      break;
    case '*': this.setState(state => { 
        
      const ans = num1 * num2;
      return { 
        
        input: ans,
        stag: state.stag + state.input + '=' + ans,
        isCalculated: true
      }
    });
      break;
    default: break;
  }
}

到这里,基本的功能已经全都实现,测试案例全部通过!🎉

最后的最后,完整代码可以在CodePen找到。

标签: 继电器rs1a23d25

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台