HTML
<!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" />
<link rel="stylesheet" href="style.css" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.0/css/all.min.css"
integrity="sha512-xh6O/CkQoPOWDdYTDqeRdPCVd1SpvCA9XXcUnZS2FmJNp1coAFzvtCN9BmamE+4aHK8yyUHUSCcJHgXloTyT2A=="
crossorigin="anonymous"
referrerpolicy="no-referrer"
/>
<title>Draggable Slider</title>
</head>
<body>
<div class="slider">
<div class="slider-inner">
<div class="slide-img">
<i class="fas fa-futbol"></i>
</div>
<div class="slide-img">
<i class="fas fa-football-ball"></i>
</div>
<div class="slide-img">
<i class="fas fa-golf-ball"></i>
</div>
<div class="slide-img">
<i class="fas fa-biking"></i>
</div>
<div class="slide-img">
<i class="fas fa-basketball-ball"></i>
</div>
<div class="slide-img">
<i class="fas fa-hockey-puck"></i>
</div>
<div class="slide-img">
<i class="fas fa-swimmer"></i>
</div>
</div>
</div>
<script src="app.js"></script>
</body>
</html>
CSS
.slider
컨테이너를 80%로 잡아 놓고,
overflow:hidden;으로 넘쳐 흐르는 건 안 보이게
.slider-inner
내부 너비는 200%로 잡아서 넘쳐 흐르도록
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
i {
color: rgb(255, 255, 255);
font-size: 3rem;
pointer-events: auto;
cursor: pointer;
}
i:hover {
color: rgb(167, 11, 245);
}
.slider {
position: absolute;
left: 10%;
top: 30%;
width: 80%;
height: 200px;
overflow: hidden;
}
.slider-inner {
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 200%;
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 5px;
pointer-events: none;
}
.slide-img {
height: 100%;
background-color: black;
text-align: center;
padding: 3rem;
}
JS
slider.addEventListener("mousedown", (e)=> {})
elem.onmousedown 요소 위에서 마우스 왼쪽 버튼 누를 때 발생
slider 위에서 마우스 왼쪽 버튼 누르면
presed = true;
마우스 커서 grabbing으로 바꿔주기
offsetX,Y는 이벤트 대상 객체에서의 상대적 마우스 좌표 반환
=> slider 위에서 클릭하면,
왼쪽 끝이 0, 오른쪽 끝이 제일 큰 값(slider 너비)을 반환함.
elem.offsetLeft : 상대적으로 가장 가까이 위치한 부모 요소(offsetParent)를 기준으로 지정 요소(elem) 좌측 상단 모서리 기준으로 상대 위치를 반환함.
=> slider 위에서 마우스 클릭한 채로 왼쪽으로 넘기면 음수값 반환. (slider-inner 너비를 더 길게 잡아놨으므로)
mouseenter, mouseup
slider에 진입하거나 클릭 떼면 커서 모양 grab으로 되도록
그리고 mouseup 되는 순간 pressed=false;로 바꿔주기
slider.addEventListener("mousemove", (e) => {})
마우스 클릭하고 있지 않으면 return해버리고
변수 X에 e.offsetX; 넣고
innerSlider.left 변경해서 내부에 있는 요소들을 왼쪽으로 이동하는 인터랙션 구현하기
그리고 그냥 냅두면 요소가 완전히 컨테이너를 벗어날 수 있음.
그래서 경계선을 판단하는 checkboundary() 함수 만들기
내부 이미지들이 끌려다닐 수 있으므로
e.preventDefault() 잡아주기
checkboundary()
if => 오른쪽으로 넘기다가 0보다 커지면 그냥 left 값 0px로 고정하기
else if => 왼쪽으로 넘기다가 다 넘겼으면 경계선에 값 고정하기
let slider = document.querySelector(".slider");
let innerSlider = document.querySelector(".slider-inner");
let pressed = false;
let startx;
let x;
slider.addEventListener("mousedown", (e) => {
pressed = true;
startx = e.offsetX - innerSlider.offsetLeft;
slider.style.cursor = "grabbing";
});
slider.addEventListener("mouseenter", () => {
slider.style.cursor = "grab";
});
// slider.addEventListener("mouseleave", () => {
// slider.style.cursor = "default";
// });
slider.addEventListener("mouseup", () => {
slider.style.cursor = "grab";
});
window.addEventListener("mouseup", () => {
pressed = false;
});
slider.addEventListener("mousemove", (e) => {
if (!pressed) return;
e.preventDefault();
x = e.offsetX;
innerSlider.style.left = `${x - startx}px`;
checkboundary();
});
function checkboundary() {
let outer = slider.getBoundingClientRect();
let inner = innerSlider.getBoundingClientRect();
if (parseInt(innerSlider.style.left) > 0) {
innerSlider.style.left = "0px";
} else if (inner.right < outer.right) {
innerSlider.style.left = `-${inner.width - outer.width}px`;
}
}
참고