CSS&JS/👀Study and Copy

[Kevin Powell]CSS,JS로 marquee 태그 같은 무한 스크롤 애니메이션 구현하기

arancia_ 2023. 10. 4. 12:58

https://www.youtube.com/watch?v=iLmBy-HKIAw 

데모 : https://ohikmyeong.github.io/kp-infinite-scroll/

 

Infinite Scorll

 

ohikmyeong.github.io

  • CSS 키워드
    • mask
    • width : max-content
    • transform:translateX(calc(-50% - (var(--gap) / 2)));
  • JS 키워드
    • $elem.cloneNode(true);

 

HTML

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
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Infinite Scorll</title>
    <link rel="stylesheet" type="text/css" href="./css/main.css">
    <script src="./js/main.js" type="module"></script>
</head>
<body>
    <a href="https://www.youtube.com/watch?v=iLmBy-HKIAw" target="_blank">Create an infinite horizontal scroll animation</a>
    
    <div class="ifslider-wrap" data-direction="left" data-speed="10s">
        <ul class="ifslider">
            <li class="ifslider-item">Hello</li>
            <li class="ifslider-item">World</li>
            <li class="ifslider-item">HTML</li>
            <li class="ifslider-item">CSS</li>
            <li class="ifslider-item">JavaScript</li>
            <li class="ifslider-item">Infinite</li>
            <li class="ifslider-item">Scroller</li>
        </ul>
    </div><!-- slider-wrap -->
 
    <div class="ifslider-wrap" data-direction="right" data-speed="5s">
        <ul class="ifslider">
            <li class="ifslider-item">Hello</li>
            <li class="ifslider-item">World</li>
            <li class="ifslider-item">HTML</li>
            <li class="ifslider-item">CSS</li>
            <li class="ifslider-item">JavaScript</li>
            <li class="ifslider-item">Infinite</li>
            <li class="ifslider-item">Scroller</li>
        </ul>
    </div><!-- slider-wrap -->
</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
@charset "utf-8";
/* 📍[wrap] */
.ifslider-wrap{
    --gap:14px;
    outline:1px solid lime;
    position:relative; overflow:hidden;
    width:100%; max-width:600px;
    padding:10px 0;
    mask:var(--mask);
    -webkit-mask:var(--mask);
}
/* 📍[slider] */
.ifslider{
    display:flex; flex-flow:row nowrap;
    gap:var(--gap);
    position:relative;
    width:max-content;
}
 
.ifslider{
    animation: move-infinite-scroll var(--data-speed) linear infinite forwards;
}
[data-direction="right"] .ifslider{
    animation: move-infinite-scroll var(--data-speed) linear infinite backwards reverse;
}
 
.ifslider:hover{
    animation-play-state: paused;
}
 
/* 📍[item] */
.ifslider-item{
    padding:.8em 2em;
    background:#363b4e;
    border-radius:4px;
}
.ifslider-item[data-ifslider-item="cloned"].flag{
    background:rgb(0, 82, 114);
}
 
/* 📍 animation */
@keyframes move-infinite-scroll {
    to{
        transform:translateX(calc(-50% - (var(--gap) / 2)));
    }
}
cs

InfiniteScroll.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
export class InfiniteScroll{
    #SPEED_DFLT = "40s";
    constructor(){
        this.$$wrap = document.querySelectorAll('.ifslider-wrap');
    }//constructor
 
    init(){
        for(const $wrap of this.$$wrap){
            this.set_direction($wrap);
            this.set_speed($wrap);
            this.duplicate_items($wrap);
        }//for
    }//init
 
    /**
     * 
     * @param {*} $wrap 
     */
    set_direction($wrap){
        const dir = $wrap.dataset.direction === "right" ? "right" : "left";
        if(dir !== "right"){
            $wrap.dataset.direction = dir;
        }
    }//set_direction
 
    /**
     * 
     * @param {*} $wrap 
     */
    set_speed($wrap){
        const speed = $wrap.dataset.speed || this.#SPEED_DFLT;
        $wrap.style.setProperty('--data-speed',speed);
    }//set_speed
 
    /**
     * 
     * @param {*} $wrap 
     */
    duplicate_items($wrap){
        const $frag= document.createDocumentFragment();
        const $slider = $wrap.getElementsByClassName('ifslider')[0];
        const $$item = $slider.querySelectorAll('.ifslider-item');
        Array.prototype.forEach.call($$item, ($item,idx) =>{
            const $clone = $item.cloneNode(true);
            $clone.dataset.ifsliderItem = "cloned";
            if(!idx){$clone.classList.add('flag');}
            $frag.appendChild($clone);
        });
        $slider.appendChild($frag);
    }//duplicate_items
 
}//class-InfiniteScroll
cs