小应用与小游戏

简易计算器

支持键盘输入和连续运算的基础四则计算器。

HTMLCSSJavaScript计算器
启用 JavaScript 后可编辑、运行和保存此案例。固定源码如下:
<!doctype html>
<html lang="zh-Hans" dir="ltr">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>简易计算器</title>
  <style>
    * { box-sizing: border-box; }
    body { min-height: 100vh; margin: 0; display: grid; place-items: center; padding: 24px; color: #282720; font-family: "Courier New", monospace; background: #cfd5c5; background-image: linear-gradient(135deg, rgba(255,255,255,.25) 25%, transparent 25%), linear-gradient(315deg, rgba(255,255,255,.25) 25%, transparent 25%); background-size: 30px 30px; }
    .calculator { width: min(370px, 100%); padding: 22px; border: 2px solid #45463d; border-radius: 18px 18px 35px 35px; background: #e5dfce; box-shadow: 0 18px 0 #6d6f62, 0 32px 50px rgba(49,50,44,.28), inset 0 2px rgba(255,255,255,.7); }
    .brand { display: flex; justify-content: space-between; align-items: center; margin: 2px 4px 18px; font-size: 10px; font-weight: 800; letter-spacing: .15em; }
    .brand strong { font-size: 15px; letter-spacing: -.04em; }
    .solar { width: 90px; height: 22px; border: 2px solid #555348; border-radius: 3px; background: repeating-linear-gradient(90deg, #5c493b 0 20px, #2f2a25 21px 22px); }
    .display { min-height: 108px; padding: 16px; overflow: hidden; border: 3px inset #777969; border-radius: 8px; color: #24302b; text-align: right; background: #adbea9; box-shadow: inset 0 5px 12px rgba(42,54,48,.23); }
    .expression { height: 22px; color: #596860; font-size: 13px; }
    .value { margin-top: 8px; overflow: hidden; font-size: clamp(34px, 10vw, 48px); line-height: 1; letter-spacing: -.07em; white-space: nowrap; }
    .keys { display: grid; grid-template-columns: repeat(4, 1fr); gap: 11px; margin-top: 20px; }
    button { min-height: 56px; border: 1px solid #3c3b35; border-radius: 9px; color: #f5f1e7; font: 800 19px/1 "Courier New", monospace; background: #46463f; box-shadow: 0 5px 0 #24241f; cursor: pointer; transition: transform .08s ease, box-shadow .08s ease; }
    button:active, button.is-active { transform: translateY(4px); box-shadow: 0 1px 0 #24241f; }
    button:focus-visible { outline: 3px solid #fff; outline-offset: 2px; }
    .function { color: #282720; background: #bdb8aa; box-shadow: 0 5px 0 #77746b; }
    .operator { color: #fff8e9; background: #d06d3f; box-shadow: 0 5px 0 #8c3f20; }
    .equals { grid-row: span 2; background: #476e65; box-shadow: 0 5px 0 #29453f; }
    .zero { grid-column: span 2; }
    .note { margin: 18px 0 0; color: #747268; font-size: 10px; text-align: center; }
    @media (prefers-reduced-motion: reduce) { button { transition: none; } }
  </style>
</head>
<body>
  <main class="calculator">
    <div class="brand"><strong>NUMBER / 84</strong><span class="solar" aria-hidden="true"></span></div>
    <div class="display" aria-live="polite"><div id="expression" class="expression"></div><div id="value" class="value">0</div></div>
    <div class="keys">
      <button class="function" type="button" data-action="clear">AC</button><button class="function" type="button" data-action="sign">±</button><button class="function" type="button" data-action="percent">%</button><button class="operator" type="button" data-operator="/">÷</button>
      <button type="button" data-digit="7">7</button><button type="button" data-digit="8">8</button><button type="button" data-digit="9">9</button><button class="operator" type="button" data-operator="*">×</button>
      <button type="button" data-digit="4">4</button><button type="button" data-digit="5">5</button><button type="button" data-digit="6">6</button><button class="operator" type="button" data-operator="-">−</button>
      <button type="button" data-digit="1">1</button><button type="button" data-digit="2">2</button><button type="button" data-digit="3">3</button><button class="operator" type="button" data-operator="+">+</button>
      <button class="zero" type="button" data-digit="0">0</button><button type="button" data-action="decimal">.</button><button class="equals" type="button" data-action="equals">=</button>
    </div>
    <p class="note">支持数字键、四则运算、Enter 与 Escape</p>
  </main>
  <script>
    const valueDisplay = document.querySelector('#value');
    const expressionDisplay = document.querySelector('#expression');
    let current = '0';
    let stored = null;
    let operator = null;
    let replaceCurrent = false;

    const symbols = { '+': '+', '-': '−', '*': '×', '/': '÷' };
    function render() {
      valueDisplay.textContent = current;
      expressionDisplay.textContent = stored === null ? '' : `${stored} ${symbols[operator] || ''}`;
    }
    function inputDigit(digit) {
      if (replaceCurrent || current === '0' || current === '错误') current = digit;
      else if (current.replace('-', '').replace('.', '').length < 10) current += digit;
      replaceCurrent = false;
      render();
    }
    function calculate(a, b, operation) {
      if (operation === '+') return a + b;
      if (operation === '-') return a - b;
      if (operation === '*') return a * b;
      if (operation === '/') return b === 0 ? null : a / b;
      return b;
    }
    function formatResult(number) {
      if (number === null || !Number.isFinite(number)) return '错误';
      return String(Number(number.toPrecision(10)));
    }
    function chooseOperator(nextOperator) {
      if (operator && !replaceCurrent) {
        current = formatResult(calculate(Number(stored), Number(current), operator));
        stored = current === '错误' ? null : Number(current);
      } else stored = Number(current);
      operator = nextOperator;
      replaceCurrent = true;
      render();
    }
    function equals() {
      if (!operator || stored === null) return;
      current = formatResult(calculate(Number(stored), Number(current), operator));
      stored = null; operator = null; replaceCurrent = true; render();
    }
    function runAction(action) {
      if (action === 'clear') { current = '0'; stored = null; operator = null; replaceCurrent = false; }
      if (action === 'sign' && current !== '0' && current !== '错误') current = current.startsWith('-') ? current.slice(1) : `-${current}`;
      if (action === 'percent' && current !== '错误') current = formatResult(Number(current) / 100);
      if (action === 'decimal' && !current.includes('.')) { current = replaceCurrent ? '0.' : `${current}.`; replaceCurrent = false; }
      if (action === 'equals') equals();
      render();
    }
    document.querySelector('.keys').addEventListener('click', event => {
      const button = event.target.closest('button');
      if (!button) return;
      if (button.dataset.digit) inputDigit(button.dataset.digit);
      else if (button.dataset.operator) chooseOperator(button.dataset.operator);
      else runAction(button.dataset.action);
    });
    document.addEventListener('keydown', event => {
      if (/^[0-9]$/.test(event.key)) inputDigit(event.key);
      else if ('+-*/'.includes(event.key)) chooseOperator(event.key);
      else if (event.key === '.' || event.key === ',') runAction('decimal');
      else if (event.key === 'Enter' || event.key === '=') runAction('equals');
      else if (event.key === 'Escape') runAction('clear');
    });
    render();
  </script>
</body>
</html>
页面反馈发现问题或有改进建议?
反馈功能需要在在线版使用

请在在线版启用 JavaScript 后提交;当前工具或案例的本地功能不受影响。

前往在线版反馈