지난번에 만든 터치슬라이더는 웹에서는 터치이벤트를 제공하지 않기 때문에 반쪽짜리 슬라이더였음.
이번에는 웹에서는 mousedown + mousemove + mouseup / 터치 지원 환경에서는 touchmove, touchend시 좌우로 SNAP 되는 바닐라 자바스크립트 슬라이더를 만들어봤다.
다음은 소스코드
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
|
<!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>touch, grab</title>
<link href="style.css" rel="stylesheet" type="text/css"/>
<script src="main.js" type="module"></script>
</head>
<body>
<button id="btn_pause">일시정지</button>
<section id="sect">
<ul id="slide">
<li><div class="content">0</div></li>
<li><div class="content">1</div></li>
<li><div class="content">2</div></li>
<li><div class="content">3</div></li>
<li><div class="content">4</div></li>
<li><div class="content">5</div></li>
</ul><!-- slide -->
<button data-arrow="prev">⬅</button>
<button data-arrow="next">➡</button>
</section><!-- sect -->
<ul id="pager"></ul>
</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
|
@charset "utf-8";
:root{
--wrap:50vh;
}
*{margin:0;padding:0;box-sizing:border-box;} li{list-style-type:none;}
button,input{font-family:inherit;font-size:inherit;color:inherit; cursor:pointer;}
html,body{
display:flex;flex-wrap:wrap;flex-direction:column;
justify-content:center;
align-items:center;
overflow:hidden;
width:100%;min-height:100vh;
background:#ccc;
font-family:sans-serif; font-size:24px;}
#btn_pause{padding:.5em 2em;}
/* */
#sect{
position:relative; overflow:hidden;
width:var(--wrap); aspect-ratio:1/1;
margin:1rem auto;
border:2px solid #000;}
/* */
#slide{
display:flex; flex-wrap:nowrap;
position:relative;
border:1px solid #000;
transition:transform .3s;
cursor:grab;}
#slide li{
flex:none;
display:flex;justify-content:center;align-items:center;
position:relative;
background:#fff;
border:1px solid red;
width:var(--wrap); aspect-ratio:1/1;}
#slide li .content{pointer-events:none; user-select:none;}
/* */
[data-arrow]{
position:absolute;
width:3rem; aspect-ratio:1/1;
top:calc(50% - 1.5rem);
border:none;
border-radius:50%;}
[data-arrow = "prev"]{left:0;}
[data-arrow = "next"]{right:0;}
/* */
#pager{
display:flex; justify-content:center;
position:relative;
width:var(--wrap);;}
#pager li{
position:relative;
height:15px; aspect-ratio:1/1;
margin:20px 5px;
background:black;
border-radius:10rem;
cursor:pointer;}
#pager li.on{aspect-ratio:3/1; background:red;}
|
cs |
JS - 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
|
import {make_pager, get_index_pager} from "./pager.js"
import {arrow_slide } from "./arrow.js";
import {stop_slide, autoSlide, adjust_slider} from "./slide.js";
import { touch_moveSlider } from "./touch.js";
import { drag_mouseDown } from "./drag.js";
export const pause = document.getElementById('btn_pause');
export const sect = document.getElementById('sect');
export const slide= document.getElementById('slide');
export const pager = document.getElementById('pager');
export let WID = window.innerHeight / 2;
export const LEN = slide.children.length;
export const MAX = WID*(LEN-1)*-1;
export let BOUND_LEFT = sect.getBoundingClientRect().left;
export let btn_txt = {
play : "다시 재생?",
pause : "일시정지"
}
export let move = {
now : 0
,PER : WID / (window.innerHeight / 50)
,ORDER : 0
,e_prev : 0
,e_now : 0
}
export let pager_num = {eq : 0};
/* ⚠ ✨ 실행 - 페이저 생성 */
make_pager(slide,pager);
/* 윈도우 리사이즈 관련 */
window.addEventListener('resize',()=>{
WID = window.innerHeight / 2;
move.PER = WID / (window.innerHeight / 50);
});
/* ✨ 실행 - 자동슬라이드 */
setTimeout(autoSlide, 1000);
/* ✨ 실행 - 터치 */
sect.addEventListener('touchmove',touch_moveSlider);
sect.addEventListener('touchend',adjust_slider);
/* ✨ 실행 - 마우스다운, 마우스무브, 마우스업 */
sect.onmousedown = drag_mouseDown;
/* ✨ 실행 - 버튼 - 일시정지 */
pause.addEventListener('click',()=>{
switch(pause.innerText){
case btn_txt.pause :
stop_slide();
break;
case btn_txt.play :
autoSlide();
pause.innerText = btn_txt.pause;
break;
default :
stop_slide();
break;
}
}); //click
/* ✨ 실행 - 좌우 버튼 클릭시 */
sect.addEventListener('click',(e)=>{
const target = e.target;
if(!target.dataset.arrow){return;}
arrow_slide(e);
return;
}); //click
/* ✨ 실행 - pager 클릭시 */
pager.addEventListener('click',(e)=>{
const thisLI = e.target;
if(thisLI.tagName !== "LI"){return;}
stop_slide();
get_index_pager(thisLI);
}); //click
|
cs |
JS - pager.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
|
import {WID, LEN, pager_num, pager, move} from "./main.js"
import { move_slide } from "./slide.js";
/* 최초 - 슬라이드 개수에 맞춰 페이저 자동 생성 */
export function make_pager(slide,pager){
const slide_li = slide.children;
for(let i=0; i<slide_li.length; i++){
const pager_li = document.createElement('LI');
pager.appendChild(pager_li);
}
pager.children[0].classList.add('on');
}//make_pager
/* 현재 페이저 계산 및 갱신 */
export function cacul_pager(num){
reset_pager();
pager_num.eq += num;
if(pager_num.eq < 0){pager_num.eq = LEN - 1;}
if(pager_num.eq > LEN - 1){pager_num.eq = 0;}
display_pager(pager_num.eq);
}//cacul_pager
/* INDEX 가져오기 */
export function get_index_pager(thisLI){
const thisEQ = Array.prototype.indexOf.call(pager.children,thisLI);
reset_pager();
display_pager(thisEQ);
move.now = WID * thisEQ * -1;
move_slide();
}//get_index_pager
/* DISPLAY */
export function display_pager(order){
pager.children[order].classList.add('on');
}//display_pager
/* RESET */
export function reset_pager(){
for(let dot of pager.children){dot.classList.remove('on');}
}//reset_pager
|
cs |
JS - arrow.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
|
import {WID} from "./main.js"
import {cacul_move_slide, stop_slide} from "./slide.js";
import { reset_pager,cacul_pager } from "./pager.js";
/* 02. 좌우 버튼 */
export function arrow_slide(e){
stop_slide();
reset_pager();
const direct = e.target.dataset.arrow;
switch(direct){
case "prev" :
cacul_pager(-1);
cacul_move_slide(WID);
break;
case "next" :
cacul_pager(1);
cacul_move_slide(-WID);
break;
default :
break;
}
}
|
cs |
JS - slide.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
|
import {slide, MAX, WID, pause, btn_txt, move, pager_num} from "./main.js"
import {reset_pager, cacul_pager, display_pager} from "./pager.js"
/* (공통) 슬라이드 움직임 */
export function cacul_move_slide(wid){
move.now += wid;
if(move.now > 0){
move.now = MAX;
}else if(move.now < MAX){
move.now = 0;
}
move_slide();
}//cacul_move_slide
export function move_slide(){
slide.style.transform = `translateX(${move.now}px)`;
}//move_slide
/* (공통) 슬라이드 멈춤 */
export function stop_slide(){
pause.innerText = btn_txt.play;
clearTimeout(set_move);
}//stop_slide
/* (공통) 좌표값 계산*/
export function is_prev_next(){
if(move.e_now > move.e_prev){
move.now += move.PER;
}else{
move.now -= move.PER;
}
move_slide();
move.e_prev = move.e_now;
}
/* (공통) 종료 후 슬라이드 위치 조정 */
export function adjust_slider(){
if(move.now > 0){
move.now = 0;
}else if(move.now < MAX){
move.now = MAX;
}
move.ORDER = Math.round(move.now / WID);
move.now = move.ORDER * WID;
move_slide();
reset_pager();
pager_num.eq = move.ORDER * -1
display_pager(pager_num.eq);
}//adjust_slider
/* (공통) 자동 슬라이드 */
let set_move;
export function autoSlide(){
cacul_pager(1);
cacul_move_slide(-WID);
set_move = setTimeout(autoSlide,2000);
}
|
cs |
JS - touch.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
import { BOUND_LEFT, move } from "./main.js";
import { display_pager } from "./pager.js";
import { is_prev_next, stop_slide } from "./slide.js";
export function touch_moveSlider(e){
e = e || window.event;
e.preventDefault;
if(e.target.dataset.arrow){return;}
stop_slide();
move.e_now = e.changedTouches[0].clientX - BOUND_LEFT;
is_prev_next();
}//touch_moveSlider
|
cs |
JS - drag.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
|
import {sect,slide,BOUND_LEFT, move} from "./main.js"
import { adjust_slider, is_prev_next, stop_slide } from "./slide.js";
export function drag_mouseDown(e){
e = e || window.event;
e.preventDefault;
stop_slide();
slide.style.cursor = 'grabbing';
document.onmouseup = drag_close;
sect.onmousemove = drag_slide;
}
function drag_slide(e){
e = e || window.event;
e.preventDefault;
if(e.target.dataset.arrow){return;}
move.e_now = e.clientX - BOUND_LEFT;
is_prev_next();
}//drag_slide
function drag_close(){
slide.style.cursor = 'grab';
adjust_slider();
document.onmouseup = null;
sect.onmousemove = null;
}//drag_close
|
cs |
'CSS&JS > ⚡Thinkers' 카테고리의 다른 글
[vanilla JS] transform으로 드래그 시키기 (client 기준) (0) | 2021.09.01 |
---|---|
밑에서 떠오르는 텍스트 (잘려보이고, 밑에서 떠오름) (0) | 2021.08.30 |
[JS] 트위터 이미지 불러오기 효과 (0) | 2021.07.13 |
(vanilla JS) touch event로 snap 되는 grab slider 만들기 (0) | 2021.07.13 |
hover시 전체 배경으로 되는... 하여간 다이나믹한 메뉴..뭐시기..뭐.. (0) | 2021.07.01 |