fullPage.js 처럼 섹션별로 페이지가 자동으로 넘어가는 기능이 없을까 찾아보다가
css로도 가능하다는걸 찾게 되어서 아카이빙을 진행하려 한다!
그건 바로 CSS의 Scroll snap
이다!!
CSS Scroll snap
은 웹 페이지에서 스크롤을 할 때, 요소가 스크롤되는 위치에 자동으로 스냅되도록 하는 CSS 속성이다.
예를들어 사용자가 웹 페이지를 스크롤할 때 중간에 멈춰버리면 콘텐츠의 중간에서 멈춰 주요 콘텐츠의 일부만 보이게 되는데, 이를 미리 설정한 위치로 자동 스냅하여 자연스러운 스크롤 움직임과 함께 사용자 경험을 더욱 향상시키게 된다!
스크롤 스냅을 적용하면 절반만 스크롤해도 해당 영역이 완전히 화면에 스냅된다
우선 스크롤 스냅을 적용할 부모 컨테이너 영역을 만들고, 그 안에 스냅될 자식 요소 영역을 만들어 준다. 그리고 부모에 scroll-snap-type 속성
과 자식에 scroll-snap-align 속성
을 적용해주면 된다. (이들 속성에 대해서는 아래 자세히 다룬다)
<html>
<div class="scroll-container">
<div class="scroll-area">1</div>
<div class="scroll-area">2</div>
<div class="scroll-area">3</div>
<div class="scroll-area">4</div>
</div>
<style>
/* 부모 스크롤 스냅 컨테이너 */
.scroll-container {
overflow: auto;
scroll-snap-type: y mandatory; /* y 축 방향으로만 scroll snap 적용 */
}
/* 자식 스크롤 스냅 영역 */
.scroll-area {
scroll-snap-align: start; /* 스크롤 위치 맞춤 */
}
/* 여기서부턴 단순 꾸미기 --------------------------- */
.scroll-area:nth-of-type(1) {
background: #49b293;
}
.scroll-area:nth-of-type(2) {
background: #c94e4b;
}
.scroll-area:nth-of-type(3) {
background: #4cc1be;
}
.scroll-area:nth-of-type(4) {
background: #8360A6;
}
</style>
</html>
이제 Scroll snap의 속성에 대해서 알아보자!
위의 시안을 보면 스크롤이 휙휙하고 빠르게 넘어가는데, 이 속도를 scroll-behavior: smooth
로 조절이 가능하다!
scroll-snap-type
속성은 스크롤 스냅이 동작하는 방식을 지정한다. 첫 번째 인자로 스냅이 적용될 축을 지정하고, 두 번째 인자로 스냅 적용 방식을 지정한다.
<style>
.container {
scroll-snap-type : [scroll snap axis], [scroll snap strictness];
}
/* y 방향으로 스크롤 스냅을 적용 */
.container {
scroll-snap-type: y mandatory;
}
/* x 방향으로 스크롤 스냅을 적용 */
.container {
scroll-snap-type: x proximity;
}
</style>
💡 scroll-snap-type 속성 값을 mandatory 로 선언 시 주의할점은, 콘텐츠 간의 간격이 넓을 때 강제로 스냅을 하게 되면 중간 콘텐츠를 건너뛰고 다음 콘텐츠로 이동하는 경우가 발생할 수 있다는 점이다.
scroll-snap-align
속성은 스크롤 스냅이 적용되는 요소가 스냅 위치에 정렬되는 방식을 지정한다. scroll-snap-type
에서 지정한 축을 기준으로 snap area의 정렬을 정한다.
<style>
/* 스냅 위치에 맞춰 요소 시작 부분을 정렬 */
.item {
scroll-snap-align: start;
}
/* 스냅 위치에 맞춰 요소 중앙 부분을 정렬 */
.item {
scroll-snap-align: center;
}
/* 스냅 위치에 맞춰 요소 끝 부분을 정렬 */
.item {
scroll-snap-align: end;
}
</style>
scroll-padding
, scroll-margin
속성은 스크롤 영역의 패딩과 마진을 조정하는 속성이다. 이 속성을 사용하면 스크롤 스냅이 적용되는 영역을 확보할 수 있다.
정말로 요소의 padding, margin 값이 변경되는 것이 아니고, 해당 뷰 포트(viewport)의 가짜 padding, margin이 적용되는 것이다.
<style>
/* 스크롤 영역 위/아래로 패딩 100px 추가 */
.container {
scroll-snap-type: y mandatory;
scroll-padding: 100px 0 100px 0; /* `scroll-padding-top`, `scroll-padding-right`, `scroll-padding-bottom`, `scroll-padding-left` */
}
/* 스크롤 영역 상/하/좌/우 모두에 패딩 50px 추가 */
.container {
scroll-snap-type: y mandatory;
scroll-padding: 50px;
}
/* 스크롤 스냅이 적용되는 영역의 상/하/좌/우 마진을 100px로 지정 */
.container {
scroll-margin: 100px; /* `scroll-margin-top`, `scroll-margin-right`, `scroll-margin-bottom`, `scroll-margin-left` */
}
/* 스크롤 스냅이 적용되는 영역의 상/하 마진을 50px, 좌/우 마진을 100px로 지정 */
.container {
scroll-margin: 50px 100px;
}
</style>
scroll-snap-stop
속성은 스크롤 스냅 위치에 도달했을 때 스크롤을 중지할 지 여부를 지정한다.
<style>
/* 스크롤 스냅이 요소의 끝에서 중단되도록 지정 */
.item {
scroll-snap-stop: always;
}
/* 스크롤 스냅이 요소의 중앙에서만 중단되도록 지정 */
.item {
scroll-snap-stop: normal;
}
</style>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Slow Scroll Snap Example</title>
<style>
/* 기본 HTML 스타일 */
html {
scroll-behavior: smooth; /* 부드러운 스크롤 기본 설정 */
}
/* 컨테이너 설정 */
.container {
height: 100vh;
overflow-y: scroll;
scroll-snap-type: y mandatory; /* y축 스냅, mandatory로 강제 스냅 */
}
/* 섹션 스타일 */
section {
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
font-size: 2rem;
scroll-snap-align: start; /* 섹션 시작점에 스냅 */
}
/* 섹션별 색상 */
.section1 { background-color: #f0f0f0; }
.section2 { background-color: #d0e8ff; }
.section3 { background-color: #ffd0d0; }
</style>
</head>
<body>
<div class="container">
<section class="section1">섹션 1</section>
<section class="section2">섹션 2</section>
<section class="section3">섹션 3</section>
</div>
<script>
// JavaScript로 스크롤 속도 제어
const container = document.querySelector('.container');
let isScrolling;
container.addEventListener('wheel', (event) => {
event.preventDefault(); // 기본 스크롤 동작 방지
clearTimeout(isScrolling);
// 스크롤 방향 감지
const delta = event.deltaY > 0 ? 1 : -1;
const sections = document.querySelectorAll('section');
let currentSection = 0;
// 현재 스크롤 위치에 따라 섹션 인덱스 계산
sections.forEach((section, index) => {
if (container.scrollTop >= section.offsetTop - 50) {
currentSection = index;
}
});
// 다음 또는 이전 섹션으로 이동
const nextSection = Math.min(Math.max(currentSection + delta, 0), sections.length - 1);
const targetTop = sections[nextSection].offsetTop;
// 부드럽고 느린 스크롤 애니메이션
container.scrollTo({
top: targetTop,
behavior: 'smooth'
});
// 스크롤이 완료될 때까지 추가 스크롤 방지
isScrolling = setTimeout(() => {
isScrolling = null;
}, 1000); // 1초 동안 추가 스크롤 방지 (속도 조절)
});
</script>
</body>
</html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Horizontal Scroll Snap with Center Alignment (Fixed)</title>
<style>
html {
scroll-behavior: smooth; /* 부드러운 스크롤 기본 설정 */
}
body {
margin: 0;
overflow: hidden; /* 불필요한 스크롤바 방지 */
}
.container {
width: 100vw;
height: 100vh;
overflow-x: auto; /* 가로 스크롤 활성화 */
overflow-y: hidden; /* 세로 스크롤 비활성화 */
display: flex; /* 섹션을 가로로 배치 */
scroll-snap-type: x proximity; /* x축 스냅, proximity로 자연스러운 스냅 */
white-space: nowrap; /* 섹션이 줄바꿈되지 않도록 */
-webkit-overflow-scrolling: touch; /* 모바일에서 부드러운 스크롤 */
}
section {
width: 100vw; /* 각 섹션이 뷰포트 너비와 동일 */
height: 100vh;
display: inline-flex; /* 가로로 정렬 */
justify-content: center;
align-items: center;
font-size: 2rem;
scroll-snap-align: center; /* 섹션의 중앙에 스냅 */
flex-shrink: 0; /* 섹션 크기 고정 */
}
.section1 { background-color: #f0f0f0; }
.section2 { background-color: #d0e8ff; }
.section3 { background-color: #ffd0d0; }
</style>
</head>
<body>
<div class="container">
<section class="section1">섹션 1</section>
<section class="section2">섹션 2</section>
<section class="section3">섹션 3</section>
</div>
<script>
const container = document.querySelector('.container');
let isScrolling = false;
container.addEventListener('wheel', (event) => {
event.preventDefault(); // 기본 스크롤 동작 방지
if (isScrolling) return;
// 수직 휠을 가로 스크롤로 변환
const delta = event.deltaY > 0 ? 1 : -1;
const sections = document.querySelectorAll('section');
let currentSection = 0;
// 현재 스크롤 위치에 따라 섹션 인덱스 계산
sections.forEach((section, index) => {
const sectionLeft = section.offsetLeft;
const sectionWidth = section.offsetWidth;
const sectionCenter = sectionLeft + sectionWidth / 2;
if (container.scrollLeft + window.innerWidth / 2 >= sectionCenter - 50 &&
container.scrollLeft + window.innerWidth / 2 <= sectionCenter + 50) {
currentSection = index;
}
});
// 다음 또는 이전 섹션으로 이동
const nextSection = Math.min(Math.max(currentSection + delta, 0), sections.length - 1);
const targetLeft = sections[nextSection].offsetLeft + (sections[nextSection].offsetWidth - window.innerWidth) / 2;
isScrolling = true;
container.scrollTo({
left: targetLeft,
behavior: 'smooth' // 부드러운 스크롤
});
// 스크롤 완료 후 isScrolling 플래그 해제
setTimeout(() => {
isScrolling = false;
}, 1000); // 1초 딜레이로 스크롤 간섭 방지
});
</script>
</body>
</html>