React学习笔记——实现 TodoList+Calculator

 2024-12-04 13:40 

一、Calculator:

【React】基于 styled-components 拟物3D按键效果的计算器

#代码仓库:
GitHub:
https://github.com/Tully-L/React-Calculator.git

#AI小黑工-Cusor

二、TodoList参考:

https://www.bilibili.com/video/BV1kH4y117yr?vd_source=ab086d60b3f40acfef7c0c8805cdbd40

#代码仓库:
GitHub:https://github.com/Archimesons/react-bilibili-tutorial
#参考资料:
1、运行时环境Node.js/npm下载入口:https://nodejs.org/en
2、IDE Visual Studio Code (vscode) 下载入口:https://code.visualstudio.com/
2、React.js中文文档:https://zh-hans.react.dev/learn
3、JavaScript中文文档:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript


三、实现截图:

第0版-第1版-第二版/最终版   #已实现基本功能


四、React-Calculator代码 

1.项目结构

1. **创建项目**

首先,打开命令行,执行以下命令:

```bash

cd E:\React

npx create-react-app react-calculator

cd react-calculator

npm start

```

2. **安装所需依赖**

```bash

npm install styled-components

```

3. **文件结构**

```

react-calculator/

├── src/

│   ├── components/

│   │   ├── Calculator.js

│   │   ├── Button.js

│   │   └── styles.js

│   ├── App.js

│   └── index.js

└── public/

└── index.html

```



2.完整代码 

```javascript:src/components/styles.js
import styled from 'styled-components';

export const CalculatorContainer = styled.div`
  width: 340px;
  margin: 100px auto;
  background: #1a1a1a;
  padding: 25px;
  border-radius: 15px;
  box-shadow: 
    0 20px 40px rgba(0,0,0,0.6),
    inset 0 2px 0 rgba(255,255,255,0.1),
    inset 0 -1px 0 rgba(0,0,0,0.8);
  border: 2px solid #333;
`;

export const DisplayContainer = styled.div`
  background: #202020;
  width: 90%;
  margin: 0 auto 25px auto;
  border-radius: 8px;
  border: 1px solid #444;
  box-shadow: 
    inset 0 2px 8px rgba(0,0,0,0.5),
    0 1px 0 rgba(255,255,255,0.1);
  overflow: hidden;
`;

export const DisplayRow = styled.div`
  height: 35px;
  padding: 0 20px;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  font-size: ${props => props.primary ? '30px' : '20px'};
  font-weight: bold;
  font-family: 'Digital-7', Consolas, monospace;
  color: ${props => props.primary ? '#00ff00' : '#00bb00'};
  text-shadow: 0 0 5px rgba(0,255,0,0.5);
`;

export const ButtonGrid = styled.div`
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 12px;
  padding: 5px;
  background: #1a1a1a;
`;

export const StyledButton = styled.button`
  position: relative;
  width: 100%;
  height: 65px;
  border: none;
  border-radius: 10px;
  cursor: pointer;
  outline: none;
  font-size: 24px;
  font-weight: bold;
  color: ${props => props.operator ? '#ff9f43' : '#ffffff'};
  background: none;
  margin-top: 5px;
  
  // 按键主体
  &::before {
    content: '';
    position: absolute;
    left: 0;
    top: 0;
    right: 0;
    bottom: 0;
    background: ${props => props.operator ? '#444' : '#3a3a3a'};
    border-radius: 10px;
    border: 2px solid rgba(255,255,255,0.1);
    transform: translateY(-8px);
    transition: all 0.1s ease-in-out;
    box-shadow: 
      0 8px 0 #222,
      0 8px 8px rgba(0,0,0,0.4),
      inset 0 2px 2px rgba(255,255,255,0.2),
      inset 0 -2px 2px rgba(0,0,0,0.2);
  }

  // 按键底部阴影
  &::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: -10px;
    height: 10px;
    background: #1a1a1a;
    border-radius: 0 0 10px 10px;
    box-shadow: 
      inset 0 -4px 8px rgba(0,0,0,0.5),
      0 2px 4px rgba(0,0,0,0.3);
  }

  span {
    position: relative;
    z-index: 1;
    display: block;
    width: 100%;
    height: 100%;
    line-height: 65px;
    text-align: center;
    transform: translateY(-8px);
    transition: all 0.1s ease-in-out;
    text-shadow: 0 -1px 0 rgba(0,0,0,0.8);
  }

  // 按下效果
  &:active::before {
    transform: translateY(-2px);
    box-shadow: 
      0 2px 0 #222,
      0 2px 4px rgba(0,0,0,0.4),
      inset 0 2px 2px rgba(255,255,255,0.2),
      inset 0 -2px 2px rgba(0,0,0,0.2);
  }

  &:active span {
    transform: translateY(-2px);
  }

  // 悬浮效果
  &:hover::before {
    background: ${props => props.operator ? '#4a4a4a' : '#404040'};
    box-shadow: 
      0 8px 0 #222,
      0 8px 10px rgba(0,0,0,0.5),
      inset 0 2px 3px rgba(255,255,255,0.2),
      inset 0 -2px 2px rgba(0,0,0,0.2);
  }
`;

# **Button组件**:

```javascript:src/components/Button.js
import React from 'react';
import { StyledButton } from './styles';

const Button = ({ value, onClick, operator }) => {
  return (
    <StyledButton operator={operator} onClick={() => onClick(value)}>
      {value}
    </StyledButton>
  );
};

export default Button;
```

#**Calculator组件**:

```javascript:src/components/Calculator.js
import React, { useState } from 'react';
import Button from './buttons';
import { CalculatorContainer, DisplayContainer, DisplayRow, ButtonGrid } from './styles';

const Calculator = () => {
    const [primaryDisplay, setPrimaryDisplay] = useState('0');
    const [secondaryDisplay, setSecondaryDisplay] = useState('');
    const [startNew, setStartNew] = useState(true);
  
    const formatNumber = (num) => {
      if (Number.isInteger(num)) return num.toString();
      const rounded = Number(parseFloat(num).toFixed(10));
      return rounded.toString();
    };
  
    const handleNumber = (num) => {
      if (startNew) {
        setSecondaryDisplay(num === '.' ? '0.' : num);
        setStartNew(false);
      } else {
        if (num === '.') {
          const lastNumber = secondaryDisplay.split(/[+\-×÷]/).pop();
          if (lastNumber.includes('.')) {
            return;
          }
          if (lastNumber === '') {
            setSecondaryDisplay(secondaryDisplay + '0.');
            return;
          }
        }
        setSecondaryDisplay(secondaryDisplay + num);
      }
    };
  
    const handleOperator = (operator, display) => {
      setStartNew(false);
      
      // 处理负号的特殊情况
      if (display === '-') {
        const lastChar = secondaryDisplay.slice(-1);
        if (secondaryDisplay === '' || '+-×÷'.includes(lastChar)) {
          // 在负号前添加左括号
          setSecondaryDisplay(secondaryDisplay + '(-');
          return;
        }
      }
      
      // 如果最后一个字符是运算符,替换它
      const lastChar = secondaryDisplay.slice(-1);
      if ('+-×÷'.includes(lastChar)) {
        setSecondaryDisplay(secondaryDisplay.slice(0, -1) + display);
      } else {
        // 如果前一个输入是右括号,不需要添加额外的右括号
        if (lastChar === ')') {
          setSecondaryDisplay(secondaryDisplay + display);
        } else {
          // 检查是否需要闭合括号
          const needClosingBracket = secondaryDisplay.includes('(-') && 
                                   !secondaryDisplay.endsWith(')');
          setSecondaryDisplay(secondaryDisplay + (needClosingBracket ? ')' : '') + display);
        }
      }
    };
  
    const handleEqual = () => {
      try {
        // 检查是否需要在计算前添加最后的右括号
        let finalExpression = secondaryDisplay;
        if (finalExpression.includes('(-') && 
            !finalExpression.endsWith(')') && 
            !'+-×÷'.includes(finalExpression.slice(-1))) {
          finalExpression += ')';
        }
  
        setPrimaryDisplay(finalExpression);
        
        // 替换乘除符号并计算
        const expression = finalExpression
          .replace(/×/g, '*')
          .replace(/÷/g, '/');
        
        const result = eval(expression);
        const formattedResult = formatNumber(result);
        setSecondaryDisplay(formattedResult);
        setStartNew(true);
      } catch (error) {
        setPrimaryDisplay('Error');
        setSecondaryDisplay('');
      }
    };
  
    const handleClear = () => {
      setPrimaryDisplay('0');
      setSecondaryDisplay('');
      setStartNew(true);
    };

  return (
    <CalculatorContainer>
      <DisplayContainer>
        <DisplayRow>{primaryDisplay}</DisplayRow>
        <DisplayRow primary>{secondaryDisplay || '0'}</DisplayRow>
      </DisplayContainer>
      <ButtonGrid>
        <Button value="7" onClick={handleNumber} />
        <Button value="8" onClick={handleNumber} />
        <Button value="9" onClick={handleNumber} />
        <Button value="÷" onClick={() => handleOperator('/', '÷')} operator />
        
        <Button value="4" onClick={handleNumber} />
        <Button value="5" onClick={handleNumber} />
        <Button value="6" onClick={handleNumber} />
        <Button value="×" onClick={() => handleOperator('*', '×')} operator />
        
        <Button value="1" onClick={handleNumber} />
        <Button value="2" onClick={handleNumber} />
        <Button value="3" onClick={handleNumber} />
        <Button value="-" onClick={() => handleOperator('-', '-')} operator />
        
        <Button value="0" onClick={handleNumber} />
        <Button value="." onClick={handleNumber} />
        <Button value="=" onClick={handleEqual} operator />
        <Button value="+" onClick={() => handleOperator('+', '+')} operator />
        
        <Button value="C" onClick={handleClear} operator />
      </ButtonGrid>
    </CalculatorContainer>
  );
};

export default Calculator;
```

#4. **App组件**:

```javascript:src/App.js
import React from 'react';
import Calculator from './components/Calculator';

function App() {
  return (
    <div className="App">
      <Calculator />
    </div>
  );
}

export default App;
```

 


3.运行项目

在命令行中执行:

```bash

npm start

```

4.实现效果

主要特点:

- 每个按键都有3D效果,点击时会有下沉感

- 运算符按钮使用橙色突出显示

- 数字按钮使用灰色

- 按钮悬停时有亮度变化

- 响应式布局

- 完整的计算器功能

使用方法:

1. 点击数字输入数值

2. 点击运算符进行运算

3. 点击等号得出结果

4. 点击C清除所有内容

修改样式:

- 可以在`styles.js`中调整颜色、阴影、大小等

- 3D效果主要通过`transform-style: preserve-3d`和`translateZ`实现

- 按钮的下沉效果通过`:active`状态和`transform`实现

需要注意的是,这个计算器使用了`eval()`函数来计算结果,在实际生产环境中应该使用更安全的计算方法。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值