작성
·
267
0
적용한 페이지 : http://phonesawa.co.kr/page/index?tpl=etc%2Fvisual.html
안녕하세요 강좌 잘 보고 있습니다.
다름이 아니라 소스코드를 홈페이지에 맞게 (URL 등) 일부분 수정하여 적용을 해보았는데요 !
몇가지 문제점이 발생하여 질문을 남기게 되었습니다.
1. 홈페이지의 헤더 영역 부분을 밀고 올라와서 보여지는 문제
2. 하단으로 스크롤을 하다가 스크롤을 멈추게 되면 서서히 상단으로 올라가는 문제
위의 두 부분은 어떻게 수정을 해야 할까요 ?
답변좀 부탁드리겠습니다.
답변 4
0
main.js를 아래 코드로 대체해 보시겠어요?
requestAnimation으로 처리하는 부분을 제거한 버전입니다.
영상 움직임 느낌이 살짝 다를 수는 있는데, 현재 문제의 원인이 될 가능성이 있을 만한 부분이라서요~
(() => {
let yOffset = 0; // window.pageYOffset 대신 쓸 변수
let prevScrollHeight = 0; // 현재 스크롤 위치(yOffset)보다 이전에 위치한 스크롤 섹션들의 스크롤 높이값의 합
let currentScene = 0; // 현재 활성화된(눈 앞에 보고있는) 씬(scroll-section)
let enterNewScene = false; // 새로운 scene이 시작된 순간 true
const sceneInfo = [
{
// 0
type: 'sticky',
heightNum: 5, // 브라우저 높이의 5배로 scrollHeight 세팅
scrollHeight: 0,
objs: {
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'),
canvas: document.querySelector('#video-canvas-0'),
context: document.querySelector('#video-canvas-0').getContext('2d'),
videoImages: []
},
values: {
videoImageCount: 300,
imageSequence: [0, 299],
canvas_opacity: [1, 0, { start: 0.9, end: 1 }],
messageA_opacity_in: [0, 1, { start: 0.1, end: 0.2 }],
messageB_opacity_in: [0, 1, { start: 0.3, end: 0.4 }],
messageC_opacity_in: [0, 1, { start: 0.5, end: 0.6 }],
messageD_opacity_in: [0, 1, { start: 0.7, end: 0.8 }],
messageA_translateY_in: [20, 0, { start: 0.1, end: 0.2 }],
messageB_translateY_in: [20, 0, { start: 0.3, end: 0.4 }],
messageC_translateY_in: [20, 0, { start: 0.5, end: 0.6 }],
messageD_translateY_in: [20, 0, { start: 0.7, end: 0.8 }],
messageA_opacity_out: [1, 0, { start: 0.25, end: 0.3 }],
messageB_opacity_out: [1, 0, { start: 0.45, end: 0.5 }],
messageC_opacity_out: [1, 0, { start: 0.65, end: 0.7 }],
messageD_opacity_out: [1, 0, { start: 0.85, end: 0.9 }],
messageA_translateY_out: [0, -20, { start: 0.25, end: 0.3 }],
messageB_translateY_out: [0, -20, { start: 0.45, end: 0.5 }],
messageC_translateY_out: [0, -20, { start: 0.65, end: 0.7 }],
messageD_translateY_out: [0, -20, { start: 0.85, end: 0.9 }]
}
},
{
// 1
type: 'normal',
// heightNum: 5, // type normal에서는 필요 없음
scrollHeight: 0,
objs: {
container: document.querySelector('#scroll-section-1')
}
},
{
// 2
type: 'sticky',
heightNum: 5,
scrollHeight: 0,
objs: {
container: document.querySelector('#scroll-section-2'),
messageA: document.querySelector('#scroll-section-2 .a'),
messageB: document.querySelector('#scroll-section-2 .b'),
messageC: document.querySelector('#scroll-section-2 .c'),
pinB: document.querySelector('#scroll-section-2 .b .pin'),
pinC: document.querySelector('#scroll-section-2 .c .pin'),
canvas: document.querySelector('#video-canvas-1'),
context: document.querySelector('#video-canvas-1').getContext('2d'),
videoImages: []
},
values: {
videoImageCount: 960,
imageSequence: [0, 959],
canvas_opacity_in: [0, 1, { start: 0, end: 0.1 }],
canvas_opacity_out: [1, 0, { start: 0.95, end: 1 }],
messageA_translateY_in: [20, 0, { start: 0.15, end: 0.2 }],
messageB_translateY_in: [30, 0, { start: 0.6, end: 0.65 }],
messageC_translateY_in: [30, 0, { start: 0.87, end: 0.92 }],
messageA_opacity_in: [0, 1, { start: 0.25, end: 0.3 }],
messageB_opacity_in: [0, 1, { start: 0.6, end: 0.65 }],
messageC_opacity_in: [0, 1, { start: 0.87, end: 0.92 }],
messageA_translateY_out: [0, -20, { start: 0.4, end: 0.45 }],
messageB_translateY_out: [0, -20, { start: 0.68, end: 0.73 }],
messageC_translateY_out: [0, -20, { start: 0.95, end: 1 }],
messageA_opacity_out: [1, 0, { start: 0.4, end: 0.45 }],
messageB_opacity_out: [1, 0, { start: 0.68, end: 0.73 }],
messageC_opacity_out: [1, 0, { start: 0.95, end: 1 }],
pinB_scaleY: [0.5, 1, { start: 0.6, end: 0.65 }],
pinC_scaleY: [0.5, 1, { start: 0.87, end: 0.92 }]
}
},
{
// 3
type: 'sticky',
heightNum: 5,
scrollHeight: 0,
objs: {
container: document.querySelector('#scroll-section-3'),
canvasCaption: document.querySelector('.canvas-caption'),
canvas: document.querySelector('.image-blend-canvas'),
context: document.querySelector('.image-blend-canvas').getContext('2d'),
imagesPath: [
'./images/blend-image-1.jpg',
'./images/blend-image-2.jpg'
],
images: []
},
values: {
rect1X: [ 0, 0, { start: 0, end: 0 } ],
rect2X: [ 0, 0, { start: 0, end: 0 } ],
blendHeight: [ 0, 0, { start: 0, end: 0 } ],
canvas_scale: [ 0, 0, { start: 0, end: 0 } ],
canvasCaption_opacity: [ 0, 1, { start: 0, end: 0 } ],
canvasCaption_translateY: [ 20, 0, { start: 0, end: 0 } ],
rectStartY: 0
}
}
];
function setCanvasImages() {
let imgElem;
for (let i = 0; i < sceneInfo[0].values.videoImageCount; i++) {
imgElem = new Image();
imgElem.src = `./video/001/IMG_${6726 + i}.JPG`;
sceneInfo[0].objs.videoImages.push(imgElem);
}
let imgElem2;
for (let i = 0; i < sceneInfo[2].values.videoImageCount; i++) {
imgElem2 = new Image();
imgElem2.src = `./video/002/IMG_${7027 + i}.JPG`;
sceneInfo[2].objs.videoImages.push(imgElem2);
}
let imgElem3;
for (let i = 0; i < sceneInfo[3].objs.imagesPath.length; i++) {
imgElem3 = new Image();
imgElem3.src = sceneInfo[3].objs.imagesPath[i];
sceneInfo[3].objs.images.push(imgElem3);
}
}
function checkMenu() {
if (yOffset > 44) {
document.body.classList.add('local-nav-sticky');
} else {
document.body.classList.remove('local-nav-sticky');
}
}
function setLayout() {
// 각 스크롤 섹션의 높이 세팅
for (let i = 0; i < sceneInfo.length; i++) {
if (sceneInfo[i].type === 'sticky') {
sceneInfo[i].scrollHeight = sceneInfo[i].heightNum * window.innerHeight;
} 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})`;
}
function calcValues(values, currentYOffset) {
let rv;
// 현재 씬(스크롤섹션)에서 스크롤된 범위를 비율로 구하기
const scrollHeight = sceneInfo[currentScene].scrollHeight;
const 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;
const scrollHeight = sceneInfo[currentScene].scrollHeight;
const scrollRatio = currentYOffset / scrollHeight;
switch (currentScene) {
case 0:
objs.canvas.style.opacity = calcValues(values.canvas_opacity, currentYOffset);
let sequence = Math.round(calcValues(values.imageSequence, currentYOffset));
if (objs.videoImages[sequence]) {
objs.context.drawImage(objs.videoImages[sequence], 0, 0);
}
if (scrollRatio <= 0.22) {
// in
objs.messageA.style.opacity = calcValues(values.messageA_opacity_in, currentYOffset);
objs.messageA.style.transform = `translate3d(0, ${calcValues(values.messageA_translateY_in, currentYOffset)}%, 0)`;
} else {
// out
objs.messageA.style.opacity = calcValues(values.messageA_opacity_out, currentYOffset);
objs.messageA.style.transform = `translate3d(0, ${calcValues(values.messageA_translateY_out, currentYOffset)}%, 0)`;
}
if (scrollRatio <= 0.42) {
// in
objs.messageB.style.opacity = calcValues(values.messageB_opacity_in, currentYOffset);
objs.messageB.style.transform = `translate3d(0, ${calcValues(values.messageB_translateY_in, currentYOffset)}%, 0)`;
} else {
// out
objs.messageB.style.opacity = calcValues(values.messageB_opacity_out, currentYOffset);
objs.messageB.style.transform = `translate3d(0, ${calcValues(values.messageB_translateY_out, currentYOffset)}%, 0)`;
}
if (scrollRatio <= 0.62) {
// in
objs.messageC.style.opacity = calcValues(values.messageC_opacity_in, currentYOffset);
objs.messageC.style.transform = `translate3d(0, ${calcValues(values.messageC_translateY_in, currentYOffset)}%, 0)`;
} else {
// out
objs.messageC.style.opacity = calcValues(values.messageC_opacity_out, currentYOffset);
objs.messageC.style.transform = `translate3d(0, ${calcValues(values.messageC_translateY_out, currentYOffset)}%, 0)`;
}
if (scrollRatio <= 0.82) {
// in
objs.messageD.style.opacity = calcValues(values.messageD_opacity_in, currentYOffset);
objs.messageD.style.transform = `translate3d(0, ${calcValues(values.messageD_translateY_in, currentYOffset)}%, 0)`;
} else {
// out
objs.messageD.style.opacity = calcValues(values.messageD_opacity_out, currentYOffset);
objs.messageD.style.transform = `translate3d(0, ${calcValues(values.messageD_translateY_out, currentYOffset)}%, 0)`;
}
break;
case 2:
let sequence2 = Math.round(calcValues(values.imageSequence, currentYOffset));
if (objs.videoImages[sequence2]) {
objs.context.drawImage(objs.videoImages[sequence2], 0, 0);
}
if (scrollRatio <= 0.5) {
// in
objs.canvas.style.opacity = calcValues(values.canvas_opacity_in, currentYOffset);
} else {
// out
objs.canvas.style.opacity = calcValues(values.canvas_opacity_out, currentYOffset);
}
if (scrollRatio <= 0.32) {
// in
objs.messageA.style.opacity = calcValues(values.messageA_opacity_in, currentYOffset);
objs.messageA.style.transform = `translate3d(0, ${calcValues(values.messageA_translateY_in, currentYOffset)}%, 0)`;
} else {
// out
objs.messageA.style.opacity = calcValues(values.messageA_opacity_out, currentYOffset);
objs.messageA.style.transform = `translate3d(0, ${calcValues(values.messageA_translateY_out, currentYOffset)}%, 0)`;
}
if (scrollRatio <= 0.67) {
// in
objs.messageB.style.transform = `translate3d(0, ${calcValues(values.messageB_translateY_in, currentYOffset)}%, 0)`;
objs.messageB.style.opacity = calcValues(values.messageB_opacity_in, currentYOffset);
objs.pinB.style.transform = `scaleY(${calcValues(values.pinB_scaleY, currentYOffset)})`;
} else {
// out
objs.messageB.style.transform = `translate3d(0, ${calcValues(values.messageB_translateY_out, currentYOffset)}%, 0)`;
objs.messageB.style.opacity = calcValues(values.messageB_opacity_out, currentYOffset);
objs.pinB.style.transform = `scaleY(${calcValues(values.pinB_scaleY, currentYOffset)})`;
}
if (scrollRatio <= 0.93) {
// in
objs.messageC.style.transform = `translate3d(0, ${calcValues(values.messageC_translateY_in, currentYOffset)}%, 0)`;
objs.messageC.style.opacity = calcValues(values.messageC_opacity_in, currentYOffset);
objs.pinC.style.transform = `scaleY(${calcValues(values.pinC_scaleY, currentYOffset)})`;
} else {
// out
objs.messageC.style.transform = `translate3d(0, ${calcValues(values.messageC_translateY_out, currentYOffset)}%, 0)`;
objs.messageC.style.opacity = calcValues(values.messageC_opacity_out, currentYOffset);
objs.pinC.style.transform = `scaleY(${calcValues(values.pinC_scaleY, currentYOffset)})`;
}
// currentScene 3에서 쓰는 캔버스를 미리 그려주기 시작
if (scrollRatio > 0.9) {
const objs = sceneInfo[3].objs;
const values = sceneInfo[3].values;
const widthRatio = window.innerWidth / objs.canvas.width;
const heightRatio = window.innerHeight / objs.canvas.height;
let canvasScaleRatio;
if (widthRatio <= heightRatio) {
// 캔버스보다 브라우저 창이 홀쭉한 경우
canvasScaleRatio = heightRatio;
} else {
// 캔버스보다 브라우저 창이 납작한 경우
canvasScaleRatio = widthRatio;
}
objs.canvas.style.transform = `scale(${canvasScaleRatio})`;
objs.context.fillStyle = 'white';
objs.context.drawImage(objs.images[0], 0, 0);
// 캔버스 사이즈에 맞춰 가정한 innerWidth와 innerHeight
const recalculatedInnerWidth = document.body.offsetWidth / canvasScaleRatio;
const recalculatedInnerHeight = window.innerHeight / canvasScaleRatio;
const whiteRectWidth = recalculatedInnerWidth * 0.15;
values.rect1X[0] = (objs.canvas.width - recalculatedInnerWidth) / 2;
values.rect1X[1] = values.rect1X[0] - whiteRectWidth;
values.rect2X[0] = values.rect1X[0] + recalculatedInnerWidth - whiteRectWidth;
values.rect2X[1] = values.rect2X[0] + whiteRectWidth;
// 좌우 흰색 박스 그리기
objs.context.fillRect(
parseInt(values.rect1X[0]),
0,
parseInt(whiteRectWidth),
objs.canvas.height
);
objs.context.fillRect(
parseInt(values.rect2X[0]),
0,
parseInt(whiteRectWidth),
objs.canvas.height
);
}
break;
case 3:
// console.log('3 play');
let step = 0;
// 가로/세로 모두 꽉 차게 하기 위해 여기서 세팅(계산 필요)
const widthRatio = window.innerWidth / objs.canvas.width;
const heightRatio = window.innerHeight / objs.canvas.height;
let canvasScaleRatio;
if (widthRatio <= heightRatio) {
// 캔버스보다 브라우저 창이 홀쭉한 경우
canvasScaleRatio = heightRatio;
} else {
// 캔버스보다 브라우저 창이 납작한 경우
canvasScaleRatio = widthRatio;
}
objs.canvas.style.transform = `scale(${canvasScaleRatio})`;
objs.context.fillStyle = 'white';
objs.context.drawImage(objs.images[0], 0, 0);
// 캔버스 사이즈에 맞춰 가정한 innerWidth와 innerHeight
const recalculatedInnerWidth = document.body.offsetWidth / canvasScaleRatio;
const recalculatedInnerHeight = window.innerHeight / canvasScaleRatio;
if (!values.rectStartY) {
// values.rectStartY = objs.canvas.getBoundingClientRect().top;
values.rectStartY = objs.canvas.offsetTop + (objs.canvas.height - objs.canvas.height * canvasScaleRatio) / 2;
values.rect1X[2].start = (window.innerHeight / 2) / scrollHeight;
values.rect2X[2].start = (window.innerHeight / 2) / scrollHeight;
values.rect1X[2].end = values.rectStartY / scrollHeight;
values.rect2X[2].end = values.rectStartY / scrollHeight;
}
const whiteRectWidth = recalculatedInnerWidth * 0.15;
values.rect1X[0] = (objs.canvas.width - recalculatedInnerWidth) / 2;
values.rect1X[1] = values.rect1X[0] - whiteRectWidth;
values.rect2X[0] = values.rect1X[0] + recalculatedInnerWidth - whiteRectWidth;
values.rect2X[1] = values.rect2X[0] + whiteRectWidth;
// 좌우 흰색 박스 그리기
objs.context.fillRect(
parseInt(calcValues(values.rect1X, currentYOffset)),
0,
parseInt(whiteRectWidth),
objs.canvas.height
);
objs.context.fillRect(
parseInt(calcValues(values.rect2X, currentYOffset)),
0,
parseInt(whiteRectWidth),
objs.canvas.height
);
if (scrollRatio < values.rect1X[2].end) {
step = 1;
// console.log('캔버스 닿기 전');
objs.canvas.classList.remove('sticky');
} else {
step = 2;
// console.log('캔버스 닿은 후');
// 이미지 블렌드
// values.blendHeight: [ 0, 0, { start: 0, end: 0 } ]
values.blendHeight[0] = 0;
values.blendHeight[1] = objs.canvas.height;
values.blendHeight[2].start = values.rect1X[2].end;
values.blendHeight[2].end = values.blendHeight[2].start + 0.2;
const blendHeight = calcValues(values.blendHeight, currentYOffset);
objs.context.drawImage(objs.images[1],
0, objs.canvas.height - blendHeight, objs.canvas.width, blendHeight,
0, objs.canvas.height - blendHeight, objs.canvas.width, blendHeight
);
objs.canvas.classList.add('sticky');
objs.canvas.style.top = `${-(objs.canvas.height - objs.canvas.height * canvasScaleRatio) / 2}px`;
if (scrollRatio > values.blendHeight[2].end) {
values.canvas_scale[0] = canvasScaleRatio;
values.canvas_scale[1] = document.body.offsetWidth / (1.5 * objs.canvas.width);
values.canvas_scale[2].start = values.blendHeight[2].end;
values.canvas_scale[2].end = values.canvas_scale[2].start + 0.2;
objs.canvas.style.transform = `scale(${calcValues(values.canvas_scale, currentYOffset)})`;
objs.canvas.style.marginTop = 0;
}
if (scrollRatio > values.canvas_scale[2].end
&& values.canvas_scale[2].end > 0) {
objs.canvas.classList.remove('sticky');
objs.canvas.style.marginTop = `${scrollHeight * 0.4}px`;
values.canvasCaption_opacity[2].start = values.canvas_scale[2].end;
values.canvasCaption_opacity[2].end = values.canvasCaption_opacity[2].start + 0.1;
values.canvasCaption_translateY[2].start = values.canvasCaption_opacity[2].start;
values.canvasCaption_translateY[2].end = values.canvasCaption_opacity[2].end;
objs.canvasCaption.style.opacity = calcValues(values.canvasCaption_opacity, currentYOffset);
objs.canvasCaption.style.transform = `translate3d(0, ${calcValues(values.canvasCaption_translateY, currentYOffset)}%, 0)`;
}
}
break;
}
}
function scrollLoop() {
enterNewScene = false;
prevScrollHeight = 0;
for (let i = 0; i < currentScene; i++) {
prevScrollHeight += sceneInfo[i].scrollHeight;
}
if (yOffset < prevScrollHeight + sceneInfo[currentScene].scrollHeight) {
document.body.classList.remove('scroll-effect-end');
}
if (yOffset > 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 (yOffset < prevScrollHeight) {
enterNewScene = true;
// 브라우저 바운스 효과로 인해 마이너스가 되는 것을 방지(모바일)
if (currentScene === 0) return;
currentScene--;
document.body.setAttribute('id', `show-scene-${currentScene}`);
}
if (enterNewScene) return;
playAnimation();
}
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);
window.addEventListener('scroll', () => {
yOffset = window.pageYOffset;
scrollLoop();
checkMenu();
});
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();
})();
0
1 번의 헤더 영역은 기존의 헤더를 숨김처리 하면서 해결이 되었습니다.
2 번의 스크롤이 문제인데,,, 말씀주신 부분을 주석처리하였으나 마찬가지로 멈춘상태에서 스크롤이 위로 올라가고 있습니다. ㅠㅠ
0
1. sticky-elem이 position: fixed이다보니 따로 적용하신 헤더를 덮어쓴 현상인데요,
따로 적용하신 헤더인 #layout_body에도 아래 코드를 넣어서 띄워주시면 됩니다(위치나 디자인은 알맞게 조정하세요~).
#layout_body {
position: absolute;
top: 0;
left: 0;
z-index: 100;
}
2. 원래는 자동 스크롤이 살짝만 되고 멈추어야 하는데, 이 부분이 추가하신 코드와 충돌나거나 안맞는 부분이 있어서 버그가 있는 것 같습니다. 일단 main.js에서 아래 부분을 주석처리 해 보시면 해결될 것으로 보입니다.
// 중간에서 새로고침 했을 경우 자동 스크롤로 제대로 그려주기
let tempYOffset = yOffset;
let tempScrollCount = 0;
if (tempYOffset > 0) {
let siId = setInterval(() => {
scrollTo(0, tempYOffset);
tempYOffset += 5;
if (tempScrollCount > 20) {
clearInterval(siId);
}
tempScrollCount++;
}, 20);
}
참고로, @simple-version 폴더에 있는 소스는 좀 더 단순하고 핵심적인 기능만 들어가있는 버전이라, 완성된 소스를 사용해서 커스터마이징 하시기는 이게 더 편하실 거에요~ 물론 완성된 소스코드를 바로 적용하시는것 보다는 강의를 보시며 직접 필요한 부분까지만 작성해서 적용해보시는 것을 추천드립니다. 코드 자체가 난이도가 있는 편이라서 소스코드를 어느정도 이해하고 계셔야 좀더 수월하게 적용하실 수 있거든요^^
0