CSS&JS/👀Study and Copy

[JS] 카운터 만들기 재구현 (handy tally counter)

arancia_ 2022. 12. 21. 16:37

코드펜 Jon Kanter 님의 Handy Tally Counter 예제를 보고 원리를 내 방식대로 재구현해보았다.
차이점은 원본은 열마다 0~9까지 DOM이 존재하고, 이걸 9번 위로 올리는 방식이면,
나는 그냥 열마다 now, prev 2개의 DOM만 존재하고, setTimeout과 animationend를 이용하여 내용을 갈아치울거다.
그리고 col의 갯수가 몇개나 늘어나든 상관없이 짜볼거다 (0~999도 되고 0~99999도 되고)

데모 페이지 : https://ohikmyeong.github.io/code-pen-copy/Hand-Tally-Counter/mine/

 

Handy Tally Counter-Copy

0 1 0 1 0 1 0 1

ohikmyeong.github.io

See the Pen Counter by Oh Ikmyeong (@dpffpself) on CodePen.

 

HMTML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<!DOCTYPE html>
<html lang="en">
<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>Handy Tally Counter-Copy</title>
    <link rel="stylesheet" href="./style.css">
    <script src="./main.js" type="module"></script>
</head>
<body>
    <p>
        <a href="https://codepen.io/jkantner/pen/ZEQYKON" target="_blank">handy tally counter</a>
    </p>
 
    <section id="wrap-counter">
        <article id="counter">
            <div class="counter-col">
                <span class="now">0</span>
                <span class="next">1</span>
            </div>
            <div class="counter-col">
                <span class="now">0</span>
                <span class="next">1</span>
            </div>
            <div class="counter-col">
                <span class="now">0</span>
                <span class="next">1</span>
            </div>
            <div class="counter-col">
                <span class="now">0</span>
                <span class="next">1</span>
            </div>
        </article>
        
        <button id="btn-one">1개씩</button>
        <button id="btn-all">전부 맞추기</button>
    </section><!-- wrap-counter -->
</body>
</html>
cs

CSS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
@charset "utf-8";
*{margin:0;padding:0;box-sizing:border-box;}
 
body{
    display:flex; flex-flow:column nowrap;
    justify-content:center; align-items:center;
    gap:20px;
    min-height:100vh;
    text-align:center;
}
 
#wrap-counter{
    display:flex;flex-flow:column nowrap;
    justify-content:center;
    gap:10px;
    position:relative;
    padding:10px;
    background:#eee;
}
 
#counter{
    --wid-col:40px;
    display:flex;
    justify-content:space-between; align-items:flex-start;
    gap:10px;
    position:relative; overflow:hidden;
    height:var(--wid-col);
    margin-bottom:var(--wid-col);
    background:black;
    border:1px solid black;
}
 
.counter-col{
    position:relative;
    width:var(--wid-col);
    background:linear-gradient(to bottom, #888, #333,#888, #333,#888);
    font-size:20px; font-weight:bold; color:#fff;
}
.counter-col.on{
    animation:col-up .1s linear both;
}
.counter-col span{
    display:block;
    width:100%; aspect-ratio:1/1;
    line-height:var(--wid-col);
}
 
button{position:relative;padding:.5em 2em;}
 
@keyframes col-up {
    from{transform:translateY(0);}
    to{transform:translateY(-50%);}
}
cs

main.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
class HandyCounter {
    constructor() {
        this.COUNT = 0;
        this.$counter = document.getElementById('counter');
        this.MAXCOUNT = '';
    }//constructor
 
    /** initiate counter */
    init() {
        for(let i=0; i<this.get_now_all().length; i++this.MAXCOUNT += '9';
        this.MAXCOUNT = Number(this.MAXCOUNT);
 
        const $btnOne = document.getElementById('btn-one');
        const $btnAll = document.getElementById('btn-all');
 
        $btnOne.addEventListener('click'this.on_click_one);
        $btnAll.addEventListener('click'this.on_click_all);
    }//init
 
    /**
     * $counter의 now DOM들을 모두 반환
     * @returns {DOM} .now 
     */
    get_now_all() {
        return Array.from(this.$counter.querySelectorAll('.now'));
    }//get_now_all
 
    /**
     * col을 이동시키고 숫자를 바꾼다
     * @param {DOM}$now .now
     * @param {Number}num 
     */
    move_col($now, num) {
        const $col = $now.parentElement;
        $col.classList.add('on');
        $col.addEventListener('animationend'this.on_ani_end($now,num), { once: true });
    }//move_col
 
    /** 
     * 애니메이션 종료시 col을 원위치로 하고 now와 next 값을 바꿉니다 
     * @param {DOM}$now .now
     * @param {Number}num 
     * */
    on_ani_end = ($now, num)=>{
        setTimeout(() => {
            const $col = $now.parentElement;
            const $next = $now.nextElementSibling;
            const next = (Number(num) + 1) % 10;
            
            $now.textContent = num;
            $next.textContent = next;
            
            const cnt = this.get_now_all().map($now => $now.textContent).join('');
            
            this.COUNT = Number(cnt);
            $col.classList.remove('on');
        }, 100);
    }//on_ani_end
 
    /**
     * 버튼 클릭 - 한개씩만 카운트 증가
     */
    on_click_one = () => {
        this.COUNT++;
        if (this.COUNT > this.MAXCOUNT) this.COUNT = 0;
        const $$now = this.get_now_all();
        const numStr = String(this.COUNT).padStart($$now.length"0");
 
        for (let i = 0; i < $$now.length; i++) {
            const $now = $$now[i];
            const num = numStr[i];
            if ($now.textContent == num) continue;
            this.move_col($now, num);
        }//for
    }//on_click_one
 
    /**
     * 버튼 클릭 - 전부 맞추기
     */
    on_click_all = () => {
        const $$now = this.get_now_all();
        const min = Math.min(...$$now.map($now => $now.textContent));
        for (let i = 0; i < $$now.length; i++) {
            const $now = $$now[i];
            const num = Number($now.textContent);
            if (num > min) continue;
            this.move_col($now, (num + 1) % 10);
        }//for
    }//on_click_all
}//HandyCounter
 
 
const COUNTER = new HandyCounter();
COUNTER.init();
cs