CSS&JS/⚡Thinkers

[CSS,JS] 타임 슬라이더

arancia_ 2022. 10. 11. 15:07

노비타 홈페이지 우측 하단 참고

 

일정 시간동안 타이머가 차오르며, 그게 끝나면 다음 페이지로 넘어간다.

 

animationend 이벤트 리스너를 이용하여 아주 간단하게 만들 수 있다.
여기서 타이밍 조정은 CSS에서 가능하다는게 가장 큰 장점이다.

깃허브 : https://github.com/OhIkmyeong/time-slider

데모 : https://ohikmyeong.github.io/time-slider/

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
<!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>타임슬라이더</title>
    <link rel="stylesheet" type="text/css" href="./css/timeSlider.css">
    <script src="./js/main.js" type="module"></script>
</head>
<body>
    <h1>TimeSlider v1.0</h1>
 
    <ul class="time-slider" id="timeSlider">
        <li style="--bg-color:salmon">1</li>
        <li style="--bg-color:orange">2</li>
        <li style="--bg-color:yellow">3</li>
        <li style="--bg-color:green">4</li>
        <li style="--bg-color:royalblue">5</li>
        <li style="--bg-color:purple">6</li>
    </ul>
    
    <footer>
        <p>
            참고 : 노비타 홈페이지 <a href="https://www.novita.co.kr/main" traget="blank">www.novita.co.kr</a>
        </p>
    </footer>
</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
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
@charset "utf-8";
*{margin:0;padding:0;box-sizing:border-box;}
li{list-style-type:none;}
 
 
 
/* 📌[TIME SLIDER] */
.time-slider-wrap{
    --gap:10px;
    --time-ing:3s;
    --time-end:1s;
    --time-end-delay:.5s;
    position:relative;
    width:100vmin; aspect-ratio:2/1;
    margin:0 auto;
    border:1px solid black;
}
.time-slider{
    position:relative; overflow:hidden;
    width:100%; height:100%;
}
 
.time-slider-item{
    display:flex; flex-flow:row wrap;
    justify-content:center;align-items:center;
    position:absolute;
    top:0;left:0;
    width:100%;height:100%;
    background:var(--bg-color);
    font-size:10vmin;
    transition:opacity .3s .1s;
}
    /* on */
    .time-slider-item.off{
        opacity:0;
        pointer-events:none;
    }
 
/* 타이머 */
.time-slider-time-wrap{
    display:flex;
    align-items:center;
    gap:10px;
    position:absolute;
    left:0;bottom:0;
    padding:var(--gap) calc(var(--gap) + var(--gap)/ 2);
    background:rgba(0,0,0,.5);
    font-size:20px; color:#fff;
}
 
.time-slider-time-bar-wrap{
    position:relative; overflow:hidden;
    width:150px; height:8px;
    background:rgba(255,255,255,.1);
    border:1px solid rgba(0,0,0,.1);
}
.time-slider-time-bar{
    position:relative;
    width:100%;height:100%;
    background:rgb(255,255,255);
    transform:translateX(calc(-100% + 1px));
}
 
.time-slider-time-bar.ing{
    animation: timebarIng var(--time-ing) linear both;
}
 
.time-slider-time-bar.end{
    animation: timebarEnd var(--time-end) var(--time-end-delay) ease-out both;
}
 
@keyframes timebarIng {
    to{transform:translateX(0);}
}
 
@keyframes timebarEnd {
    from{transform:translateX(0);}
    to{transform:translateX(100%);}
}
cs

main.js

1
2
3
4
5
6
import { TimeSlider } from "./timeSlider.js";
 
const $slider = document.getElementById('timeSlider');
 
const timeSlider = new TimeSlider();
timeSlider.init($slider);
cs

timeSlider.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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/** time-slider */
export class TimeSlider{
    /** time-slider */
    constructor(){
        this.DOM = new TimeSliderDomMaker(this)
        this.CURR = 0;
        this.LEN = null;
    }//constructor
 
    /** 
     * time-slider 생성 시작 
     * @param {DOM}$slider DOM
     * */
    init($slider){
        this.DOM.init($slider);
    }//init
 
    update(){
        this.DOM.hide_item(this.CURR);
        this.update_curr_idx();
        this.DOM.display_curr_idx();
    }//update
 
    update_curr_idx(){
        this.CURR++;
        if(this.CURR >= this.LEN){
            this.CURR = 0;
            this.DOM.show_all_item();
        }
    }//update_curr_idx
}//TimeSlider
 
 
class TimeSliderDomMaker{
    constructor(CTRL){
        this.CTRL = CTRL;
        this.$slider = null;
        this.$$item = null;
        this.$wrap = null;
        this.$timer = {
            wrap : null,
            curr : null,
            all : null,
            bar : null
        }
    }//constructor
 
    init($slider){
        this.$slider = $slider;
        this.make_wrap();
        this.order_reverse_item();
        this.make_time_bar();
        this.animate_time_bar();
    }//init
 
    /** .time-slider-wrap */
    make_wrap(){
        const $parent = this.$slider.parentElement;
        this.$wrap = document.createElement("DIV");
        this.$wrap.classList.add('time-slider-wrap');
        const $all = this.$slider.nextElementSibling;
        if(!$all){
            $parent.appendChild(this.$wrap);
        }else{
            $parent.appendChild(this.$wrap);
            $parent.insertBefore(this.$wrap,$all);
        }
 
        this.$wrap.appendChild(this.$slider);
    }//make_wrap
 
    order_reverse_item(){
        this.$$item = Array.from(this.$slider.children);
        this.CTRL.LEN = this.$$item.length;
        for(let i=this.CTRL.LEN - 1; i >=0; i--){
            this.$$item[i].classList.add('time-slider-item');
            this.$slider.appendChild(this.$$item[i]);
        }
    }//order_reverse_item
 
    make_time_bar(){
        this.$timer.wrap = document.createElement('DIV');
        this.$timer.curr = document.createElement('DIV');
        this.$timer.all = document.createElement('DIV');
        this.$timer.bar = document.createElement('DIV');
        const $barWrap = document.createElement('DIV');
 
        this.$timer.wrap.classList.add('time-slider-time-wrap');
        this.$timer.curr.classList.add('time-slider-time-curr');
        this.$timer.all.classList.add('time-slider-time-all');
        this.$timer.bar.classList.add('time-slider-time-bar');
        $barWrap.classList.add('time-slider-time-bar-wrap');
        $barWrap.appendChild(this.$timer.bar);
 
        this.display_curr_idx();
        this.$timer.all.textContent = String(this.CTRL.LEN).padStart(2,"0");
        
        this.$timer.wrap.appendChild(this.$timer.curr);
        this.$timer.wrap.appendChild($barWrap);
        this.$timer.wrap.appendChild(this.$timer.all);
 
        this.$wrap.appendChild(this.$timer.wrap);
    }//make_time_bar
 
    display_curr_idx(){
        const idx = String(this.CTRL.CURR + 1).padStart(2,"0");
        this.$timer.curr.textContent = idx;
    }//display_curr_idx
 
    hide_item(idx){
        this.$$item[idx].classList.add('off');
    }//hide_item
 
    show_all_item(){
        this.$$item.forEach($item=>$item.classList.remove('off'));
    }//show_item
 
    ing_time_bar(){
        this.$timer.bar.classList.remove('end');
        this.$timer.bar.classList.add('ing');
    }//ing_time_bar
 
    end_time_bar(){
        this.$timer.bar.classList.remove('ing');
        this.$timer.bar.classList.add('end');
    }//end_time_bar
 
    animate_time_bar(){
        this.ing_time_bar();
        this.$timer.bar.addEventListener('animationend',()=>{
            this.end_time_bar();
            this.$timer.bar.addEventListener('animationend',()=>{
                this.CTRL.update();                
                //재귀
                this.animate_time_bar();
            },{once:true});
        },{once:true});
    }//animate_time_bar
}//TimeSliderDomMaker
cs