codestates / codestates/Memory-It
[Dev Log] UI : Experimental 터치슬라이더 구현하기 #3
- Dominant language
- JavaScript
- Stars
- 1
- Forks
- 2
- PR merge metrics
- No merged PRs in 30d
Description
- 지금부터가 하이라이트라고 할 수 있다. 터치 또는 드래그이벤트를 확인했고, 좌표를 찾을 수 있다.
- 일단 좌표의 이동만큼 이미지가 같이 이동하면 되고
- 최종적으로는 터치 또는 드래그가 끝났을때 시작,끝의 좌표차를 이용해서 어떤 이전,현재, 다음 이미지중 무엇을 놓을지 정하면 된다.
## 1. 연속적으로 애니메이션 효과 주기(requestAnimationFrame)
- window.requestAnimationFrame(callback)을 호출하면 인자로 넘긴 콜백함수를 실행시킨다.
- 이때 콜백 내부에서는 타겟으로 잡은 DOM객체의 style을 변경시켜주면 된다.
- 그리고 인자로준 콜백함수 내부에서requestAnimationFrame(callback)을 재귀적으로 실행시켜주면 연속적으로 애니메이션효과를 줄 수 있다.
- 해당 메소드는 ID값을 반환하는데 cancelAnimationFrame(ID)를 호출해서 재귀호출을 멈출 수 있다.
[window.requestAnimationFrame() 참조 - MDN](https://developer.mozilla.org/ko/docs/Web/API/Window/requestAnimationFrame)
```jsx
const setSliderPosition = () => {
pictureWrapperRef.current.style.transform = `translateX(${currentTranslateValue.current}px)`
// currentTranslateValue.current 이 부분은 바로 다음 파트에서 설명함.
}
const animation = stamp => {
// console.log(stamp)
if (isDragging.current) {
setSliderPosition()
requestAnimationFrame(animation)
}
}
const touchStart = (e, idx) => {
pictureContainerRef.current.style.cursor = 'grabbing'
isDragging.current = true
currentIdx.current = idx
startPos.current = getPositionX(e)
animationId.current = requestAnimationFrame(animation)
}
const touchEnd = () => {
if (isDragging.current) {
pictureContainerRef.current.style.cursor = 'grab'
isDragging.current = false
cancelAnimationFrame(animationId.current)
}
}
```
## 2. 좌표값을 통해 연속 애니메이션 적용
- 이전 글에서 터치 또는 커서의 좌표를 찾는 getPositionX함수를 만들었다.
- 슬라이드 시작 좌표에서 슬라이드 중인 좌표 차를 이용해서 그만큼 translate에 적용한다.
- touchMove나 mouseMove 이벤트에 의해 연속적으로 좌표차가 생기는데 이를 requestAnimationFrame()을 이용해서 연속적으로 애니메이션을 적용시켜준다.
```jsx
const touchMove = e => {
if (isDragging.current) {
const currentPosition = getPositionX(e)
currentTranslateValue.current =
prevTranslateValue.current + currentPosition - startPos.current
}
}
```
https://user-images.githubusercontent.com/84060219/149630203-585da535-645d-4ef6-b1b7-21529580b3d9.mov
## 3. 사용자의 의도를 이용한 슬라이딩
- 이것이 맨 처음 말한 **_진짜_** 슬라이더가 되는 부분이라고 할 수 있다.
- 우린 터치 슬라이드를 할 때 다음 사진으로 넘기기위해 다음사진에 도달할때까지 슬라이드를 넘기지 않는다. 대충 슥 훑듯이 넘기는데 이러한 사용자 경험을 만들어주는 부분이다.
- 이 부분도 좌표차를 이용해서 구현할 수 있다.
- 사용자가 일정한 정도를 넘는 슬라이드를 시도하면 다음 또는 이전으로 넘어가게 한다.
- currentTranslateValue와 prevTranslateValue가 특히 유용하게 사용되고 마무리로 적절한 값들을 저장해줘야함.
```jsx
const setPositionByIndex = () => {
currentTranslateValue.current =
currentIdx.current * -pictureContainerRef.current.offsetWidth
prevTranslateValue.current = currentTranslateValue.current
setSliderPosition()
}
const touchEnd = () => {
if (isDragging.current) {
pictureContainerRef.current.style.cursor = 'grab'
isDragging.current = false
cancelAnimationFrame(animationId.current)
const movedBy = currentTranslateValue.current - prevTranslateValue.current
if (movedBy < -100 && currentIdx.current < allImage.length - 1) {
currentIdx.current += 1
}
if (movedBy > 100 && currentIdx.current > 0) {
currentIdx.current -= 1
}
setPositionByIndex()
}
}
```
- currentTranslateValue는 touchMove(mouseMove)에서 연속적으로 변하는 값이다. 즉 연속적으로 슬라이딩되고 있는 움직임의 정도(슬라이딩 시작과 현재좌표의 좌표상 거리 차).
- prevTranslateValue는 처음사진을 기준으로 0,
- moveBy < -100 or 100 < moveBy 만큼. 즉 moveBy가 -99 ~ 99 사이의 값이 아니여야만(어느정도 슬라이딩해야만) 최종적으로 보여주는 사진이 이전 또는 다음 사진이 된다. (개인적으론 100 ~ 150정도가 적당한듯했다.)
- 아무튼 범위에 따라 currentIdx를 바꿔준다.
- 그리고 나면 setPositionByIndex()를 호출하는데 해당함수 내부에서는 바뀐(안바꼈을 수 도 있고) 인덱스값에 따라 currentTranslateValue 와 prevTranslateValue를 바꾸는데
- 여기서 사용된 pictureContainerRef.current.offsetWidth는 **_이미지 하나만큼의 크기를 가지는 DOM객체_** 의 크기. 즉 이미지 하나의 크기와 동일하다. 결국 인덱스 * -이미지크기를 함으로써 최종적으로 translate에 적용할 값이 정해지는 셈이다.
- 그후 다시 setSliderPosition() 호출한다.
- setSliderPosition()는 **_'2. 좌표값을 통해 연속 애니메이션 적용'_** 에서 사용했었는데, 지금은 최종적으로 결정된 좌표값을 stanslate에 적용하는대에 쓰인다.
https://user-images.githubusercontent.com/84060219/149631453-feaf206f-8ec0-43b9-8db3-5bca25f25abb.mov
- 추가로 touchMove 함수에서
- currentTranslateValue.current = prevTranslateValue.current + currentPosition - startPos.current 이 부분과
- touchEnd 함수에서
- const movedBy = currentTranslateValue.current - prevTranslateValue.current 이 부분을 보면
- prevTranslateValue를 더했다 뺐다를 하는데 이렇게 해줘야 movedBy의 범위를 moveBy < -100 or 100 < moveBy 로 고정적으로 계산할 수 있다.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.