반응형

html 코드

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>UP & DOWN 게임</title>

    <!-- reset.css -->

    <!-- custom css -->
    <link rel="stylesheet" href="./main.css">

    <!-- custom js -->
    <script src="./app.js" defer></script>

</head>
<body>

    <div class="wrapper">
        <section class="main">
            <h1 class="main-title">신나는 UP & DOWN 게임~!</h1>
            <article class="number-wrapper">
                <h2>
                    <em id="begin">1</em>부터 <em id="end">100</em>사이의 숫자를 입력하세요.
                </h2>
                <div id="numbers"></div>
            </article>
        </section>

        <aside class="result">
            <div id="up">UP!</div>
            <div id="down">DOWN!</div>
        </aside>

        <div id="finish">Congratulation!</div>
    </div>
   
</body>
</html>

 

 

 

js 코드

//게임에 필요한 데이터 객체
const gameData = {
    secret : Math.floor(Math.random()*100) + 1,
    answer : null, //사용자가 클릭한 숫자
    min : 1,
    max : 100
};

//숫자 아이콘 생성 함수
function makeNumberIcons() {

    //id=numbers 태그 안에다가 숫자 아이콘을 배치해 주세요.
    //숫자 아이콘의 개수는 객체의 min, max에 따라 달라집니다.
    //숫자 아이콘은 div태그이고 클래스 이름이 icon입니다.
    //리턴값은 id=numbers 태그 요소입니다.

    const $numbers = document.getElementById('numbers');
    const $frag = document.createDocumentFragment();

    for(let i=gameData.min; i<=gameData.max; i++) {
        const $icon = document.createElement('div');
        $icon.classList.add('icon');
        $icon.textContent = i;
        $frag.appendChild($icon);
    }
    $numbers.appendChild($frag);
    return $numbers;
}

//아이콘 전체 삭제 함수 정의
function clearNumberIcons($numbers) {
    for(let $icon of [...$numbers.children]) {
        $numbers.removeChild($icon);
    }
}

//up & down 애니메이션을 작동시킬 클래스 추가/제거 함수 정의
function executeUpDownAnimation(isUp) {
    const ANI_CLASS_NAME = 'selected';
    document.getElementById('up').classList.toggle(ANI_CLASS_NAME, isUp);
    document.getElementById('down').classList.toggle(ANI_CLASS_NAME, !isUp);
}

//정답을 맞췄을 때 처리를 수행할 함수 정의
function processCorrect($target) {
    //축하메세지 박스를 나타내게 하는 코드
    const $finish = document.getElementById('finish');
    $finish.classList.add('show');

    //정답 아이콘을 움직이게 하는 코드
    $target.setAttribute('id', 'move');
}

//정답을 판별해 주는 함수 정의
function checkAnswer($numbers, $target) {
    //객체에서 정답과 사용자의 선택값 가져오기
    const {secret, answer} = gameData;
    console.log(secret);
    const $begin = document.getElementById('begin');
    const $end = document.getElementById('end');

    //정답을 맞췄을 시 정답처리하는 함수를 호출 (processCorrect)
    //up 또는 down일 경우에는 min과 max값을 변경하고
    //executeUpDownAnimation 함수를 호출.
    if(secret === answer) {
        processCorrect($target);
        return;
    } else if(secret < answer) {
        //down일 경우
        gameData.max = answer - 1;
        $end.textContent = answer;
        executeUpDownAnimation(false);
    } else {
        //up인 경우
        gameData.min = answer + 1;
        $begin.textContent = answer;
        executeUpDownAnimation(true);
    }

    //판별 후에는 아이콘을 재 배치.
    clearNumberIcons($numbers); //현재 렌더링 되어 있는 아이콘들을 전체 삭제
    makeNumberIcons(); //min, max는 변경되어 있고 다시 아이콘을 배치
}


//핵심 실행 로직 즉시 실행 함수 (main 비슷한 역할)
(function() {

    const $numbers = makeNumberIcons();

    //숫자 아이콘 클릭 이벤트 (부모 요소에 이벤트를 설정)
    $numbers.addEventListener('click', e => {

        if(!e.target.matches('#numbers > .icon')) {
            return;
        }

        console.log(`${e.target.textContent} 클릭됨!`);

        gameData.answer = +e.target.textContent;

        //정답 체크 함수 호출
        checkAnswer($numbers, e.target);

    });

}());
 
 

css 코드 

 


/* reset */
a {
    color: inherit;
    text-decoration: none;
}

/* layout */
.wrapper {
    font-size: 18px;
    background: #8c8c8c;
    height: 100vh;
    position: relative;
}

section.main {
    width: 40%;
    background: #f6f6f6;
    border-radius: 10px;
    box-shadow: 0 0 5px rgba(0, 0, 0, 0.7);

    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    overflow: hidden;
}

section.main .main-title {
    padding: 30px 20px;
    font-size: 30px;
    font-weight: 700;
    text-align: center;
    background: #a3f8ff;
    border-bottom: 1px solid #d3d3d3;
}

section.main .number-wrapper {
    padding: 50px 20px;
}

section.main .number-wrapper h2 {
    font-size: 22px;
    text-align: center;
    text-decoration: underline;
}

section.main .number-wrapper h2 em {
    font-size: 1.2em;
    font-weight: 700;
    color: red;
}

#numbers {
    width: 70%;
    height: 400px;
    border: 1px solid #000;
    border-radius: 10px;
    margin: 30px auto 0;
    padding: 30px 50px;
    overflow: auto;
}

#numbers .icon {
    width: 100px;
    height: 100px;
    font-size: 32px;
    font-weight: 700;
    border-radius: 50%;
    color: #fff;
    text-align: center;
    line-height: 100px;
    margin-right: 15px;
    margin-bottom: 10px;

    display: inline-block;
    cursor: pointer;
}

#numbers .icon:hover {
    opacity: 0.7;
    transform: scale(1.1);
}

#numbers .icon:nth-child(4n) {
    background: orangered;
}
#numbers .icon:nth-child(4n-1) {
    background: skyblue;
}
#numbers .icon:nth-child(4n-2) {
    background: yellowgreen;
}
#numbers .icon:nth-child(4n-3) {
    background: orange;
}

/* UP & DOWN 아이콘 */

.result {
    position: absolute;
    top: 50%;
    right: 20%;
    transform: translateY(-50%);
}

.result div[id] {
    width: 150px;
    height: 150px;
    border-radius: 50%;
    font-size: 30px;
    font-weight: 700;
    color: #fff;
    text-align: center;
    line-height: 150px;
    margin-bottom: 30px;
}

#up {
    background: red;
}

#down {
    background: blue;
}

.result div.selected {
    animation: jumping 0.1s infinite linear alternate;
}

@keyframes jumping {
    0% {
        transform: translateY(0);
    }
    50% {
        transform: translateY(-10px);
    }
    100% {
        transform: translateY(-20px);
    }
}

/* finish 영역 */
#finish {
    width: 60%;
    height: 200px;
    background: tomato;
    font-size: 80px;
    font-weight: 700;
    border: 2px solid #000;
    border-radius: 20px;
    text-align: center;
    line-height: 200px;

    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);

    opacity: 0;
    z-index: -10;

}

#finish.show {
    animation: showUp 1s linear forwards;
}

@keyframes showUp {
    0% {
        opacity: 0;
        z-index: -10;
        top: -50%;
    }
    50% {
        opacity: 0.5;
        top: 0;
    }
    100% {
        opacity: 1;
        z-index: 10;
        top: 50%;
    }
}

/* 정답 아이콘에 id=move가 붙으면 해당 아이콘이 커지면서 움직이도록 */
#move {
    position: absolute;
    left: 50%;
    top: 10%;
    z-index: 10;
    border: 4px dashed #000;
    animation: move 2s linear forwards;
}

@keyframes move {
    0% {
        top: 0;
        transform: translateX(0) scale(1);
    }
    100% {
        top: 10%;
        transform: translateX(-50%) scale(2);
    }
}

 

완성된 사이트

 

 

 

※  모든 코드는 중x정보x리학원 이x민 강사님의 코드를 활용하여 제작하였음을 알려드립니다.

반응형

+ Recent posts