(() => {
let yOffset = 0; //window.pageYoffset 대신 쓸 변수
let prevScrollHeight = 0; //현재 스크롤 위치(yOffset)보다 이전에 위치한 스크롤 섹션들의 스크롤 높이값의 합
let currentScene = 0; //현재 활성화된(눈 앞에 보고있는) 씬(scroll-section)
let enterNewScene = false; //새로운 scene이 시작된 순간 true
let acc = 0.1;
let delayedYOffset = 0;
let rafId;
let rafState;
const sceneInfo = [
{
// 0
type : 'sticky',
heightNum : 8, // 브라우저 높이의 5배로 scrollHeight 세팅
scrollHeight : 0,
objs: {
container : document.querySelector('#scroll-section-0'),
canvas : document.querySelector('#video-canvas-0'),
context : document.querySelector('#video-canvas-0').getContext('2d'),
videoImages : []
},
values : {
videoImageCount : 314,
imageSequence : [0, 313],
canvas_opacity : [1, 0, {start: 0.9, end: 1}],
}
},
{
// 1
type : 'normal',
// heightNum : 5, // 브라우저 높이의 5배로 scrollHeight 세팅 (normal에서는 필요없음)
scrollHeight : 0,
objs: {
container : document.querySelector('#scroll-section-1')
}
},
{
// 2
type : 'sticky',
heightNum : 8, // 브라우저 높이의 5배로 scrollHeight 세팅
scrollHeight : 0,
objs: {
container: document.querySelector('#scroll-section-2'),
canvas : document.querySelector('#video-canvas-1'),
context : document.querySelector('#video-canvas-1').getContext('2d'),
videoImages : []
},
values: {
videoImageCount : 471,
imageSequence : [0, 470],
canvas_opacity_in : [0, 1, {start: 0, end: 0.1}],
canvas_opacity_out : [1, 0, {start: 0.95, end: 1}],
}
},
{
// 3
type : 'normal',
// heightNum : 5, // 브라우저 높이의 5배로 scrollHeight 세팅 (normal에서는 필요없음)
scrollHeight : 0,
objs: {
container : document.querySelector('#scroll-section-3')
}
},
{
// 4
type : 'sticky',
heightNum : 8, // 브라우저 높이의 5배로 scrollHeight 세팅
scrollHeight : 0,
objs: {
container: document.querySelector('#scroll-section-4'),
canvas : document.querySelector('#video-canvas-2'),
context : document.querySelector('#video-canvas-2').getContext('2d'),
videoImages : []
},
values: {
videoImageCount : 338,
imageSequence : [0, 337],
canvas_opacity_in : [0, 1, {start: 0, end: 0.1}],
canvas_opacity_out : [1, 0, {start: 0.95, end: 1}],
}
},
{
// 5
type : 'normal',
// heightNum : 5, // 브라우저 높이의 5배로 scrollHeight 세팅 (normal에서는 필요없음)
scrollHeight : 0,
objs: {
container : document.querySelector('#scroll-section-5')
}
},
];
function setCanvasImages() {
let imgElem1;
for (let i = 1; i < sceneInfo[0].values.videoImageCount; i++) {
let filenameNumber = i;
let filename = '';
if ( filenameNumber < 10) {
filename = '000' + filenameNumber;
} else if ( filenameNumber < 100) {
filename = '00' + filenameNumber;
} else if (filenameNumber >= 100 && filenameNumber < 1000) {
filename = '0' + filenameNumber;
} else {
filename = String(filenameNumber);
}
imgElem1 = new Image();
imgElem1.src = `./video/chapter1/img_${filename}.jpg`;
sceneInfo[0].objs.videoImages.push(imgElem1);
// 24프레임
}
let imgElem2;
for (let i = 1; i < sceneInfo[2].values.videoImageCount; i++) {
let filenameNumber = i;
let filename = '';
if ( filenameNumber < 10) {
filename = '000' + filenameNumber;
} else if ( filenameNumber < 100) {
filename = '00' + filenameNumber;
} else if (filenameNumber >= 100 && filenameNumber < 1000) {
filename = '0' + filenameNumber;
} else {
filename = String(filenameNumber);
}
imgElem2 = new Image();
imgElem2.src = `./video/chapter2/img_${filename}.jpg`;
sceneInfo[2].objs.videoImages.push(imgElem2);
// 24프레임
}
let imgElem3;
for (let i = 1; i < sceneInfo[4].values.videoImageCount; i++) {
let filenameNumber = i;
let filename = '';
if ( filenameNumber < 10) {
filename = '000' + filenameNumber;
} else if ( filenameNumber < 100) {
filename = '00' + filenameNumber;
} else if (filenameNumber >= 100 && filenameNumber < 1000) {
filename = '0' + filenameNumber;
} else {
filename = String(filenameNumber);
}
imgElem3 = new Image();
imgElem3.src = `./video/chapter3/img_${filename}.jpg`;
sceneInfo[4].objs.videoImages.push(imgElem3);
// 24프레임
}
}
function setLayout() {
// 각 스크롤 섹션의 높이 세팅
for (let i = 0; i < sceneInfo.length; i++) {
if (sceneInfo[i].type === 'sticky') {
sceneInfo[i].scrollHeight = sceneInfo[i].heightNum * window.innerHeight; //각섹션 스크롤 총 높이 = hightnum * 뷰포트 높이
} else if (sceneInfo[i].type === 'normal') {
sceneInfo[i].scrollHeight = sceneInfo[i].objs.container.offsetHeight;
}
sceneInfo[i].objs.container.style.height = `${sceneInfo[i].scrollHeight}px`;
}
yOffset = window.pageYOffset;
let totalScrollHeight = 0;
for ( let i = 0; i < sceneInfo.length; i++) {
totalScrollHeight += sceneInfo[i].scrollHeight;
if( totalScrollHeight >= yOffset ) {
currentScene = i;
break;
}
}
document.body.setAttribute('id', `show-scene-${currentScene}`);
// 캔버스에 사이즈 조정
const heightRatio = window.innerHeight / 1080;
sceneInfo[0].objs.canvas.style.transform = `translate3d(-50%, -50%, 0) scale(${heightRatio})`;
sceneInfo[2].objs.canvas.style.transform = `translate3d(-50%, -50%, 0) scale(${heightRatio})`;
sceneInfo[4].objs.canvas.style.transform = `translate3d(-50%, -50%, 0) scale(${heightRatio})`;
}
function calcValues(values, currentYOffset) {
let rv;
// 현재 씬 (스크롤섹션)에서 스크롤된 범위를 비율로 구하기
const scrollHeight = sceneInfo[currentScene].scrollHeight;
let scrollRatio = currentYOffset / scrollHeight;
if (values.length === 3) {
// start ~ end 사이에 애니메이션 실행
const partScrollStart = values[2].start * scrollHeight;
const partScrollEnd = values[2].end * scrollHeight;
const partScrollHeight = partScrollEnd - partScrollStart;
if (currentYOffset >= partScrollStart && currentYOffset <= partScrollEnd) {
rv =(currentYOffset - partScrollStart) / partScrollHeight * (values[1] - values[0]) + values[0];
} else if ( currentYOffset < partScrollStart) {
rv = values[0];
} else if ( currentYOffset > partScrollEnd) {
rv = values[1];
}
} else {
rv = scrollRatio * (values[1] - values[0]) + values[0];
}
return rv;
}
function playAnimation() {
const objs = sceneInfo[currentScene].objs;
const values = sceneInfo[currentScene].values;
const currentYOffset = yOffset - prevScrollHeight; //섹션에 현재 yoffset 값
const scrollHeight = sceneInfo[currentScene].scrollHeight;
const scrollRatio = currentYOffset / scrollHeight;
switch (currentScene) {
case 0 :
// let sequence = Math.round(calcValues(values.imageSequence, currentYOffset)); //math 반올림 처리
// objs.context.drawImage(objs.videoImages[sequence], 0, 0);
objs.canvas.style.opacity = calcValues(values.canvas_opacity, currentYOffset);
break;
case 1 :
break;
case 2 :
// let sequence2 = Math.round(calcValues(values.imageSequence, currentYOffset)); //math 반올림 처리
// objs.context.drawImage(objs.videoImages[sequence2], 0, 0);
if (scrollRatio <= 0.3) {
// in
objs.canvas.style.opacity = calcValues(values.canvas_opacity_in, currentYOffset);
} else {
// out
objs.canvas.style.opacity = calcValues(values.canvas_opacity_out, currentYOffset);
}
break;
case 3 :
break;
case 4 :
// let sequence4 = Math.round(calcValues(values.imageSequence, currentYOffset)); //math 반올림 처리
// objs.context.drawImage(objs.videoImages[sequence4], 0, 0);
if (scrollRatio <= 0.3) {
// in
objs.canvas.style.opacity = calcValues(values.canvas_opacity_in, currentYOffset);
} else {
// out
objs.canvas.style.opacity = calcValues(values.canvas_opacity_out, currentYOffset);
}
break;
case 5 :
break;
}
}
function scrollLoop() {
enterNewScene = false;
prevScrollHeight = 0;
for (let i = 0; i < currentScene; i++) {
prevScrollHeight += sceneInfo[i].scrollHeight;
}
if (delayedYOffset < prevScrollHeight + sceneInfo[currentScene].scrollHeight) {
document.body.classList.remove('scroll-effect-end');
}
if (delayedYOffset > prevScrollHeight + sceneInfo[currentScene].scrollHeight) {
enterNewScene = true;
if (currentScene === sceneInfo.length - 1) {
document.body.classList.add('scroll-effect-end');
}
if (currentScene < sceneInfo.length - 1) {
currentScene++;
}
document.body.setAttribute('id', `show-scene-${currentScene}`);
}
if (delayedYOffset < prevScrollHeight) {
enterNewScene = true;
// 브라우저 바운스 효과로 인해 마이너스가 되는 것을 방지(모바일)
if (currentScene === 0) return;
currentScene--;
document.body.setAttribute('id', `show-scene-${currentScene}`);
}
if (enterNewScene) return;
playAnimation();
}
function loop() {
delayedYOffset = delayedYOffset + (yOffset - delayedYOffset) * acc;
if (!enterNewScene) {
if (currentScene === 0 || currentScene === 2 || currentScene === 4) {
const currentYOffset = delayedYOffset - prevScrollHeight;
const objs = sceneInfo[currentScene].objs;
const values = sceneInfo[currentScene].values;
let sequence = Math.round(calcValues(values.imageSequence, currentYOffset));
if (objs.videoImages[sequence]) {
objs.context.drawImage(objs.videoImages[sequence], 0, 0);
}
}
}
// 일부 기기에서 페이지 끝으로 고속 이동하면 body id가 제대로 인식 안되는 경우를 해결
// 페이지 맨 위로 갈 경우: scrollLoop와 첫 scene의 기본 캔버스 그리기 수행
if (delayedYOffset < 1) {
scrollLoop();
sceneInfo[0].objs.canvas.style.opacity = 1;
sceneInfo[0].objs.context.drawImage(sceneInfo[0].objs.videoImages[0], 0, 0);
}
// 페이지 맨 아래로 갈 경우: 마지막 섹션은 스크롤 계산으로 위치 및 크기를 결정해야할 요소들이 많아서 1픽셀을 움직여주는 것으로 해결
if ((document.body.offsetHeight - window.innerHeight) - delayedYOffset < 1) {
let tempYOffset = yOffset;
scrollTo(0, tempYOffset - 1);
}
rafId = requestAnimationFrame(loop);
if (Math.abs(yOffset - delayedYOffset) < 1) {
cancelAnimationFrame(rafId);
rafState = false;
}
}
// 페이지 로드 됐을때
// window.addEventListener('DOMContentLoaded', setLayout);
window.addEventListener('load', () => {
setLayout(); // 중간에 새로고침 시, 콘텐츠 양에 따라 높이 계산에 오차가 발생하는 경우를 방지하기 위해 before-load 클래스 제거 전에도 확실하게 높이를 세팅하도록 한번 더 실행
// document.body.classList.remove('before-load');
setLayout();
sceneInfo[0].objs.context.drawImage(sceneInfo[0].objs.videoImages[0], 0, 0);
// 중간에서 새로고침 했을 경우 자동 스크롤로 제대로 그려주기
let tempYOffset = yOffset;
let tempScrollCount = 0;
if (tempYOffset > 0) {
let siId = setInterval(() => {
scrollTo(0, tempYOffset);
tempYOffset += 5;
if (tempScrollCount > 20) {
clearInterval(siId);
}
tempScrollCount++;
}, 20);
}
// 스크롤 했을때
window.addEventListener('scroll', () => {
yOffset = window.pageYOffset;
scrollLoop();
// checkMenu();
if (!rafState) {
rafId = requestAnimationFrame(loop);
rafState = true;
}
});
window.addEventListener('resize', () => {
if (window.innerWidth > 900) {
window.location.reload();
}
});
window.addEventListener('orientationchange', () => {
scrollTo(0, 0);
setTimeout(() => {
window.location.reload();
}, 500);
});
// document.querySelector('.loading').addEventListener('transitionend', (e) => {
// document.body.removeChild(e.currentTarget);
// });
});
setCanvasImages();
})();