Javascript

Javascript30 - day27 Click and Drag to Scroll

joy_lee 2021. 5. 4. 11:50

목표

마우스 클릭/드래그로 화면 내의 요소 움직이기

 

내가 작성한 코드

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
const items = document.querySelector('.items');
let coords = {
    left: 0,
    x: 0
}
 
function clickItems() {
    items.classList.add('active');
    coords = {
        left: items.scrollLeft,
        x: event.clientX
    }
 
    items.addEventListener('mousemove', dragItems);
    items.addEventListener('mouseup', leaveItems);
}
 
function dragItems() {
    const dx = coords.x - event.clientX;
    items.scrollLeft = coords.left + dx;
}
    
function leaveItems() {
    items.classList.remove('active');
    coords.left = items.scrollLeft;
 
    items.removeEventListener('mousemove', dragItems);
    items.removeEventListener('mouseup', leaveItems);
}
 
items.addEventListener('mousedown', clickItems);
cs

'mousedown' 이벤트 발생시 clickItems 함수를 실행하고, clickItems 안에 eventListener를 통해 'mousemove'와 'mouseup'이벤트에 대한 함수를 연결시켰다.(마우스를 클릭한 후에만 실행될 수 있도록)
'active' class를 추가해서 클릭했다는 것을 확실히 알 수 있도록 한다.

clickItems를 통해 처음 클릭했을 때의 스크롤 위치와 마우스 위치를 변수에 저장하고,

dragItems에서 마우스 위치와 움직인 마우스 위치를 통해 움직일 거리를 계산한다. 

leaveItems에서는 'active' class를 제거하고, 현재 스크롤 위치를 저장한다.

(mousedown에서 처음 스크롤 위치를 저장하므로 moouseup에서는 나중값을 저장하지 않아도 될 것 같다.)

mousemove와 mouseup에 대한 eventlistener를 제거해준다.

 

시간은 좀 걸렸지만 원하는 기능을 만들 수 있었다.
그런데 마우스를 떼지 않고 items div를 벗어났을 때, 'active' class가 그대로 남아있고
다시 마우스를 div에 올려두면 클릭하지 않은 채로 items가 움직였다.
('mouseleave' event를 신경쓰지 못함)


영상에 나온 코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const slider = document.querySelector('.items');
let isDown = false;
let startX;
let scrollLeft;
 
slider.addEventListener('mousedown', () => {
    isDown = true;
});
slider.addEventListener('mouseleave', () => {
    isDown = false;
});
slider.addEventListener('mouseup', () => {
    isDown = false;
});
slider.addEventListener('mousemove', () => {
    if (!isDown) return// stop the fn from running
// drag slider
});
 
cs

 

isDown, startX, scrollLeft 변수들을 만들어주고, 'mousedown', 'mouseleave', 'mouseup', 'mousemove'에 대한 이벤트를 각각 익명함수에 연결한다.(굳이 따로 함수를 만들지 않음)

mouseleave : mouse가 element를 떠날 때 이벤트 발생

마우스를 떼지 않은 채 요소를 벗어나면 효과가 멈추도록 한다.

isDown 변수를 통해 mousemove일때 drag효과를 발생여부를 결정한다.

(false = 기본값, mouseleave, mouseup일때는 return; / true = mousedown했을 때는 아래 코드 실행)

 

1
2
3
4
5
6
slider.addEventListener('mousedown', (e) => {
    isDown = true;
    slider.classList.add('active');
    startX = e.pageX - slider.offsetLeft;
    scrollLeft = slider.scrollLeft;
});
cs

mousedown 이벤트가 발생했을 땐 slider에 'active' class를 추가하고,

startX와 scrollLeft에 마우스를 클릭했을 때의 정보를 입력해준다(초기값)

e.pageX : 마우스의 현재 위치

slider.offsetLeft : slider의 위치

e.pageX - slider.offsetLeft 를 해야 마우스가 slider가 시작되는 곳에서 얼마나 움직였는지 정확하게 구할 수 있다.

slider.offsetLeft : slider의 현재 sliderbar 위치(얼마나 옆으로 움직였는지)

 

1
2
3
4
5
6
7
slider.addEventListener('mousemove', (e) => {
    if (!isDown) return// stop the fn from running
    e.preventDefault();
    const x = e.pageX - slider.offsetLeft;
    const walk = (x - startX) * 2;
    slider.scrollLeft = scrollLeft - walk;
});
cs

isDown = true일 경우 drag를 구현해준다.

e.preventDefault();로 기본적인 드래그 반응을 제거한다.(글이 있는 경우 문자 선택을 막음)

마우스가 움질일 때마다 새롭게 x와 walk을 구하도록 만들어준다.

x는 위의 startX와 같은 방법으로 구한다(현재 마우스 위치)

walk는 현재 마우스 위치(x)에서 처음의 마우스위치(startX)를 뺀 값으로, 얼마나 마우스가 움직였는지 알려준다.

slider.scrollLeft에 scrollLeft - walk를 입력함으로 처음 스크롤 위치에서 walk만큼 이동함을 알려준다.

 

1
2
3
4
slider.addEventListener('mouseup', () => {
    isDown = false;
    slider.classList.remove('active');
});
cs

mouseup 이벤트가 발생하면 더이상 움직이지 않도록 isDown = false로 설정하고, 'active' class를 제거한다.

 

새롭게 알게된 것

class를 잘 보고 active class를 사용함

마우스를 클릭해서 드래그 하는 것도 세세하게 코딩해줘야 구현가능하다는 것이 번거롭기도 하면서도 신기했다.