CSS는 이전에 작업했던 CSS를 참조하여 작성하였고, 계산기에 맞게 적절히 조절했다.
/*
Date : 2020-05-08
Time : 10:22
File : style.css
Dir : D:\dev\calc
*/
html {
box-sizing: border-box;
}
*,
*::before,
*::after {
box-sizing: inherit;
}
body {
margin: 0;
}
/* Responsive Images */
embed,
iframe,
img,
object,
video {
max-width: 100%;
}
h1,
h2,
h3,
h4,
h5,
h6,
ul,
ol,
li,
p,
pre,
blockquote,
figure,
hr {
margin: 0;
padding-right: 0;
padding-left: 0;
}
a {
text-decoration: none;
}
a:focus {
outline: none;
}
h1,
h2,
h3,
h4,
h5,
h6 {
display: block;
}
/* Removes all decimals and discs from lists */
ol,
ul {
list-style: none;
}
input,
textarea,
button {
border: 0;
border-radius: 0;
background-color: transparent;
font-size: inherit;
font-family: inherit;
font-weight: inherit;
outline: none;
appearance: none;
text-align: left;
}
input:hover,
input:active,
input:focus,
textarea:hover,
textarea:active,
textarea:focus,
button:hover,
button:active,
button:focus {
outline: none;
}
:root {
font-family: Helvetica, Arial, sans-serif;
}
html {
font-size: 175%;
font-weight: 300;
line-height: 1.3;
}
body {
align-items: center;
background-image: linear-gradient(236deg, #272f38, #898999);
display: flex;
height: 100vh;
justify-content: center;
}
.container {
max-width: 20em;
color: white
}
.container > p {
text-align: center;
}
.calculator {
border-radius: 12px;
box-shadow: 0 0 40px 0px rgba(0, 0, 0, 0.15);
margin-left: auto;
margin-right: auto;
margin-top: 2em;
max-width: 15em;
overflow: hidden;
}
.calculator__display {
background-color: #272f38;
color: #fff;
font-size: 1.714285714em;
padding: 0.5em 0.75em;
text-align: right;
}
.calculator__keys {
background-color: #999;
display: grid;
grid-gap: 1px;
grid-template-columns: repeat(4, 1fr);
}
.calculator__keys > * {
background-color: #fff;
padding: 0.5em 1.25em;
position: relative;
text-align: center;
}
.calculator__keys > *:active::before,
.calculator__keys > .is-depressed::before {
background-color: rgba(0, 0, 0, 0.2);
bottom: 0;
box-shadow: 0 0 6px 0 rgba(0, 0, 0, 0.5) inset;
content: "";
left: 0;
opacity: 0.3;
position: absolute;
right: 0;
top: 0;
z-index: 1;
}
.key--operator {
background-color: #eee;
}
.key--equal {
background-image: linear-gradient(to bottom, #272f38, #484f59);
grid-column: -2;
grid-row: 2 / span 4;
}
현재 기능 구현을 하지 않은 껍데기만 있는 계산기의 모습이다.
style.css를 html 코드에서 link하여 사용중이다.
link href="style.css" rel="stylesheet"
<!--
Date : 2020-05-07
Time : 11:01
File : calculator.html
Dir : D:\dev\calc
-->
<link href="style.css" rel="stylesheet">
<script src="calculator.js"></script>
<div class="container">
<p>
RE_BROTHER CALCULATOR
</p>
<div class="calculator">
<div class="calculator__display">0</div>
<div class="calculator__keys">
<button>7</button>
<button>8</button>
<button>9</button>
<button data-action="clear">AC</button>
<button>4</button>
<button>5</button>
<button>6</button>
<button>1</button>
<button>2</button>
<button>3</button>
<button data-action="decimal">.</button>
<button>0</button>
<button class="key--equal" data-action="calculate">=</button>
<button class="key--operator" data-action="add">+</button>
<button class="key--operator" data-action="sub">-</button>
<button class="key--operator" data-action="mul">×</button>
<button class="key--operator" data-action="div">÷</button>
</div>
</div>
</div>
웹 페이지에서 사용하는 계산기가 아닌 terminal 상에서 사용하는 계산기에 대한 코드를 미리 작성해보았다.
const calculate = (n1, operator, n2) => {
let result = ''
if (operator === 'add') {
result = parseFloat(n1) + parseFloat(n2)
} else if (operator === 'subtract') {
result = parseFloat(n1) - parseFloat(n2)
} else if (operator === 'multiply') {
result = parseFloat(n1) * parseFloat(n2)
} else if (operator === 'divide') {
result = parseFloat(n1) / parseFloat(n2)
}
return result
}
위 코드를 실행하면 operator에 따라 연산 기호가 변경된다. 하지만 위와 같이 코드를 작성하게 되면 재할당이 너무 많이 되기 때문에 재할당을 줄여야할 필요성이 있어서 아래와 같이 수정해보았다.
const calculate = (n1, operator, n2) => {
if (operator === 'add') {
return firstNum + parseFloat(n2)
} else if (operator === 'subtract') {
return parseFloat(n1) - parseFloat(n2)
} else if (operator === 'multiply') {
return parseFloat(n1) * parseFloat(n2)
} else if (operator === 'divide') {
return parseFloat(n1) / parseFloat(n2)
}
}
이제 직접 계산기에 들어갈 js파일을 생성하여 하나 하나 기능 구현을 진행해보도록 하겠다.
/*
Date : 2020-05-08
Time : 13:00
File : calculator.js
Dir : D:\dev\calc
*/
const calculate = (n1, operator, n2) => {
const firstNum = parseFloat(n1)
const secondNum = parseFloat(n2)
if (operator === 'add') return firstNum + secondNum
if (operator === 'sub') return firstNum - secondNum
if (operator === 'mul') return firstNum * secondNum
if (operator === 'div') return firstNum / secondNum
}
const getKeyType = key => {
const { action } = key.dataset
if (!action) return 'number'
if (
action === 'add' ||
action === 'sub' ||
action === 'mul' ||
action === 'div'
) return 'operator'
return action
}
const createResultString = (key, displayedNum, state) => {
const keyContent = key.textContent
const keyType = getKeyType(key)
const {
firstValue,
operator,
modValue,
previousKeyType
} = state
if (keyType === 'number') {
return displayedNum === '0' ||
previousKeyType === 'operator' ||
previousKeyType === 'calculate'
? keyContent
: displayedNum + keyContent
}
if (keyType === 'decimal') {
if (!displayedNum.includes('.')) return displayedNum + '.'
if (previousKeyType === 'operator' || previousKeyType === 'calculate') return '0.'
return displayedNum
}
if (keyType === 'operator') {
return firstValue &&
operator &&
previousKeyType !== 'operator' &&
previousKeyType !== 'calculate'
? calculate(firstValue, operator, displayedNum)
: displayedNum
}
if (keyType === 'clear') return 0
if (keyType === 'calculate') {
return firstValue
? previousKeyType === 'calculate'
? calculate(displayedNum, operator, modValue)
: calculate(firstValue, operator, displayedNum)
: displayedNum
}
}
const updateCalculatorState = (key, calculator, calculatedValue, displayedNum) => {
const keyType = getKeyType(key)
const {
firstValue,
operator,
modValue,
previousKeyType
} = calculator.dataset
calculator.dataset.previousKeyType = keyType
if (keyType === 'operator') {
calculator.dataset.operator = key.dataset.action
calculator.dataset.firstValue = firstValue &&
operator &&
previousKeyType !== 'operator' &&
previousKeyType !== 'calculate'
? calculatedValue
: displayedNum
}
if (keyType === 'calculate') {
calculator.dataset.modValue = firstValue && previousKeyType === 'calculate'
? modValue
: displayedNum
}
if (keyType === 'clear' && key.textContent === 'AC') {
calculator.dataset.firstValue = ''
calculator.dataset.modValue = ''
calculator.dataset.operator = ''
calculator.dataset.previousKeyType = ''
}
}
const updateVisualState = (key, calculator) => {
const keyType = getKeyType(key)
Array.from(key.parentNode.children).forEach(k => k.classList.remove('is-depressed'))
if (keyType === 'operator') key.classList.add('is-depressed')
if (keyType === 'clear' && key.textContent !== 'AC') key.textContent = 'AC'
if (keyType !== 'clear') {
const clearButton = calculator.querySelector('[data-action=clear]')
clearButton.textContent = 'CE'
}
}
const calculator = document.querySelector('.calculator')
const display = calculator.querySelector('.calculator__display')
const keys = calculator.querySelector('.calculator__keys')
keys.addEventListener('click', e => {
if (!e.target.matches('button')) return
const key = e.target
const displayedNum = display.textContent
const resultString = createResultString(key, displayedNum, calculator.dataset)
display.textContent = resultString
updateCalculatorState(key, calculator, resultString, displayedNum)
updateVisualState(key, calculator)
})
Test URL : https://codepen.io/re_brother/pen/zYvWpwP