React + Vite 手写仿桌面窗口加减法计算器:拖动、最小化、键盘输入、历史记录
项目简介
这是一个基于 Vite + React 的加减法计算器,重点不是“会算加减法”,而是把浏览器页面做成一个完整的“桌面窗口”体验:
- 计算器可以自由拖动。
- 标题栏有最小化和关闭按钮。
- 关闭后可以在底部任务栏重新打开。
- 支持鼠标点击和键盘输入。
- 保留最近 12 条历史记录。
核心状态设计
计算器不是一堆分散的useState,而是围绕一次计算过程设计的:
input -> 当前显示的数字字符串 operand1 -> 第一个操作数 operator -> add / subtract justEvaluated-> 刚按下等于,再次输入数字时是否覆盖显示关键交互:
- 输入数字时,最多 14 位。
- 按下运算符时,如果上一组操作还没结算,先自动结算再继续。
- 按
=时把表达式写进历史记录,并把结果作为下一次计算的起点。 - 所有结果用
toFixed(10)后再转回浮点数,避免0.1 + 0.2这类浮点误差直接展示。
核心计算逻辑:
function compute(a, b, operator) { return operator === 'add' ? a + b : a - b } function pressOperator(nextOperator) { const current = parseInput(input) if (operand1 !== null && operator !== null) { const result = roundResult(compute(operand1, current, operator)) setOperand1(result) setInput(formatNumber(result)) } else { setOperand1(current) } setOperator(nextOperator) setInput('0') setJustEvaluated(false) } function evaluate() { if (operand1 === null || operator === null) return const second = parseInput(input) const result = roundResult(compute(operand1, second, operator)) const expression = `${formatNumber(operand1)} ${operatorSymbol(operator)} ${formatNumber(second)}` setHistory((current) => [{ id: Date.now(), expression, result: formatNumber(result) }, ...current].slice(0, 12), ) setInput(formatNumber(result)) setOperand1(result) setOperator(null) setJustEvaluated(true) }窗口拖动怎么实现
窗口拖动使用 Pointer Events:
- 在标题栏按下时记录鼠标相对窗口左上角的偏移。
- 移动时根据鼠标位置计算新坐标。
- 用
clamp把窗口限制在屏幕范围内,不允许拖出桌面。 - 每次操作提升
zIndex,让当前窗口保持最前。
function startDrag(event) { if (event.target.closest('.titlebar__controls')) return bringToFront() const rect = windowRef.current.getBoundingClientRect() dragOffset.current = { x: event.clientX - rect.left, y: event.clientY - rect.top, } isDragging.current = true event.currentTarget.setPointerCapture(event.pointerId) } function moveDrag(event) { if (!isDragging.current || !windowRef.current) return const rect = windowRef.current.getBoundingClientRect() const maxX = Math.max(8, window.innerWidth - rect.width - 8) const maxY = Math.max(8, window.innerHeight - rect.height - 56) setWindowPos({ x: clamp(event.clientX - dragOffset.current.x, 8, maxX), y: clamp(event.clientY - dragOffset.current.y, 8, maxY), }) }键盘支持
页面挂在window上的keydown监听器,让计算器不需要鼠标也能完整使用:
0-9:输入数字.:小数点+/-:选择运算Enter/=:计算Backspace:退格Escape/Delete/C:全部清空
界面布局
界面主要由三部分组成:
- 标题栏:应用名、最小化、关闭。
- 显示区:上一行显示表达式,下一行显示当前值。
- 按键区:数字、加减、退格、清空、等于。
底部还有一个任务栏,显示计算器按钮和当前时间,让“关闭窗口再重新打开”的交互更像真实桌面。
运行方式
npminstallnpmrun dev生产构建:
npmrun build可扩展方向
- 增加乘除和括号,扩展成完整四则运算。
- 增加百分比、正负号、平方根等科学计算按键。
- 历史记录增加一键回填,点击某条历史结果继续计算。
- 增加深色主题和不同桌面壁纸。