보통 웹사이트에서 배너가 끝없이 반복되며 한쪽으로 흐르는 효과를 재현해보자.
방법을 크게 2가지로 써봤는데,
1. js의 신규 내장 API인 $elem.animate(keyframes,options)를 사용하여 쉽고 효과적으로 재현하기
2. Promise와 setTimeout을 사용하여 원시적으로 재현하기
슬프게도 2번이 보기가 더 안 좋다..! 미세하지만 툭 끊기는 느낌이 나기 때문. 화면을 다시 그리고 이벤트 루프가 돌고...화면을 다시 페인팅하고 어쩌고...이런것 때문에 js는 생각대로 마음처럼 움직여주지 않는다.
어찌하였건 똑똑한 개발자들이 animate를 만들어주었으니 더 부드러운 효과가 재현 가능한거다...!
css에서 animation이 종료되었을시 이벤트를 추가하고 싶다면 animationend를 썼듯,
animate()는 finish를 사용한다.
다만 setTimeout이나 setInterval처럼 변수로 한번 받아줘야함.
const ani = $elem.animate(keyframes,options);
ani.addEventListener('finish',callback);
animate 사용
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
|
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
*{margin:0;padding:0;box-sizing:border-box;}
body{
display:flex;flex-flow:column nowrap;
justify-content:center;align-items:center;
gap:30px;
width:100%;height:100vh;
background:#ccc;
}
li{list-style-type:none;}
#wrap{
position:relative;
overflow:hidden;
background:#bbb;
padding:20px;
}
#marquee{
display:flex;flex-flow:row nowrap;
justify-content:flex-start; align-items:center;
gap:20px;
position:relative;
font-size:3rem;
}
#marquee li{
position:relative;
padding:0.5rem 3rem 0.8rem;
background:#fff;
border:1px solid #aaa; border-radius:4px;
}
#marquee li.cloned{color:#ccc;}
</style>
</head>
<body>
<h1>marquee test</h1>
<div id="wrap">
<ul id="marquee">
<li>lorem</li>
<li>ipsum</li>
<li>dolo</li>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
</div>
<script>
const $mq = document.getElementById('marquee');
function marquee_elem($elem){
const $wrap = $elem.parentElement
const widWrap = $wrap.getBoundingClientRect().width;
$wrap.style.width = `${widWrap}px`;
const $$li = $elem.querySelectorAll('LI');
const $frag = document.createDocumentFragment();
$$li.forEach($li =>{
const $newLi = $li.cloneNode(true);
$newLi.classList.add('cloned'); //복제품 비교용
$frag.appendChild($newLi);
});
$elem.appendChild($frag);
move_elem($elem);
}//marquee_elem
function move_elem($elem){
const ani = $elem.animate([{
transform : 'translateX(calc(-100% - 20px))'
}],{
duration : 5000,
easing : "linear"
});
ani.addEventListener('finish',()=>{
move_elem($elem);
},{once:true});
}//move_elem
marquee_elem($mq);
</script>
</body>
</html>
|
cs |
setTimeout과 Promise 사용
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
|
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
*{margin:0;padding:0;box-sizing:border-box;}
body{
display:flex;flex-flow:column nowrap;
justify-content:center;align-items:center;
gap:30px;
width:100%;height:100vh;
background:#ccc;
}
li{list-style-type:none;}
#wrap{
position:relative;
overflow:hidden;
background:#bbb;
padding:20px;
}
#marquee{
display:flex;flex-flow:row nowrap;
justify-content:flex-start; align-items:center;
gap:20px;
position:relative;
font-size:3rem;
}
#marquee.on{
transition:transform 5s linear;
}
#marquee li{
position:relative;
padding:0.5rem 3rem 0.8rem;
background:#fff;
border:1px solid #aaa; border-radius:4px;
}
#marquee li.cloned{color:#ccc;}
</style>
</head>
<body>
<h1>marquee test</h1>
<div id="wrap">
<ul id="marquee">
<li>lorem</li>
<li>ipsum</li>
<li>dolo</li>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
</div>
<script>
const $mq = document.getElementById('marquee');
function marquee_elem($elem){
const $wrap = $elem.parentElement
const widWrap = $wrap.getBoundingClientRect().width;
$wrap.style.width = `${widWrap}px`;
const $$li = $elem.querySelectorAll('LI');
const $frag = document.createDocumentFragment();
$$li.forEach($li =>{
const $newLi = $li.cloneNode(true);
$newLi.classList.add('cloned'); //복제품 비교용
$frag.appendChild($newLi);
});
$elem.appendChild($frag);
move_elem($elem);
}//marquee_elem
function move_elem($elem){
console.log('move_elem');
$elem.classList.add('on');
$elem.style.transform = `translate(calc(-100% - 20px))`;
$elem.addEventListener('transitionend',async ()=>{
await reset_elem($elem);
move_elem($elem);
},{once:true});
}//move_elem
function reset_elem($elem){
return new Promise(res =>{
$elem.classList.remove('on');
$elem.style.transform = `translate(0)`;
setTimeout(()=>{
res();
},0);
});
}//reset_elem
(async function(){
await marquee_elem($mq);
})();
</script>
</body>
</html>
|
cs |
'CSS&JS > ⚡Thinkers' 카테고리의 다른 글
[CSS/JS]vanilla JS로 핸들러 2개인 range input 구현하기(기본) (2) | 2023.10.11 |
---|---|
[JS]부모에만 mousedown,mouseup,mousemove를 적용하고 자식에는 click만 적용하고 싶을 때 (0) | 2023.08.31 |
[JS][Canvas] 캔버스로 파이그래프 그리기 (0) | 2023.04.29 |
[JS]바닐라 자바스크립트로 slick slider 간단히 구현해보기 (0) | 2023.01.20 |
[JS]Draggable & Resize 모달창 (0) | 2022.12.19 |