swipe란 손가락을 댄 후, 일직선으로 드래그하는 것을 말한다.
상단 gif와 같다.
(1) 공부용
(2) 경량화
(3) 문서 읽기 싫음 (이미 읽음)
(4) 리액트에서 쉽게 사용하기 위해서 react-id-swiper를 다운받아야 함
그렇다고 이 문서가 react로 포팅된 swiper는 아님.
먼저 DOM 틀부터 필요하다.
잇몸이 있어야 이를 끼워넣든 하지 않겠는가
<div id="container">
<div id="inner">
... 다량의 이미지 태그
</div>
</div>
container는 넘치는 컨텐츠를 보여주지 않기 위해
overflow: hidden
을 사용한다.
inner는 실제 컨텐츠를 담는 공간이다.
container
가 보여주지 않는 공간(넘치는 공간)을 다 담는 것이다.
개발자 도구를 켜서 inner부분을 잡아보면 container와 크기가 같은 것을 볼 수 있다.
div는 display: block
속성을 기본으로 가지고 있고,
이는 상위 객체의 width를 따라간다.
그래서 overflow: hidden
인 container와, inner 모두 같은 px이다.
body {
margin: 0;
}
#container {
height: 300px;
overflow: hidden;
}
#inner {
display: flex;
height: 300px;
transition-property: transform;
}
img {
width: 100%;
}
body에 보기 싫은 margin이 8px이나 껴있으니 빼주자.
inner에 display: flex
를 준 이유는 이미지를 일렬로 나열하기 위함이다.
display: flex
의 기본 flex-direction
은 row
이기 때문이다.
transition-property: transform
트랜지션은 transform이 일어날 때만 사용하겠다는 뜻이다.
이렇게 하면 한 화면에 하나의 이미지가 보일 것이다. (모바일 기준)
먼저 eventlistener의 세 종류를 알아야 한다.
window.addEventListener('touchstart', callback);
window.addEventListener('touchmove', callback);
window.addEventListener('touchend', callback);
세 가지 이벤트 모두 터치
위치를(스크린에 올려둔 마우스의 포인터 기준) 알 수 있고,
각각의 이름과 같이 touchstart
는 터치를 시작한 시점 touchmove
는 움직이고 있을 당시
touchend
는 터치가 끝난 시점을 잡아낸다.
나는 container 라는 이름을 가진 div가 스와이프
됨에 따라
inner가 보여주는 슬라이드가 변하는 것을 원하기 때문에
container에 eventListener
를 부여하기로 했다.
const container = document.getElementById('container');
const inner = document.getElementById('inner');
let startPos = 0;
let offset = 0;
let curPos = 0;
const screenWidth = container.clientWidth;
전역 변수로 다음과 같이 지정하였다.
startPos는 touchstart 지점에서 찍히는 screenX를 뜻한다.
offset은 touch를 시작하고 나서의 변위를 뜻한다.
curPos는 현재 슬라이드의 위치, inner가 마지막에 움직이는 위치를 위해 정의했다.
window.onload = function() {
여기에 밑의 세가지 체크포인트가 들어간다.
}
먼저 우리는 터치를 드래그 함으로써 이미지를 넘길 것이기 때문에 터치를 시작한 위치를 알아야 한다.
container.addEventListener('touchstart', (e) => {
startPos = e.touches[0].pageX;
});
스와이프를 끝마친 다음에야 슬라이드가 넘어가는 비(非)유동적인 swiper를 만들지 않기 위해서는
이미지가 내가 스와이프 하고 있는 동안
을 따라와야 한다.
따라서 경위(offset)를 구해주는데 여기에 curPos를 더한 이유는
현재 몇번째 슬라이드에서 스와이프를 하고 있는지를 적용하기 위해서다.
긴 말 할 것 없다. transform으로 x값을 translate해주자.
이 때 transitionDuration은 바로바로 드래그에 이미지가 딸려오게 하기 위함이다.
container.addEventListener('touchmove', (e) => {
offset = curPos + (e.targetTouches[0].pageX - startPos);
inner.style.transform = `translate3d(${offset}px, 0px, 0px)`;
inner.style.transitionDuration = '0ms';
});
앞과 마찬가지로 변위(offset)를 구해주자.
destination을 따로 구한 이유는
앞이나 뒤의 일정 공간을 더 끌어도 결국에는 마지막 혹은 첫번째 슬라이드로 돌아오게 하기 위함이다.
Math.round를 해준 이유는 반 이상 이미지를 드래그 했냐고 물어보는 것이다.
container.addEventListener('touchend', (e) => {
const sum = curPos + (e.changedTouches[0].pageX - startPos);
let destination = Math.round(sum / screenWidth) * screenWidth;
if (destination > 0) {
destination = 0;
}
else if (destination < -(screenWidth * (이미지의 총 갯수 - 1))) {
destination = -(screenWidth * (이미지의 총 갯수 - 1));
}
inner.style.transform = `translate3d(${destination}px, 0px, 0px)`;
inner.style.transitionDuration = '300ms';
curPos = destination;
setTimeout(() => {
inner.style.transitionDuration = '0ms';
}, 300);
});
근데, 돌아올 때 0ms만에 돌아오면 뒤의 여백을 땡긴 의미가 없지 않는가?
transitionDuration
을 300ms로 바꾸어주고,
300ms 후에 다시 0ms으로 바꾸어준다.
막 자신있게 이야기해서 마치 라이브러리 하나를 위의 코드로 단축한 것 같지만,
굉장히 중요한 기능들은 구현하지 못했다.
예를 들면 드래그 한 범위가 좁더라도 그 스피드가 빠르면 스크롤 스피드를 적용하여
가중치를 곱해주어야 꼭 반이 넘지 않아도 슬라이드가 넘어가는데,
touch의 callback parameter를 유심히 찾아보았으나, 그런 속성은 찾지 못하였다.
혹시 알고 있다면 댓글에 써주길 간곡히 요청한다. ㅠㅠ
😎😎