개발 기술/css 애니메이션 (with js)

계산기 만들기(with js, css)

by GicoMomg 2021. 9. 27.

이번 시간에는 Array.from, grid를 사용하여 계산기를 만들어보자!

1. preview

See the Pen calculator by KumJungMin (@kumjungmin) on CodePen.



2. html

preview에서 본 계산기는 어떻게 구성되었을까? 자세한 코드를 알아보자!

1) 전체 구조

  • frmaecalculator의 부모로, 내부 자식이 수평&수직 정렬되게 한다.
  • calculator에는 크게 result, content가 있다.
  • result는 결과를 보여주는 영역이다.
  • content내부에는 key-wrap, clac-wrap이 있는데, 자판 역할을 한다.

2) 전체 코드

  • 위 그림을 코드로 보면 아래와 같다.
    <div class="frame">
    <div class="calculator">
      <div class="result"></div>          <!--결과창-->
      <div class="content">
        <div class="key-wrap"></div>     <!--좌측 키판-->
        <div class="calc-wrap"></div>    <!--우측 키판-->
      </div>
    </div>
    </div>



3. scss(1)

1) frame 설정하기

  • frame가 수평&수직 중앙 정렬이 되도록 그림과 같이 지정한다.

.frame {
  width: 400px;
  height: 400px;
  position: absolute;
  top: 50%;       //[1]
  left: 50%;      //[1]
  transform: translate(-50%, -50%);  //[1]
}

  • frame의 자식인 calculator를 수평&수직 중앙 정렬하기 위해 flex를 사용한다.
    .frame {
    ...
    display: flex;           //[2]
    justify-content: center; //[2] 수평 중앙
    align-items: center;     //[2] 수직 중앙
    background: ...;  //배경색은 자유
    }



2) calculator 설정하기

(1) 내부 요소 배열하기

calculator내부에는 result, content가 있다.
이 두 요소는 아래 그림과 같이 column방향으로 배열되어야 한다.

.calculator {
  width: 16rem;
  height: 20rem;
  display: flex;             //[1]
  justify-content: flex-end; //[2]
  flex-direction: column;    //[3]
  ...
}

[1] 그러기 위해서는 먼저 display: flex를 한다.
[2] justify-content: flex-end를 하여 수직 하단 정렬을 한다.
[3] flex-directioncolumn으로 지정해서, 요소의 방향을 변경한다.


(2) calculator의 배경색을 반투명하게

calculator의 배경색을 반투명으로 설정하여 뒷배경이 흐릿하게 보여야 한다.

.calculator {
  background: rgba( 36, 36, 36, 0.75);
  box-shadow: 0 8px 32px 0 rgba( 31, 38, 135, 0.7 );
  backdrop-filter: blur(4px);                     //[5]
  border-radius: 0.5rem;          
  border: 1px solid rgba( 255, 255, 255, 0.18 );  //[6]
  ...
}

[5] backdrop-filter는 요소 뒤 영역에 그래픽 효과를 적용하는데, blur(4px)을 하면 요소 뒤 영역이 흐려진다.
(단, 이 효과를 위해서는 요소나 요소의 배경을 반투명하게는 설정해야 함)

[6] backdrop-filter가 적용되기 위해 rgba로 배경색을 반투명하게 지정한다.



3) 결과창 꾸미기

  • 계산 결과를 알려주는 result에는 아래와 같이 설정한다.
    .result {
    width: 100%;
    height: 2.5rem;
    padding: 0 1.4rem;
    font-weight: 500;
    font-size: 1.2rem;
    overflow: auto;
    box-sizing: border-box;
    color: #fff;
    }



4) 키패드 정렬하기

content에는 key-wrap(좌측 키패드) & calc-wrap(우측 키패드)이 있다. 이 키패드들은 content에서 좌우 정렬이 되어야 하며, wrap 내부 요소인 숫자&계산 버튼은 일정한 간격으로 배치되어야 한다.

(1) 키패드를 좌우 정렬하기

.content {
  width: 100%;
  display: flex;    //[1]
}

[1] content의 내부 요소들이 좌우 정렬이 되도록 flex를 설정한다.

.key-wrap,
.calc-wrap {
  height: 13rem;
  ...
}
.key-wrap {
  flex: 4;     //[2]
  ...
}
.calc-wrap {
  flex: 1;     //[2]
}

[2] flex: N을 설정하여 N비율만큼 차지하게 된다.
좌측:우측 = 4:1 비율이 되도록 key-wrap에는 flex:4 & calc-wrap에는 flex:1을 설정한다.


(2) 버튼을 일정하게 정렬하기

.key-wrap,
.calc-wrap {
  ...
  display: grid;    //[3]
}
.key-wrap {
  grid-template-columns: auto auto auto;   //[4]
}

[3] 키패드 내부 버튼들이 격자 배치되도록 display:grid를 한다.
[4] 좌측 키패드의 버튼이 3열이 되도록 grid-template-columns: auto auto auto를 해준다.

버튼 디자인의 경우 js을 사용해 버튼을 추가한 후 진행하도록 하자 :)




4. js

1) 필요한 dom 가져오기

  • 좌측&우측 키패드에 버튼을 추가하기 위해 querySelector로 dom을 가져온다.
  • result의 경우, 연산을 보여줘야하므로 querySelector로 가져온다.
    const numWrap = document.querySelector('.key-wrap');   //좌측키패드
    const calcWrap = document.querySelector('.calc-wrap'); //우측키패드
    const result = document.querySelector('.result');      //결과창



2) 버튼과 연산을 위한 변수 선언하기

(1) 좌측 키패드 버튼 선언하기

  • 좌측 키패드는 1~9, 초기화(C), 0, 계산(=)이 필요하므로 아래와 같이 선언할 수 있다.
    const keyboard = [1, 2, 3, 4, 5, 6, 7, 8, 9, 'C', 0, '=']; 

하지만, 19를 일일이 작성한다니 너무 비효율적인 거 같다! 🤔🤔
그렇다면 `
N`까지의 숫자를 한 번에 만들 수 있는 방법은 무엇이 있을까?
2가지 정도 알아보자!


A. Array.from 사용하기

: Array.from()은 새로운 배열 객체를 생성할 수 있다.

Array.from(A, B)
A에는 배열로 반환하고자 하는 이터러블 객체
B에는 배열의 모든 요소에 대해 호출할 맵핑 함수를 선언

: 먼저 첫번째 인자에 {length: 9}를 넣어주면, 길이가 9인 배열이 생성된다.

const keyboard = Array.from({length:9}); 

//[undefined,undefined,undefined,undefined,undefined,undefined,undefined,undefined,undefined]

: 그 다음 두 번째 인자는 (v, i)=>i+1을 하면 index+1 형태의 배열이 생성된다.

const keyboard = Array.from({length:9}, (v,i)=>i+1)); 

//[1, 2, 3, 4, 5, 6, 7, 8, 9]

: 만약 다른 배열에 넣어서 사용할 경우 전개 연산자를 이용하면 된다.

const keyboard = [...Array.from({length:9}, (v,i)=>i+1), 'C', 0, '=']; 

//[1, 2, 3, 4, 5, 6, 7, 8, 9, 'C', 0, '=']

B. map() & fill() 사용하기

: 먼저 길이가 9인 배열을 생성한다.

const keyboard = Array(9); 

//[undefined,undefined,undefined,undefined,undefined,undefined,undefined,undefined,undefined]

: fill()는 배열의 시작 ~ 끝 인덱스 전까지 정적인 값으로 채우는 함수이다.
: 그런데 이 fill()map()과 같이 사용하면 map()에서 리턴된 값으로 배열을 채울 수 있다!

const keyboard = Array(9).fill().map((v,i)=>i+1);

////[1, 2, 3, 4, 5, 6, 7, 8, 9]

: 다른 배열에 넣어서 사용할 경우 전개 연산자를 이용하면 된다.

const keyboard = [...Array(9).fill().map((v,i)=>i+1), 'C', 0, '=']; 

//[1, 2, 3, 4, 5, 6, 7, 8, 9, 'C', 0, '=']

이 예시에서는 Array.from방식을 사용했지만, 둘 중 어느 것을 사용해도 좋다 :)
지금까지 좌측 키패드 변수 선언에 대해 알아보았으니 이제는 우측&연산 변수도 살펴보자!



(2) 우측 키패드 버튼 & 연산 결과를 담을 변수 선언하기

const calc = ['<-', '+', '-'];   //우측 키패드
let formula = [];                //연산 결과
  • 우측 키패드에서 사용할 지우기(<-), 더하기(+), 빼기(-) 버튼 변수를 선언한다.
  • 연산 결과를 담을 배열(formula)을 선언한다.



3) 각각의 키패드에 버튼 추가하기

(1) 좌측 키패드에 버튼 추가하기

for (const i in keyboard) {
  const btn = document.createElement('button');   //[1]
  btn.classList.add('num');                       //[2]
  btn.classList.add('label-' + i);                //[2]
  btn.value = keyboard[i];                        //[3]
  btn.innerText = keyboard[i];                    //[4]
  btn.addEventListener('click', calculate);       //[5]
  numWrap.appendChild(btn);                       //[6]
}

[1] createElement을 사용하여 button 요소를 생성한다.
[2] classList.add(클래스명)으로 button 요소에 클래스를 지정한다.
[3] btn.value을 사용하면 버튼 요소의 value를 지정할 수 있다.
[4] innerText로 버튼 내부의 글자를 작성한다.
[5] addEventListener로 click 이벤트시 calculate 함수를 실행하게 한다.
[6] 완성된 버튼 요소를 numWrap의 자식으로 추가한다.


(2) 우측 키패드에 버튼 추가하기

for(const i in calc) {
  const btn = document.createElement('button'); //[1]
  btn.classList.add('calc');               //[2]
  btn.value = calc[i];                     //[3]
  btn.innerText = calc[i];                 //[4]
  btn.addEventListener('click', calculate);//[5]
  calcWrap.appendChild(btn);               //[6]
}

[1] createElement을 사용하여 button 요소를 생성한다.
[2] classList.add(클래스명)으로 button 요소에 클래스를 지정한다.
[3] btn.value을 사용하면 버튼 요소의 value를 지정할 수 있다.
[4] innerText로 버튼 내부의 글자를 작성한다.
[5] addEventListener로 click 이벤트시 calculate 함수를 실행하게 한다.
[6] 완성된 버튼 요소를 calcWrap의 자식으로 추가한다.



4) 연산 함수 만들기

(1) 필요한 데이터는...

function calculate(e) {
  const value = e.target.value;                 //[1]
  ...
}

[1] e.target.value로 버튼의 value값을 가져온다.
이 함수에서는 이 value이 무엇인지에 따라 다른 연산을 진행한다.


(2) = 버튼 클릭하면 연산을 한다.

function calculate(e) {
  ...
  if (value === '=') {                          
    const data = eval(formula.join('')) || '';  //[2-1][2-2]
    formula = data.toString().split('');        //[3-1][3-2]
  } 
  ...
}

[2-1] formula.join('')을 하여 배열을 하나의 문자로 만든다.
[2-2] 그 다음 eval()을 사용하여 string 형태의 연산식을 계산한다.
[3-1] 계산된 값(data)을 toString()로 문자로 변환하고, split('')을 하여 배열로 만든다.
[3-2] 이렇게 생성된 값을 다시 formula에 저장한다.

왜 연산한 값(data)을 string으로 만들고 다시 배열로 만들어서 formula에 넣을까?
그 이유는 계산된 결과값에 지우기 버튼(<-)을 클릭했을 때에 하나씩 지워지게 하기 위함이다!


(3) C 버튼 클릭하면 초기화한다.

function calculate(e) {
  ...
  else if (value === 'C') formula = [];         //[4]
}

[4] c버튼을 누르면 초기화가 되어야 하므로 formula을 빈 배열로 초기화한다.


(5) <- 버튼 클릭하면 가장 마지막 문자를 지운다.

function calculate(e) {
  ...
  else if (value === '<-') formula.pop();    //[5]
}

[5] <-클릭시 마지막에 작성한 문자를 삭제해야하므로, pop()을 사용한다.


(6) 값을 추가하고 나타내기

function calculate(e) {
  ...
  else formula.push(value);                 //[6]
  result.innerText = formula.join('');      //[7]
}

[6] 숫자나 연산을 클릭했을 경우에는 push를 사용해 formula에 추가한다.
[7] formula.join('')을 한 값을 result에 나타내기 위해 innerText를 사용한다.



5. scss (2)

우리는 결과적으로 js를 이용해 버튼과 이벤트를 추가하였다!
그럼 이제 마지막으로 추가한 버튼에 디자인을 적용하고 끝내자 :)

  • 버튼 디자인의 경우 간단하다.
  • hover시 배경색과 글자색이 변하게 하고, (=) 버튼의 경우 다른 css를 적용하였다.
    .num,
    .calc {
    position: relative;
    background: transparent;
    color: #fff;
    border: 1px solid rgba(0,0,0, 0.2);
    font-size: 1.2rem;
    font-weight: 500;
    cursor: pointer;
    transition: 0.25s;
    &:hover {
      background: #fff;
      color: #3d3d3d;
    }
    }
    .label-11 {
    background: #6A82FB;
    &:hover {
      background: #FC5C7D;
    }
    }





출처
아래 출처로 이동하면 더 자세히 알 수 있다고? 꼭 가봐야 겠는데~ 🤔

반응형

댓글