Scroll 현재 값 계산하기
Apple 웹 사이트를 보다가 제품을 소개하는 페이지에 이용자가 scroll을 할 때마다 특정 구간에서 다양하고 화려한 애니메이션이 동작하는 것을 보고 감탄을 하게 되었다.
웹페이지가 이렇게 화려하고 스토리가 있을 수 있구나!!! 너무 빠져든 나머지 나는 그렇게 Scroll에 대해 공부를 시작하게 되었다.
❗️ 애플 사이트에서 Scroll 애니메이션 효과는 Chrome, Safari에서만 동작한다.
📝 전체적인 구현 컨셉
파란색 숫자 0 1 2 3 은 Scroll할때 특정 영역 Scene을 의미한다.
특정 영역(Scene)에 도달했을때 그 영역에 구현한 Animation이 동작한다. (각 구간에 대한 Animation 처리)
시작에 앞서 가장 우선시 되어야 할 것은 우리가 Scroll을 할 때마다 현재 또는 이전에 대한 Scroll 위치 값을 알아야 한다.
0 1 2 3 의 Scene 에 대한 HTML 코드
HTML
<section class="scroll-section" id="scroll-section-0">
<h1>Apple WonPro</h1>
<div class="sticky-elem main-message">
<p>누가 야수에게 힘을 허락하였는가</p>
</div>
</section>
<section class="scroll-section" id="scroll-section-1">
<p class="description">
야수 같은 파워와 판도를 바꾸는 배터리 사용 시간.
WonKeun Silicon의 마법은 그 효율성에서 비롯되죠.
</p>
</section>
<section class="scroll-section" id="scroll-section-2">
<div class="sticky-elem desc-message">
<p>
역대 가장 강력한 Won Pro가 등장했습니다. 최초의 프로용 Apple
Silicon인 Keun Pro 또는 Keun Max 칩을 탑재해 쏜살같이 빠른 속도
</p>
</div>
</section>
<section class="scroll-section" id="scroll-section-3">
<p class="canvas-caption">
WonMotion에는 처음으로 도입되는 기술이 웹페이지 스크롤부터 게임
플레이까지 엄청나게 매끄러운 움직임과 탁월한 반응성.
</p>
</section>
스크롤의 전체 높이 지정하기
- 각 scene에 대한 정보를 담고 있는 배열을 생성한다.
- Scroll section 0 1 2 3 에 대한 객체를 3개 만든다.
3가지의 객체
- 스크롤 높이에 대한 객체 (스크롤 구간의 높이에 따라서 Animation의 동작이 빠르냐 느리냐를 결정할 수 있다. )
- heightNum : 브라우저 높이의 5배로 scrollHeight를 셋팅해준다.
- 디바이스 마다 높이가 다르기 때문에 특정 숫자로 높이를 고정하면 기기 마다 스크롤 값이 모두 다르게 된다.
- 각 기기의 높이를 우선 읽어온 다음에 높이 곱하기 heightNum을 해주면 모든 기기는 5배 높이의 값을 동등하게 적용된다.
- objs :
- container : 각 구간의 section 컨테이너 역할이며, (HTML : id가 scroll-section-__ 인 태그에 대한 정보를 담는다. )
JavaScript
const sceneInfo = [
{
// 0
heightNum: 5, // 브라우저 높이의 5배로 scrollHeight 셋팅.
scrollHeight : 0, // 다른 funtion에서 높이 셋팅을 시켜줄 것이다.
// (다양한 기기에 따른 높이 값과 창 사이즈 변화에 대한 처리 때문에 따로 함수로 처리한다.)
objs: {
container: document.querySelector('#scroll-section-0')
}
},
{
// 1
heightNum: 5,
scrollHeight : 0,
objs: {
container: document.querySelector('#scroll-section-1')
}
},
{
// 2
heightNum: 5,
scrollHeight : 0,
objs: {
container: document.querySelector('#scroll-section-2')
}
},
{
// 3
heightNum: 5,
scrollHeight : 0,
objs: {
container: document.querySelector('#scroll-section-3')
}
},
];
function setLayout(){
// 각 Scroll 섹션에 높이를 셋팅하는 함수.
for(let i = 0; i < sceneInfo.length; i++) {
sceneInfo[i].scrollHeight = sceneInfo[i].heightNum * window.innerHeight;
// sceneInfo의 scrollHeight 값은 (window.innerHeight)웹페이지 전체 높이 x (heightNum) 5 이다.
sceneInfo[i].objs.container.style.height = `${sceneInfo[i].scrollHeight}px`
// objs객체 안에 container에 id가 scroll-section-__ 인 태그들의 style 속성을 변경 시키는데.
// 높이 값을 scrollHeight 값으로 모두 적용 시켜준다.
}
}
window.addEventListenr('load',setLayout); // 웹페이지가 Load되면 setLayout함수 실행시켜주기.
window.addEventListenr('resize',setLayout); // 웹페이지 창 크기가 변경되면 seyLayout의 함수를 재실행 시킨다.
정리 :
1. setLayout() 함수는 sceneInfo 배열에 있는 scrollHeight = 0 값을
웹페이지의 전체 높이에 5배(heightNum)로 값을 scrollHeight에 넣어준다.
{
// 0, 1, 2, 3
heightNum: 5,
scrollHeight : heightNum * window.innerHeight,
objs: {
container: document.querySelector('#scroll-section-__')
}
}
그 후에
HTML 코드에 보이는 4개의<section id='scroll-section-__'>
태그들은 setLayout() 함수를 통해 각각 각각 scrollHeight 값을 height 값으로 갖는다.
웹페이지의 전체 높이(innerHeight)가 1041 라고 했을때. 1041 x 5(heightNum) = 5205
즉, 각 <section id='scroll-section-__'>
의 높이는 5205이고 4개의 section이 있다면 웹페이지의 전체 높이는 5205 x 4 = 20820 이다.
몇 번째 Scroll section이 우리 눈앞에 있는지
몇 번째 section을 scroll 중인지 판별하기.
(MDN) 스크롤 이벤트의 조절
Scroll이 될 때 그 Scroll에 대한 값을 처리를 하기 위해서 window.addEventListener('scroll')을 사용한다.
스크롤 값을 얻을 때 사용되는 pageYOffset, scrollY
scrollY : scrollY는 IE에서만 동작한다. 즉 최신 브라우저에서 동작한다.
pageYOffset : 구형 브라우저까지 신경써야 한다면 pageYoffset를 사용하는게 좋다.
scrollLoop() 함수의 원리 파악하기
🔎 전체 scroll의 값인 scrollY의 값 만으로 현재 몇번째 section인지 판별하기는 어렵다. 판별의 기준은 👇
let prevScrollHeight = 0; // 현재 스크롤 scrollY보다 이전에 위치한 스크롤 섹션들의 높이값의 합
let scrollY = 0; //pageYOffset 대신 쓸 변수.
let currentScene = 0; // 현재 활성화 된 section (눈 앞에 보고있는 section)
function scrollLoop(){ // 현재 눈앞에 몇번째 스크롤이 실행되고 있는지를 판별하는 함수.
}
window.addEventListener('scroll', () => { // 익명함수를 넣은 이유는 스크롤은 복잡게하게 동작하기 때문이다.
scrollY = window.pageYOffset || document.documentElement.scrollTop; // 브라우저 버전 호환을 위해 조건문을 사용해서 두가지를 적용시키는 것이 안전하다.
scrollLoop(); // 스크롤을 하면 기본적으로 실행되는 함수.
})
"function scrollLoop() { } 작성"
function scrollLoop() {
prevScrollHeight = 0; // 원하는 값에서 그 값을 또 더한 값들이 스크롤할 때마다 기하급수적으로 더해져서 스크롤 할때 0으로 초기화를 시켜버림.
for(let i = 0; i < currentScene; i++) {
prevScrollHeight = prevScrollHeight + sceneInfo[i].scrollHeight;
// prevScrollHeight에 모든 section(0,1,2,3)의 높이의 값을 더해서 넣는다.
// prevScrollHeight에 = 15615
}
"스크롤 값에 의해 객체 이동 원리"
if(scrollY > prevScrollHeight + sceneInfo[currentScene].scrollHeight) {
currentScene++;
// sceneInfo 배열에 4번째까지 객체가 존재하는데,
// sceneInfo 배열안에 N번째 객체의 scrollHeight 값 보다 자신이 현재 스크롤한 값(scrollY)이 더 크면
// currentScene는 1 증가하고 다음 sceneInfo에 있는 다음 N번째 객체로 이동한다.
// sceneInfo[0].scrollHeight <-- sceneInfo[currentScene].scrollHeight
// sceneInfo[1].scrollHeight <-- sceneInfo[currentScene].scrollHeight
// sceneInfo[2].scrollHeight <-- sceneInfo[currentScene].scrollHeight
// sceneInfo[3].scrollHeight <-- sceneInfo[currentScene].scrollHeight
}
if(scrollY < prevScrollHeight) {
if(currentScene === 0) return // 브라우저 바운스 효과로 인해 마이너스가 되는 것을 방지(모바일)
currentScene--;
}
}
☘️ Scene이 바뀔 때마다 scroll 값 0에서 재시작 시키기
지금까지는 총 scroll 값에 대한 것만 지정해 주었다.
하지만 currentScene이 바뀔 때마다 해당 Scene에서 얼마나 스크롤을 했는지에 대한 값도 알아낼 필요가 있다.
currentScene : 0, 1, 2, 3 은 4개의 영역이 존재한다.
계산은 간단하다.
지나가 버린 Scene들의 스크롤의 높이 값을 더해서 변수에 담아주었던 prevScrollHeight 변수에서 총 스크롤 값인 scrollY의 값을 빼주면 된다.
prevScrollHeight
0번째 Scene : 0
1번째 Scene : 5205
2번째 Scene : 10410
3번째 Scene : 15615
const currentYScrollSet = scrollY - prevScrollHeight;
☘️ 스크롤 값에 따라 opacity 값 변경해주기
특정 섹션에 있는 text 글씨가 스크롤의 값에 따라 나타나고 사라지는 효과를 적용할 것이다.
우선 opacity 특징은 대표적으로 0 과 1 의 값을 지정할 수 있다.
그렇기 때문에 0 ~ 1 사이의 값 즉 스크롤의 값을 0 . ___ 인 소수점으로 나타내야 한다.
const sceneInfo = [
{
scrollHeight: 0,
objs: {
//?: HTML 객체들을 모아두는 곳.
container: document.querySelector('#scroll-section-0'),
messageA: document.querySelector('#scroll-section-0 .main-message.a'),
messageB: document.querySelector('#scroll-section-0 .main-message.b'),
messageC: document.querySelector('#scroll-section-0 .main-message.c'),
messageD: document.querySelector('#scroll-section-0 .main-message.d'),
},
values: {
messageA_opacity: [0, 1], // opacity의 시작값과 종료값 설정해주기.
},
}
1. 얼만큼 스크롤 되었는지 비율로 계산하기
function calcValue(values, currentYScrollSet){
// values : sceneInfo의 messageA_opacity
// currentYScrollSet : 현재 Scene에 스크롤한 값
// 현재 Scene의 전체범위 분의 현재 스크롤 값(currentYScrollSet)을 지정해줘야 한다.
let scrollRatio = currentYScrollSet / sceneInfo[currentScene].scrollHeight;
let rv = scrollRatio * (values[1] - values[0]) + values[0]; // 전체 범위 구하는 식
}
// 전체 범위 구하기 !
"각 텍스트는 opacity로 나타나서 사라지는 영역이 모두 다르다 어떤 텍스트는 (최솟값)200 ~ 500(최대값) 영역까지 동작하거나"
"어떤 텍스트의 opacity 시작점은 (최솟값)500 ~ 1000(최대값)일 수 있다"
"현재는 messageA_opacity: [0, 1] 이지만 messageA_opacity: [200, 900] 즉 시작점 200 ~ 900으로 끝낼 수 있다."
values: {
messageA_opacity: [0, 1], // opacity의 시작값과 종료값 설정해주기.
}
let rv = scrollRatio * (values[1] - values[0]) + values[0];
values[1] = 1
values[0] = 0
🧩 messageA_opacity: [200, 900]
인 경우
values: {
messageA_opacity: [200, 900], // opacity의 시작값과 종료값 설정해주기.
}
let rv = scrollRatio * (values[1] - values[0]) + values[0]; // (200 - 900) 계산이 끝나면 시작값 + 200 을 더해준다.
values[1] = 900
values[0] = 200
values[1] : 900 - values[0] : 200 = 700
700 + values[0] : 200 = 900
🧩 calcValue() 함수를 동작 시키고 opacity의 효과를 동작하는 function 생성하기
function playAnimation() {
const objs = sceneInfo[currentScene].objs; //TODO: DOM 객체 요소들
const values = sceneInfo[currentScene].values;
const currentYScrollSet = scrollY - prevScrollHeight; //TODO: Scene이 바뀌면 scrollY 값이 다시 0에서 시작
switch (currentScene) {
case 0:
let messageA_opacity_in = calcValues(values.messageA_opacity,currentYScrollSet);
objs.messageA.style.opacity = messageA_opacity_in;
// messageA: document.querySelector('#scroll-section-0 .main-message.a'),
break;
case 1:
break;
case 2:
break;
case 3:
break;
}
}
🌿 애니메이션이 동작하는 영역 나누기
const sceneInfo = [
{
scrollHeight: 0,
objs: {
//?: HTML 객체들을 모아두는 곳.
container: document.querySelector('#scroll-section-0'),
messageA: document.querySelector('#scroll-section-0 .main-message.a'),
messageB: document.querySelector('#scroll-section-0 .main-message.b'),
},
values: {
messageA_opacity_in: [0, 1, { start: 0.1, end: 0.2 }], // 비율로 계산했기 때문에 0.1 ~ 0.2 약 10% 구간에서 동작 하도록 설정.
messageB_opacity_in: [0, 1, { start: 0.3, end: 0.4 }], // 나타나기 30% ~ 40% 지점
messageA_opacity_out: [1, 0, { start: 0.25, end: 0.3 }], // 사라지기 25% ~ 30% 지점
messageB_opacity_out: [1, 0, { start: 0.1, end: 0.2 }], // 사라지기 10% ~ 20% 지점
},
},
]
function calcValues(values, currentYScrollSet) {
//? currentYScrollSet: 현재 Scene에서 얼마나 스크롤 됬는지.
const scrollHeight = sceneInfo[currentScene].scrollHeight;
//? scrollRatio : 현재 (Scene)에서 스크롤된 범위를 비율로 구하기
const scrollRatio = currentYScrollSet / scrollHeight;
let rv;
if (values.length === 3) { // values 객체 안에 3번째 index로 start, end 값이 존재하는지 여부 확인.
//? start ~ end 사이에 animation 실행.
const partScrollStart = values[2].start * scrollHeight; // 시작점(start) : 0.1 x 5205
const partScrollEnd = values[2].end * scrollHeight; // 종료지점(end) : 0.2 x 5205
const partScrollHeight = partScrollEnd - partScrollStart; // 시작부터 종료점까지의 높이
// 1041 - 520.5 = 520.5
} else {
rv = scrollRatio * (values[1] - values[0]) + values[0];
}
return rv;
}
Author And Source
이 문제에 관하여(Scroll 현재 값 계산하기), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@dbk03053/Scroll-현재-값-계산하기저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)