
Vue로 첫 프로젝트를 만들 때 scroll action에 대해 많은 관심을 가졌었다.
window.addeventListener('scroll', 블라블라)
를 통해 많은 것을 구현했으나 리소스를 굉장히 많이 잡아먹어 원할하지 않았던 경험이 있다.
그당시 구현했던 한 섹션마다 자동 스크롤을 위해 했던 뻘짓
const viewH = document.documentElement.clientHeight // viewport 길이
let nowH = 0
let downStack = 0
let upStack = 0
let scrollPrevent = true
document.querySelector("#app > div.home").addEventListener('wheel', (e) => {
// console.log('scrollPrevent')
e.preventDefault()
if (scrollPrevent == false){
return
}
// console.log(nowH)
// if (nowH >= document.body.scrollHeight) { console.log('1')}
if (e.wheelDeltaY < 0){
if (downStack > 3 && scrollPrevent) {
scrollPrevent = false
nowH += viewH * 1.3
nowH = (nowH >= document.body.scrollHeight ) ? document.body.scrollHeight : nowH - viewH * 0.16
window.scrollTo({top: nowH, behavior: 'smooth'});
downStack = 0
setTimeout(() => {scrollPrevent = true}, 1000)
console.log('down')
} else{
downStack ++
if (upStack >= 0) upStack --
nowH = (nowH >= document.body.scrollHeight) ? document.body.scrollHeight : nowH + viewH * 0.04
window.scrollTo({top: nowH, behavior: 'smooth'});
}
} else if (e.wheelDeltaY > 0) {
if (upStack > 3 && scrollPrevent) {
scrollPrevent = false
nowH -= viewH * 1.3
nowH = (nowH <= 0) ? 0 : nowH + viewH * 0.16
window.scrollTo({top: nowH, behavior: 'smooth'});
upStack = 0
setTimeout(() => {scrollPrevent = true}, 800)
// console.log('up')
} else {
upStack ++
if (downStack >= 0) downStack --
nowH -= viewH * 0.04
if (nowH <= 0) nowH = 0
window.scrollTo({top: nowH, behavior: 'smooth'});
}
}
}, {passive: false})
스크롤 위아래를 감지해서 스택이 쌓이면 자동으로 한 섹션 이동하는 알고리즘
스택이 너무 많이 쌓이는 것을 방지하기 위해
setTimeout으로 한 번 섹션이 이동하면 스택이 쌓이는 것을 800ms 동안 방지한다.
자바스크립트에 대한 개념이 많이 부족해 애쓴 모습이 지금도 불쌍한 코드이다.

각설하고 이건 아니다 싶어서 css awards사이트들을 둘러보며 소스를 열며 뜯어보니
스크롤 액션에 대한 것은 10개중 9개는 gsap 라이브러리 사용한 것을 확인했다.
docs를 읽어보니 jquery보다 20배 이상 빠르며 굉장히 오래된 역사를 가져
국내는 미흡하지만 꽤 큰 커뮤니티가 형성된 것을 보고 이 라이브러리를 사용해야겠다고 마음먹었다.
그 당시 내가 원했던 액션은 스크롤을 하면 여러개의 섹션이 각각의 방향으로 이동하는 액션이였다.

let len = Object.keys(this.reviews).length
const el = document.querySelector('.frame')
const reel = document.querySelectorAll('.wheel')
const reels1 = [reel[0], reel[2]]
const reels2 = [reel[1], reel[3]]
ScrollTrigger.create({
trigger: el,
start: 'top top',
end: `+=${len * 50}%`,
scrub: true,
pin: true,
pinType: 'transform',
animation: gsap
.timeline()
.set(reel, { willChange: 'transform' })
.fromTo(reels1, { y: '5%'}, { y: `-${len * 10}%`, ease: 'linear' })
.fromTo(reels2, { y: `-${len * 10}%`}, { y: '5%', ease: 'linear'}, '<')
.set(reel, { willChange: 'auto'})
})
이렇게 비교적 간단한 코드로 구현이 가능했다.
간단히 설명하면
el은 전체 프레임
reel은 각각의 프레임을 잡기 위해 전체선택 (지금보니 el을 reel로 대체해도 괜찮을듯하다)
start는 top top으로 프레임의 위쪽, 뷰포트의 아래쪽
end 는 data의 길이만큼
scrub true로 스크롤과 이어줌
pin 으로 overflow되는 배열로 가는 것이 아닌 현재 화면 고정