개발

온라인 코인 토스 사이트 구현


어쩌다 동전던지기로 뭘 결정할일이 있어서 온라인 사이트에서 코인 토스를 구현한게 없을까 뒤적이다가 괜찮은게 없어서 이왕에 한번 만들어보자 결심하고 만든 기능이다.

본문 이미지

코인을 클릭하면 코인토스를 실행할 수 있고, 자신이 실행한 코인토스 결과를 그 페이지 안에서 아래에 통계로써 기록해준다.

코인토스의 확률은 아래코드와 같이 자바스크립트에서 제공하는 Math rand 함수를 이용했다.

const newDeg = (() => {
    const rand = Math.random() >= 0.5;
    return currentDeg + 180 * flipCount + (rand ? 0 : 180);
})();Copy

엄청난 난수가 아닌 실제 1/2 구분만 하면되고, 매번 클릭에 따른 요청 딜레이에 따라 충분히 랜덤값을 얻을 수 있어 간단하게 구현할 수 있다.

이후에 결과값을 화면에 띄워주고, 현재 결과를 아래 통계 테이블에 기록해주면 되는 간단한 기능이라 볼 수 있다.

실제 코인 토스 함수는 아래와 같다.

<script>
    let totalCount = 0;
    let headsCount = 0;
    let isAnimating = false;

    function tossCoin() {
        if (isAnimating) return;
        const sound = new Audio('/coin.mp3');
        sound.play().catch(e => {
            console.warn("Coin sound failed to play", e);
        });
        isAnimating = true;

        const coin = document.getElementById("coin");
        const move = document.getElementById("coin-move");
        const result = document.getElementById("result");
        const resultBody = document.getElementById("resultBody");
        const shadow = document.getElementById("coin-shadow");

        const currentRotation = coin.style.transform || "rotateX(0deg)";
        const currentDegMatch = currentRotation.match(/rotateX\((\d+)deg\)/);
        const currentDeg = currentDegMatch ? parseInt(currentDegMatch[1], 10) : 0;

        const flipCount = 5;
        const newDeg = (() => {
            const rand = Math.random() >= 0.5;
            return currentDeg + 180 * flipCount + (rand ? 0 : 180);
        })();

        coin.style.transition = "transform 1.0s cubic-bezier(0.33, 0, 0.2, 1)";
        move.style.transition = "transform 0.6s ease-in-out";
        move.style.transform = "translateY(-100px)";
        shadow.style.transform = "scale(0.5)";
        shadow.style.opacity = "0.3";
        const shine = document.querySelector('.coin .shine');
        shine.style.opacity = '1';
        setTimeout(() => {
            shine.style.opacity = '0';
        }, 400);

        const tossMessages = [
            "운명의 코인토스!",
            "자, 결과는 무엇일까?",
            "긴장되는 순간!",
            "앞일까? 뒷일까?",
            "결정의 시간이야!",
            "운에 맡겨보자!",
            "돌려돌려 동전판~",
            "한 번 던져보자!",
            "흥미진진한 토스!"
        ];
        result.textContent = tossMessages[Math.floor(Math.random() * tossMessages.length)];

        setTimeout(() => {
            // 기존보다 리얼한 느낌을 위해 rotateZ 추가
            const zWobble = (Math.random() - 0.5) * 40; // -20도~20도 정도의 비틀림
            coin.style.transform = `rotateX(${newDeg}deg) rotateZ(${zWobble}deg)`;
        }, 30);

        setTimeout(() => {
            move.style.transform = "translateY(0px)";
            shadow.style.transform = "scale(1)";
            shadow.style.opacity = "1";
        }, 600);

        setTimeout(() => {
            const finalDeg = newDeg % 360;
            const isFront = finalDeg % 360 === 0;

            totalCount++;
            if (isFront) headsCount++;

            const resultProbability = isFront
                ? ((headsCount / totalCount) * 100).toFixed(1)
                : (((totalCount - headsCount) / totalCount) * 100).toFixed(1);

            result.textContent = isFront ? "앞면!" : "뒷면!";

            const row = document.createElement("tr");
            row.innerHTML = `
                <td>${totalCount}</td>
                <td>${isFront ? "앞면" : "뒷면"}</td>
                <td>${resultProbability}%</td>
            `;
            resultBody.insertBefore(row, resultBody.firstChild);
            isAnimating = false;
        }, 1000);
    }
</script>Copy

실제 동전의 앞뒷면을 랜덤으로 구현하는 코드보다 실제 코인토스처럼 보이게 하기위한 별도의 스타일, 스크립트 처리가 더 많은게 함정이긴하다만...

데스크탑 버전

그래도 결과를 보면 참 만족스럽다.
나름 모바일 사용성도 꽤나 고려했고, 동전 던지기다운 느낌좀내려고 반사광 처리나 동전 튕기는 소리도 그럴듯하게 추가해보았다.

모바일 버전

모바일 버전이 손으로 직접 누르는 느낌때문에 더 동전 던지는 느낌은 있다.

#JavaScript #블로그
2 개의 댓글
고양만두 1달 전 대댓글
동전 던질 때 소리까지 나니깐 더 실감나는 것 같아요 ㅎㅎ
회사에서 커피 내기할 때 딱인듯!
에루샤 1달 전 대댓글
@고양만두
땡그르르~
×