CSS&JS/👀Study and Copy

[CSS+JS] requestAnimationFrame 으로 로딩 효과 구현하기

arancia_ 2025. 4. 10. 11:20

유튜브 쇼츠 : https://youtube.com/shorts/CghGn-glw28?si=4LfGyNdcicBsYOwE

See the Pen CSS+JS Loading requestanimationframe by Oh Ikmyeong (@dpffpself) on CodePen.

HTML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CSS LOADING DOT</title>
    <link rel="stylesheet" href="./style.css" type="text/css"/>
    <script src="./loader.js" defer></script>
</head>
<body>
    <a href="https://youtube.com/shorts/CghGn-glw28?si=b7Npva9mkIS39dCO" target="_blank">CSS LOADING DOT SHORTS</a>
    <div class="loader">
        <span class="dot"></span>
        <span class="dot"></span>
        <span class="dot"></span>
        <span class="dot"></span>
        <span class="dot"></span>
    </div>
</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
*{margin:0;padding:0;box-sizing:border-box;}
 
html,body{
    width:100%;min-height:100vh;
    background:#000;
}
 
body{
    display:flex; flex-flow:column nowrap;
    gap:30px;
    justify-content:center; align-items:center;
}
 
a{
    display:inline-block;
    padding:5px 10px;
    text-decoration:none;
    background:#fafafc;
    border-radius:4px;
    font-size:12px;
}
 
.loader{
    --_size:20px;
    --_gap:10px;
    position:relative;
    width:calc(var(--_size) * 5 + var(--_gap) * 4);
    height:var(--_size);
}
 
.dot{
    display:block;
    position:absolute;
    top:0;
    width:var(--_size);
    aspect-ratio:1/1;
    background:#fff;
    border-radius:50%;
}
cs



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
const $$dot = document.querySelectorAll(".dot");
 
const SIZE = 20;
const GAP = 10;
const COUNT = 5;
/* 1차 정렬 */
for (let i = 0; i < $$dot.length; i++) {
    const $dot = $$dot[i];
    const startPx = SIZE * i + GAP * i
    $dot.style.transform = `translate(${startPx}px,0px)`;
    $dot.dataset.remember = startPx;
    move_dots($dot);
}
 
function move_dots($dot) {
    const remember = parseInt($dot.dataset.remember);
    if (remember <= 0) {
        const cnt = COUNT - 1;
        const next = (cnt * SIZE) + (cnt * GAP);
        $dot.dataset.remember = next;
 
        const ani = $dot.animate([
            {
                transform: `translate(0px, 0px)`
            },
            {
                transform: `translate(0px, -50px)`,
                background: "red"
            },
            {
                transform: `translate(${next}px, -50px)`,
                background: "red"
            },
            {
                transform: `translate(${next}px, 0px)`,
                background: "#fff"
            }
        ], {
            fill: "both",
            duration: 1200,
            easing: "ease-out"
        });
        ani.addEventListener("finish", () => {
            requestAnimationFrame(() => {
                move_dots($dot);
            });
        }, { once: true });
    } else {
        const next = remember - (SIZE + GAP);
        $dot.dataset.remember = next;
        const ani = $dot.animate([
            {
                transform: `translate(${next}px, 0px)`
            }
        ], {
            fill: "both",
            duration: 1200,
            easing: "ease-in"
        });
        ani.addEventListener("finish", () => {
            requestAnimationFrame(() => {
                move_dots($dot);
            });
        }, { once: true });
    }
 
}//move_dots
cs

animate와
animate가 종료되면 쓸 수 있는 이벤트리스너 finish
requestAnimationFrame으로 콜백하기

사실 css만으로도 구현할 수 있긴 할텐데.... 그게 더 귀찮다
하지만 실제 프로젝트에는 css만으로 구현할 수 있는 방안이 훨씬 좋을것이다