작성
·
149
0
@charset 'utf-8';
html {
font-family: 'Noto Sans KR', sans-serif;
font-size: 14px;
}
body {
overflow-x: hidden;
color: rgb(29, 29, 31);
letter-spacing: -0.05em;
background: white;
}
p {
line-height: 1.6;
}
a {
color: rgb(29, 29, 31);
text-decoration: none;
}
.global-nav {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 44px;
padding: 0 1rem;
}
.local-nav {
position: absolute;
top: 45px;
left: 0;
width: 100%;
height: 52px;
padding: 0 1rem;
border-bottom: 1px solid #ddd;
}
.global-nav-links,
.local-nav-links {
display: flex;
align-items: center;
max-width: 1000px;
height: 100%;
margin: 0 auto;
}
.global-nav-links {
justify-content: space-between;
}
.local-nav-links .product-name {
margin-right: auto;
font-size: 1.2rem;
}
.local-nav-links a {
font-size: 0.8rem;
}
.local-nav-links a:not(.product-name) {
margin-left: 2em;
}
.scroll-section {
padding-top: 50vh;
border: 3px solid red;
}
#scroll-section-0 h1 {
font-size: 4rem;
text-align: center;
}
.main-message {
top: 35vh;
display: flex;
align-items: center;
justify-content: center;
margin: 5px 0;
height: 3em;
font-size: 2.5rem;
opacity: 0;
}
.main-message small {
display: block;
font-size: 1.2rem;
margin-bottom: 0.5em;
}
#scroll-section-2 .main-message {
font-size: 3.5rem;
}
.desc-message {
font-weight: bold;
width: 50%;
}
.pin {
width: 1px;
height: 100px;
background: rgb(29, 29, 31);
}
#scroll-section-2.b {
top: 10%;
left: 40%;
}
#scroll-section-2.c {
top: 15%;
left: 45%;
}
.mid-message {
max-width: 1000px;
margin: 0 auto;
padding: 0 1rem;
font-size: 2rem;
color: #888;
}
.mid-message strong {
color: rgb(29, 29, 31);
}
.canvas-caption {
max-width: 1000px;
margin: 0 auto;
padding: 0 1rem;
color: #888;
font-size: 1.2rem;
}
.footer {
display: flex;
align-items: center;
justify-content: center;
height: 7rem;
color: white;
background: darkorange;
}
.main-message p {
line-height: 1.2;
font-weight: bold;
text-align: center;
}
.description {
max-width: 1000px;
margin: 0 auto;
padding: 0 1rem;
font-size: 1.2rem;
color: #888;
}
.description strong {
float: left;
margin-right: 0.2em;
font-size: 3rem;
color: rgb(29, 29, 31);
}
.sticky-elem {
position: fixed;
left: 0;
width: 100%;
display: none;
}
#show-scene-0 #scroll-section-0 .sticky-elem,
#show-scene-1 #scroll-section-1 .sticky-elem,
#show-scene-2 #scroll-section-2 .sticky-elem,
#show-scene-3 #scroll-section-3 .sticky-elem {
display: block;
}
@media (min-width:1024px) {
#scroll-section-0 h1 {
font-size: 9vw;
}
.main-message {
font-size: 2rem;
}
.description {
font-size: 2rem;
}
.description strong {
font-size: 6rem;
}
#scroll-section-2 .main-message {
font-size: 6vw;
}
.main-message small {
font-size: 1.5vw;
}
.desc-message {
width: 20%;
}
#scroll-section-2.b {
top: 20%;
left: 53%;
}
#scroll-section-2.c {
left: 55%;
}
.mid-message {
font-size: 4vw;
}
canvas-caption {
font-size: 2rem;
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>AirMug Pro</title>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@400;900&display=swap" rel="stylesheet">
<link rel="stylesheet" href="css/default.css">
<link rel="stylesheet" href="css/main.css">
</head>
<body>
<div class="container">
<nav class="global-nav">
<div class="global-nav-links">
<a href="#" class="global-nav-item">Rooms</a>
<a href="#" class="global-nav-item">Ideas</a>
<a href="#" class="global-nav-item">Storeds</a>
<a href="#" class="global-nav-item">Contact</a>
</div>
</nav>
<nav class="local-nav">
<div class="local-nav-links">
<a href="#" class="product-name">Airmug pro</a>
<a href="">개요</a>
<a href="">제품사양</a>
<a href="">구입하기</a>
</div>
</nav>
<section class="scroll-section" id="scroll-section-0">
<h1>AirMug Pro</h1>
<div class="sticky-elem main-message a">
<p>온전히 빠져들게 하는<br>최고급 세라믹</p>
</div>
<div class="sticky-elem main-message b">
<p>주변 맛을 느끼게 해주는<br>주변 맛 허용 모드</p>
</div>
<div class="sticky-elem main-message c">
<p>온종일 편안한<br>맞춤형 손잡이</p>
</div>
<div class="sticky-elem main-message d">
<p>새롭게 입가를<br>찾아온 매혹</p>
</div>
</section>
<section class="scroll-section" id="scroll-section-1">
<p class="description">
<strong>보통 스크롤 영역</strong>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Nam praesentium ducimus odio, eveniet eaque soluta aliquid animi unde inventore est sapiente fugit laboriosam voluptatum ipsum quidem illum molestiae obcaecati ut voluptas assumenda quae. Rerum hic, odio unde eum quod? Inventore eius provident vel impedit veniam incidunt at voluptatibus facere odio totam iure ipsam libero, sit eligendi nostrum velit laborum quaerat ipsum ut consectetur. Quos voluptatibus pariatur, accusantium laborum iusto, impedit. Ex enim veritatis nesciunt reprehenderit architecto reiciendis amet cum nihil aut beatae itaque, nulla labore eum quaerat facilis dolorem veniam! Consequatur aliquam placeat dicta voluptas nam recusandae quibusdam nostrum, culpa! Blanditiis in ipsa quaerat dolorem tempore velit, deleniti, excepturi placeat eum facilis officia recusandae praesentium nesciunt porro quibusdam debitis consequuntur, nobis saepe? Fugit voluptates consectetur sapiente maxime dolore, hic, esse laborum expedita eligendi natus cum dolor pariatur qui dicta, minus. Repudiandae architecto, sit et similique odio corrupti repellat. Id iusto perferendis dolores quos non aliquid quis officia, voluptatibus odio. Tenetur fugit repellat quas, consectetur explicabo nihil labore optio molestias, omnis minima dolorum! Expedita corporis beatae, inventore porro nostrum tempora rem. Eligendi, rerum? Quaerat eaque deserunt animi, voluptatibus, quis accusantium fuga recusandae temporibus deleniti ipsa ut laborum, magni cupiditate, illo. Corporis.
</p>
</section>
<section class="scroll-section" id="scroll-section-2">
<div class="sticky-elem sticky-elem-canvas">
<canvas id="video-canvas-1" width="1920" height="1080"></canvas>
</div>
<div class="sticky-elem main-message a">
<p>
<small>편안한 촉감</small>
입과 하나 되다
</p>
</div>
<div class="sticky-elem desc-message b">
<p>
편안한 목넘김을 완성하는 디테일한 여러 구성 요소들, 우리는 이를 하나하나 새롭게 살피고 재구성하는 과정을 거쳐 새로운 수준의 머그, AirMug Pro를 만들었습니다. 입에 뭔가 댔다는 감각은 어느새 사라지고 오롯이 당신과 음료만 남게 되죠.
</p>
<div class="pin"></div>
</div>
<div class="sticky-elem desc-message c">
<p>
디자인 앤 퀄리티 오브 스웨덴,<br>메이드 인 차이나
</p>
<div class="pin"></div>
</div>
</section>
<section class="scroll-section" id="scroll-section-3">
<p class="mid-message">
<strong>Retina 머그</strong><br>
아이디어를 광활하게 펼칠<br>
아름답고 부드러운 음료 공간.
</p>
<canvas class="image-blend-canvas" width="1920" height="1080"></canvas>
<p class="canvas-caption">
Lorem ipsum dolor, sit amet consectetur adipisicing elit. Eveniet at fuga quae perspiciatis veniam impedit et, ratione est optio porro. Incidunt aperiam nemo voluptas odit quisquam harum in mollitia. Incidunt minima iusto in corporis, dolores velit. Autem, sit dolorum inventore a rerum distinctio vero illo magni possimus temporibus dolores neque adipisci, repudiandae repellat. Ducimus accusamus similique quas earum laborum. Autem tempora repellendus asperiores illum ex! Velit ea corporis odit? Ea, incidunt delectus. Sapiente rerum neque error deleniti quis, et, quibusdam, est autem voluptate rem voluptas. Ratione soluta similique harum nihil vel. Quas inventore perferendis iusto explicabo animi eos ratione obcaecati.
</p>
</section>
<footer class="footer">2020 영주마실</footer>
</div>
<script src="js/main.js"></script>
</body>
</html>
(() => { //전역변수를 피하기 위해서 씀. (function())
let yOffset = 0; // window.pageYOffset 대신 쓸 변수
let prevScrollHeight = 0; //현재 스크롤 위치(yOffset)보다 이전에 위치한 스크롤 섹션들의 스크롤 높이값의 합
let currentScene = 0; //현재 활성화된(눈 앞에 보고있는) 씬(scroll-section)
let currenYOffset = yOffset - prevScrollHeight;
let enterNewScene = false; // 새로운 씬에 들어갔다.
// sceneInfo 추가내용 적용 후
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')
},
values: {
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')
},
values: {
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')
},
values: {
}
}
];
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 === 'nomarl'){
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}`);
}
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; // 안써주면 undefined
}
// playAnimation 추가내용 적용 후
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:
// console.log('0 play');
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:
// console.log('2 play');
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)})`;
}
break;
case 3:
// console.log('3 play');
break;
}
}
function scrollLoop() {
enterNewScene = false;
prevScrollHeight = 0;
for (let i = 0; i < currentScene; i++) {
prevScrollHeight += sceneInfo[i].scrollHeight;
}
if (yOffset > prevScrollHeight + sceneInfo[currentScene].scrollHeight) {
enterNewScene = true;
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('resize', setLayout); //세로크기에 따라 height 반응형
window.addEventListener('scroll', () => {
yOffset = window.pageYOffset;
scrollLoop();
});
window.addEventListener('load', setLayout);
window.addEventListener('resize', setLayout);
setLayout();
})();
답변 1
0
1. main.js에 오타가 있어서 type: normal 섹션의 높이가 제대로 안잡히고 있습니다.
setLayout 함수 안에 nomarl을 normal로 고쳐주세요.
2. CSS 선택자 부분에 띄어쓰기가 안되어서 제대로 선택이 안되고 있습니다.
이 부분에서
#scroll-section-2.b 를
#scroll-section-2 .b 로,
#scroll-section-2.c 를
#scroll-section-2 .c 로 바꾸어주세요~