OneScroll Layout (1) 강의중 질문 드립니다.
안녕하세요, 쉬운 강의 덕분에 gsap 을 쉽게 접하고 사용할 수 있어서 감사합니다.
근데 해당 부분 구현중에 범샘께서 작성하신 코드(ScrollTrigger-finished)에서 확인해보면
1) section3 도달 후, section2로 스크롤을 올리는 경우
2) section4 도달 후, section3에서 다시 section4 로 스크롤을 내리는 경우
해당 2가지 경우에서 section03을 기준으로 스크롤이 위아래로 작동을 잘 하지 않습니다.
if(currentPageIndex === 3) return;
때문인 것 같은데, 스크롤을 자유롭게 올렸다 내렸다 하면서 페이지를 확인할 수 있도록 하려고 하는데 잘 안되네요.
코드를 어떤 방식으로 수정해야할지 질문 남깁니다.
감사합니다.
回答 1
1
안녕하세요 김민식님 😀
질문주신 문제를 확인 했으며, 코드에 대한 설명은 아래와 같습니다.
section3 도달 후, section2로 스크롤을 올리는 경우
원인 : section03 진입시 state.isPlaying값이 true로 변경되어 wheel 이벤트는 작동되지만
handleWheel 이벤트 안에서의 조건 처리 인 if(state.isPlaying) 부분에 조건이 적용되지 않아 바로 올라가지 않는 것 입니다.
해결방안 :
일반 스크롤을 조금이라도 내린 후 올리면 다시 isPlaying의 상태 변수가 true로 되기 때문에 제대로 동작할 수 있습니다.
우선, 언급해 주신 if(currentPageIndex === 3) return 조건을 else 안으로 넣어 애니메이션이 작동되도록 설정합니다.
if(state.isPlaying){
state.isPlaying = false;
if(direction === 'up'){
if(currentPageIndex <= 1) return;
--currentPageIndex
}else{
if(currentPageIndex === 3) return; // 해당 부분 입니다.
if(currentPageIndex >= sections.length) return;
++currentPageIndex
}
transition(currentPageIndex,direction)
}그리고 다음 문제를 해결하기 위해선 isPlaying이 false인과 동시에 휠의 방향이 up인 조건이 필요하며,
handleWheel 안에 조건을 더 만들어 처리해줄 수 있습니다.
if(!state.isPlaying && direction === 'up'){
if(!state.isGoingUp){
transition(2,'up')
state.isGoingUp = true;
}
return;
}이후 해당 조건 발동시 아래의 코드가 읽히지 않도록 return을 넣어 주고(아래의 코드는 isPlaying이 true인 경우 애니메이션이 실행되는 조건며, transition함수는 실행 즉시 isPlaying을 true로 만들어 버립니다.) 애니메이션이 1회만 실행할 수 있도록(wheel 이벤트는 여러번 실행되기 때문에 1번만 실행될 수 있는 변수가 필요합니다.) isGoingUp변수를 만들고 제어해줍니다.
이어서 transition 함수 안의 scrollTrigger onComplete callback 부분에 section03 영역에 다시 진입시 isGoingUp 변수를 false로 만들어주어 다음번에 같은 섹션 재진입시 (section02 → section03) 동일한 애니메이션을 가져갈 수 있도록 처리해줍니다.
onComplete:()=>{
state.isPlaying = true;
globalEnter()
switch (index) {
case 1: page01.enter(); return;
case 2: page02.enter(); return;
case 3:
page03.enter();
state.isGoingUp = false;
return
;
case 4: page04.enter(); return;
}
}
section4 도달 후, section3에서 다시 section4 로 스크롤을 내리는 경우
원인 : section04 도달 후 지속적인 down wheel을 할 경우 isPlaying의 상태가 false이기 때문에 더이상 애니메이션이 동작하지 않음.
해결방안 :
section04에 도착할 경우 휠을 계속 진행하여도 isPlaying의 상태를 true로 유지시켜 주어야 합니다.
그리고 section01에서도 위로 계속 휠을 할 경우 동일한 문제가 발생하기 때문에 handleWheel 함수 안에서 같이 조건처리를 진행합니다.
function handleWheel(e){
let direction = e.deltaY < 0 ? 'up' : 'down'
// 아래 코드의 2줄의 조건 처리 부분 입니다.
if(direction === 'up' && currentPageIndex === 1) return;
if(direction === 'down' && currentPageIndex === sections.length) return;
if(!state.isPlaying && !state.isScrolling && direction === 'up'){
if(!state.isGoingUp){
transition(2,'up')
state.isGoingUp = true;
}
return;
}
if(state.isPlaying){
state.isPlaying = false;
if(direction === 'up'){
if(currentPageIndex <= 1) return;
--currentPageIndex
}else{
if(currentPageIndex === 3) return;
if(currentPageIndex >= sections.length) return;
++currentPageIndex
}
transition(currentPageIndex,direction)
}
}
위에서 return을 해주셔야 아래의 playing을 변경하는 코드까지 도달하지 않으므로 위에서 조건처리를 진행합니다.
이후 section03에서도 스크롤을 할 경우 자잘하게 section의 이동이 생기는 문제를 해결하기 위해 확실하게 스크롤 중인 상태냐 아니냐에 따라 추가 조건을 넣어 문제를 해결합니다.
전역 상태는 다음과 같습니다.
const state = {
isPlaying: true,
isScrolling: false,
isGoingUp: false
}
scrollTrigger onUpdate callback 부분에 progress를 가져와 0인 상태에서만 isScrolling이 false가 될 수 있도록 (스크롤 제일 최상단 부분) 설정해줍니다.
page03:{
enter:()=>{
// console.log('enter page03');
if(!ScrollTrigger.getById('section03')){
ScrollTrigger.create({
trigger: '.depth_wrapper',
start: 'top top',
end: 'bottom bottom',
markers: true,
id:'section03',
onLeaveBack:()=> {
if(!state.isScrolling){
transition(2,'up')
}
},
onLeave:()=> transition(4,'down'),
onUpdate:({progress}) => {
// 해당 부분 입니다.
// onUpdate 함수는 매개변수가 있기 때문에 progress만 구조분해로 가져옵니다.
if(progress === 0) {
state.isScrolling = false;
}else{
state.isScrolling = true;
}
}
})
markers()
}
},
leave:()=>{
// console.log('leave page03');
}
},
이렇게 3가지 옵션을 설정하면 보다 안정적으로 oneScroll 이벤트와 중간에 연결된 normal scroll의 레이아웃까지 mixin하여 사용할 수 있습니다.
완성된 코드는 다음과 같습니다.
const state = {
isPlaying: true,
isScrolling: false,
isGoingUp: false
}
let currentPageIndex = 1;
const sections = gsap.utils.toArray('.section');
const pages = {
page01:{
enter:()=>{
// console.log('enter page01');
},
leave:()=>{
// console.log('leave page01');
},
},
page02:{
enter:()=>{
// console.log('enter page02');
},
leave:()=>{
// console.log('leave page02');
}
},
page03:{
enter:()=>{
// console.log('enter page03');
if(!ScrollTrigger.getById('section03')){
ScrollTrigger.create({
trigger: '.depth_wrapper',
start: 'top top',
end: 'bottom bottom',
markers: true,
id:'section03',
onLeaveBack:()=> {
if(!state.isScrolling){
transition(2,'up')
}
},
onLeave:()=> transition(4,'down'),
onUpdate:({progress}) => {
if(progress === 0) {
state.isScrolling = false;
}else{
state.isScrolling = true;
}
}
})
markers()
}
},
leave:()=>{
// console.log('leave page03');
}
},
page04:{
enter:()=>{
// console.log('enter page04');
},
leave:()=>{
// console.log('leave page04');
}
},
}
function globalEnter(){
// console.log('globalEnter');
gsap.to('h2',{opacity:1,y:0})
}
function globalLeave(){
// console.log('globalLeave');
gsap.to('h2',{opacity:0,y:30})
}
function transition(index,dir){
const {page01,page02,page03,page04} = pages;
currentPageIndex = index;
gsap.to('.wrapper',{
y: -innerHeight * (index - 1),
duration:1.5,
ease:'expo.inOut',
onStart:()=>{
globalLeave()
switch (dir === 'up' ? index + 1 : index - 1) {
case 1: page01.leave(); return;
case 2: page02.leave(); return;
case 3: page03.leave(); return;
case 4:
page04.leave();
state.isPlaying = false;
return;
}
},
onComplete:()=>{
state.isPlaying = true;
globalEnter()
switch (index) {
case 1: page01.enter(); return;
case 2: page02.enter(); return;
case 3:
page03.enter();
state.isPlaying = false;
state.isGoingUp = false;
return
;
case 4: page04.enter(); return;
}
}
})
}
function handleWheel(e){
let direction = e.deltaY < 0 ? 'up' : 'down'
if(direction === 'up' && currentPageIndex === 1) return;
if(direction === 'down' && currentPageIndex === sections.length) return;
if(!state.isPlaying && !state.isScrolling && direction === 'up'){
if(!state.isGoingUp){
transition(2,'up')
state.isGoingUp = true;
}
return;
}
if(state.isPlaying){
state.isPlaying = false;
if(direction === 'up'){
if(currentPageIndex <= 1) return;
--currentPageIndex
}else{
if(currentPageIndex === 3) return;
if(currentPageIndex >= sections.length) return;
++currentPageIndex
}
transition(currentPageIndex,direction)
}
}
container.addEventListener('wheel',handleWheel)
markers()
자세하게 확인해볼 수 있도록 수업 자료에 (scrollTrigger-finished) 업데이트 해두겠습니다.

열정적으로 수업을 들어주셔서 감사합니다 :)
imagesLoaded에 관한 질문
0
19
2
섹션04 Layout에서 Mixed Layout파트의 실습 index.html파일 열었을때 선생님께서 보여주시는 가로스크롤이 안나타남.
0
87
3
안녕하세요 ScrollSmoother에 대해 질문드릴게요
1
203
2
GSAP 플러그인의 무료화 관련
2
225
3
barba와 ScrollSmoother 사용했을 때 스크롤 업데이트
0
110
2
barba.js 사용시 페이지이동
1
207
2
이상한 부분이 있어서 문의드립니다.
1
103
1
smooth-scrollbar 관련 질문
1
227
2
imagesLoaded 에 관해 질문드려요
1
107
1
scroll Draw SVG에서 실선이 아닌 점선으로 그리고 싶어요
1
186
1
파트 4 오픈 일정 문의
1
145
2
GSAP을 사용하면서 리사이징 시 애니메이션 값 재할당에 대해 질문드립니다.
1
191
2
스무스 스크롤바 모바일에서 뻑뻑한 느낌이 들어요!
1
247
2
OneScroll Layout 질문 드립니다!
1
164
2
OneScroll Layout (1) 관련 재질문드립니다.
2
166
1
이미지 엑박
1
189
1
UI관련 문의드립니다..!
1
238
2
OneScroll Layout 모바일 터치
1
471
2
노션 링크 보는 곳
1
387
1
반응형 관련해서 질문드립니다.
1
559
2
ScrollTrigger의 end와 toggleClass
1
948
1
Text Effects(2)는 Text Effects(1)영상에 포함돼있는 것 같아요!
1
269
1
nav위에 마우스를 올리면 스크롤이 안돼요 😢
1
491
2
ScrollTrigger의 animation에 함수호출
1
451
1

